blob: 8b19e1091909e2729651e46594a034cf6717606f [file] [log] [blame]
// Copyright 2016 PDFium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "core/fpdfdoc/cpdf_formfield.h"
#include <vector>
#include "constants/form_fields.h"
#include "constants/form_flags.h"
#include "core/fpdfapi/page/cpdf_docpagedata.h"
#include "core/fpdfapi/page/cpdf_pagemodule.h"
#include "core/fpdfapi/parser/cpdf_array.h"
#include "core/fpdfapi/parser/cpdf_dictionary.h"
#include "core/fpdfapi/parser/cpdf_document.h"
#include "core/fpdfapi/parser/cpdf_indirect_object_holder.h"
#include "core/fpdfapi/parser/cpdf_name.h"
#include "core/fpdfapi/parser/cpdf_number.h"
#include "core/fpdfapi/parser/cpdf_reference.h"
#include "core/fpdfapi/parser/cpdf_string.h"
#include "core/fpdfapi/render/cpdf_docrenderdata.h"
#include "core/fpdfdoc/cpdf_interactiveform.h"
#include "core/fxcrt/fx_memory.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/base/containers/contains.h"
namespace {
// Create and destroys the page module that is necessary when instantiating a
// CPDF_Document.
class ScopedCPDF_PageModule {
public:
FX_STACK_ALLOCATED();
ScopedCPDF_PageModule() { CPDF_PageModule::Create(); }
~ScopedCPDF_PageModule() { CPDF_PageModule::Destroy(); }
};
class CPDF_TestEmptyDocument final : public CPDF_Document {
public:
CPDF_TestEmptyDocument()
: CPDF_Document(std::make_unique<CPDF_DocRenderData>(),
std::make_unique<CPDF_DocPageData>()) {}
};
void TestMultiselectFieldDict(RetainPtr<CPDF_Array> opt_array,
RetainPtr<CPDF_Object> values,
RetainPtr<CPDF_Object> selected_indices,
bool expected_use_indices,
const std::vector<int>& expected_indices,
const std::vector<int>& excluded_indices) {
auto form_dict = pdfium::MakeRetain<CPDF_Dictionary>();
form_dict->SetNewFor<CPDF_Name>("Type", "Annot");
form_dict->SetNewFor<CPDF_Name>("Subtype", "Widget");
form_dict->SetNewFor<CPDF_Name>(pdfium::form_fields::kFT,
pdfium::form_fields::kCh);
constexpr int kMuliSelectFlag = pdfium::form_flags::kChoiceMultiSelect;
form_dict->SetNewFor<CPDF_Number>(pdfium::form_fields::kFf, kMuliSelectFlag);
form_dict->SetFor("Opt", opt_array);
form_dict->SetFor(pdfium::form_fields::kV, values);
form_dict->SetFor("I", selected_indices);
CPDF_TestEmptyDocument doc;
CPDF_InteractiveForm form(&doc);
CPDF_FormField form_field(&form, form_dict.Get());
EXPECT_EQ(expected_use_indices, form_field.UseSelectedIndicesObject());
for (int i = 0; i < form_field.CountOptions(); i++) {
const bool expected_selected = pdfium::Contains(expected_indices, i);
EXPECT_EQ(expected_selected, form_field.IsItemSelected(i));
}
for (int i : excluded_indices) {
EXPECT_FALSE(form_field.IsItemSelected(i));
}
}
} // namespace
TEST(CPDF_FormFieldTest, GetFullNameForDict) {
WideString name = CPDF_FormField::GetFullNameForDict(nullptr);
EXPECT_TRUE(name.IsEmpty());
CPDF_IndirectObjectHolder obj_holder;
CPDF_Dictionary* root = obj_holder.NewIndirect<CPDF_Dictionary>();
root->SetNewFor<CPDF_Name>("T", "foo");
name = CPDF_FormField::GetFullNameForDict(root);
EXPECT_STREQ("foo", name.ToUTF8().c_str());
CPDF_Dictionary* dict1 = obj_holder.NewIndirect<CPDF_Dictionary>();
root->SetNewFor<CPDF_Reference>("Parent", &obj_holder, dict1->GetObjNum());
dict1->SetNewFor<CPDF_Name>("T", "bar");
name = CPDF_FormField::GetFullNameForDict(root);
EXPECT_STREQ("bar.foo", name.ToUTF8().c_str());
CPDF_Dictionary* dict2 = dict1->SetNewFor<CPDF_Dictionary>("Parent");
name = CPDF_FormField::GetFullNameForDict(root);
EXPECT_STREQ("bar.foo", name.ToUTF8().c_str());
CPDF_Dictionary* dict3 = obj_holder.NewIndirect<CPDF_Dictionary>();
dict2->SetNewFor<CPDF_Reference>("Parent", &obj_holder, dict3->GetObjNum());
dict3->SetNewFor<CPDF_Name>("T", "qux");
name = CPDF_FormField::GetFullNameForDict(root);
EXPECT_STREQ("qux.bar.foo", name.ToUTF8().c_str());
dict3->SetNewFor<CPDF_Reference>("Parent", &obj_holder, root->GetObjNum());
name = CPDF_FormField::GetFullNameForDict(root);
EXPECT_STREQ("qux.bar.foo", name.ToUTF8().c_str());
name = CPDF_FormField::GetFullNameForDict(dict1);
EXPECT_STREQ("foo.qux.bar", name.ToUTF8().c_str());
name = CPDF_FormField::GetFullNameForDict(dict2);
EXPECT_STREQ("bar.foo.qux", name.ToUTF8().c_str());
name = CPDF_FormField::GetFullNameForDict(dict3);
EXPECT_STREQ("bar.foo.qux", name.ToUTF8().c_str());
}
TEST(CPDF_FormFieldTest, IsItemSelected) {
ScopedCPDF_PageModule page_module;
auto opt_array = pdfium::MakeRetain<CPDF_Array>();
opt_array->AppendNew<CPDF_String>(L"Alpha");
opt_array->AppendNew<CPDF_String>(L"Beta");
opt_array->AppendNew<CPDF_String>(L"Gamma");
opt_array->AppendNew<CPDF_String>(L"Delta");
opt_array->AppendNew<CPDF_String>(L"Epsilon");
{
// No Values (/V) or Selected Indices (/I) objects.
std::vector<int> expected_indices;
std::vector<int> excluded_indices{-1, 5};
TestMultiselectFieldDict(opt_array, /*values=*/nullptr,
/*selected_indices=*/nullptr,
/*expected_use_indices=*/false, expected_indices,
excluded_indices);
}
{
// Values (/V) object is just a string.
auto values = pdfium::MakeRetain<CPDF_String>(/*pPool=*/nullptr, L"Gamma");
std::vector<int> expected_indices{2};
std::vector<int> excluded_indices{-1, 5};
TestMultiselectFieldDict(opt_array, values, /*selected_indices=*/nullptr,
/*expected_use_indices=*/false, expected_indices,
excluded_indices);
}
{
// Values (/V) object is just an invalid string.
auto values = pdfium::MakeRetain<CPDF_String>(/*pPool=*/nullptr, L"Omega");
std::vector<int> expected_indices;
std::vector<int> excluded_indices{-1, 5};
TestMultiselectFieldDict(opt_array, values, /*selected_indices=*/nullptr,
/*expected_use_indices=*/false, expected_indices,
excluded_indices);
}
{
// Values (/V) object is an array with one object.
auto values = pdfium::MakeRetain<CPDF_Array>();
values->AppendNew<CPDF_String>(L"Beta");
std::vector<int> expected_indices{1};
std::vector<int> excluded_indices{-1, 5};
TestMultiselectFieldDict(opt_array, values, /*selected_indices=*/nullptr,
/*expected_use_indices=*/false, expected_indices,
excluded_indices);
}
{
// Values (/V) object is an array with one invalid object.
auto values = pdfium::MakeRetain<CPDF_Array>();
values->AppendNew<CPDF_String>(L"Omega");
std::vector<int> expected_indices;
std::vector<int> excluded_indices{-1, 5};
TestMultiselectFieldDict(opt_array, values, /*selected_indices=*/nullptr,
/*expected_use_indices=*/false, expected_indices,
excluded_indices);
}
{
// Values (/V) object is an array with multiple objects.
auto values = pdfium::MakeRetain<CPDF_Array>();
values->AppendNew<CPDF_String>(L"Beta");
values->AppendNew<CPDF_String>(L"Epsilon");
std::vector<int> expected_indices{1, 4};
std::vector<int> excluded_indices{-1, 5};
TestMultiselectFieldDict(opt_array, values, /*selected_indices=*/nullptr,
/*expected_use_indices=*/false, expected_indices,
excluded_indices);
}
{
// Values (/V) object is an array with multiple objects with one invalid.
auto values = pdfium::MakeRetain<CPDF_Array>();
values->AppendNew<CPDF_String>(L"Beta");
values->AppendNew<CPDF_String>(L"Epsilon");
values->AppendNew<CPDF_String>(L"Omega");
std::vector<int> expected_indices{1, 4};
std::vector<int> excluded_indices{-1, 5};
TestMultiselectFieldDict(opt_array, values, /*selected_indices=*/nullptr,
/*expected_use_indices=*/false, expected_indices,
excluded_indices);
}
{
// Selected indices (/I) object is just a number.
auto selected_indices = pdfium::MakeRetain<CPDF_Number>(3);
std::vector<int> expected_indices{3};
std::vector<int> excluded_indices{-1, 5};
TestMultiselectFieldDict(opt_array, /*values=*/nullptr, selected_indices,
/*expected_use_indices=*/true, expected_indices,
excluded_indices);
}
{
// Selected indices (/I) object is just an invalid number.
auto selected_indices = pdfium::MakeRetain<CPDF_Number>(26);
std::vector<int> expected_indices;
std::vector<int> excluded_indices{-1, 5, 26};
TestMultiselectFieldDict(opt_array, /*values=*/nullptr, selected_indices,
/*expected_use_indices=*/true, expected_indices,
excluded_indices);
}
{
// Selected indices (/I) object is an array with one object.
auto selected_indices = pdfium::MakeRetain<CPDF_Array>();
selected_indices->AppendNew<CPDF_Number>(0);
std::vector<int> expected_indices{0};
std::vector<int> excluded_indices{-1, 5};
TestMultiselectFieldDict(opt_array, /*values=*/nullptr, selected_indices,
/*expected_use_indices=*/true, expected_indices,
excluded_indices);
}
{
// Selected indices (/I) object is an array with multiple objects.
auto selected_indices = pdfium::MakeRetain<CPDF_Array>();
selected_indices->AppendNew<CPDF_Number>(0);
selected_indices->AppendNew<CPDF_Number>(2);
selected_indices->AppendNew<CPDF_Number>(3);
std::vector<int> expected_indices{0, 2, 3};
std::vector<int> excluded_indices{-1, 5};
TestMultiselectFieldDict(opt_array, /*values=*/nullptr, selected_indices,
/*expected_use_indices=*/true, expected_indices,
excluded_indices);
}
{
// Selected indices (/I) object is an array with multiple objects and some
// are invalid.
auto selected_indices = pdfium::MakeRetain<CPDF_Array>();
selected_indices->AppendNew<CPDF_Number>(0);
selected_indices->AppendNew<CPDF_Number>(2);
selected_indices->AppendNew<CPDF_Number>(3);
selected_indices->AppendNew<CPDF_Number>(-5);
selected_indices->AppendNew<CPDF_Number>(12);
selected_indices->AppendNew<CPDF_Number>(42);
std::vector<int> expected_indices{0, 2, 3};
std::vector<int> excluded_indices{-5, -1, 5, 12, 42};
TestMultiselectFieldDict(opt_array, /*values=*/nullptr, selected_indices,
/*expected_use_indices=*/true, expected_indices,
excluded_indices);
}
{
// Values (/V) or Selected Indices (/I) objects conflict with different
// lengths.
auto values = pdfium::MakeRetain<CPDF_Array>();
values->AppendNew<CPDF_String>(L"Beta");
values->AppendNew<CPDF_String>(L"Epsilon");
auto selected_indices = pdfium::MakeRetain<CPDF_Array>();
selected_indices->AppendNew<CPDF_Number>(0);
selected_indices->AppendNew<CPDF_Number>(2);
selected_indices->AppendNew<CPDF_Number>(3);
std::vector<int> expected_indices{1, 4};
std::vector<int> excluded_indices{-1, 5};
TestMultiselectFieldDict(opt_array, values, selected_indices,
/*expected_use_indices=*/false, expected_indices,
excluded_indices);
}
{
// Values (/V) or Selected Indices (/I) objects conflict with same lengths.
auto values = pdfium::MakeRetain<CPDF_Array>();
values->AppendNew<CPDF_String>(L"Alpha");
values->AppendNew<CPDF_String>(L"Epsilon");
auto selected_indices = pdfium::MakeRetain<CPDF_Array>();
selected_indices->AppendNew<CPDF_Number>(2);
selected_indices->AppendNew<CPDF_Number>(3);
std::vector<int> expected_indices{0, 4};
std::vector<int> excluded_indices{-1, 5};
TestMultiselectFieldDict(opt_array, values, selected_indices,
/*expected_use_indices=*/false, expected_indices,
excluded_indices);
}
{
// Values (/V) or Selected Indices (/I) objects conflict with values being
// invalid.
auto values = pdfium::MakeRetain<CPDF_Array>();
values->AppendNew<CPDF_String>(L"Beta");
values->AppendNew<CPDF_String>(L"Epsilon");
values->AppendNew<CPDF_String>(L"Omega");
auto selected_indices = pdfium::MakeRetain<CPDF_Array>();
selected_indices->AppendNew<CPDF_Number>(1);
selected_indices->AppendNew<CPDF_Number>(4);
std::vector<int> expected_indices{1, 4};
std::vector<int> excluded_indices{-1, 5};
TestMultiselectFieldDict(opt_array, values, selected_indices,
/*expected_use_indices=*/false, expected_indices,
excluded_indices);
}
{
// Values (/V) or Selected Indices (/I) objects conflict with selected
// indices being invalid.
auto values = pdfium::MakeRetain<CPDF_Array>();
values->AppendNew<CPDF_String>(L"Beta");
values->AppendNew<CPDF_String>(L"Epsilon");
auto selected_indices = pdfium::MakeRetain<CPDF_Array>();
selected_indices->AppendNew<CPDF_Number>(1);
selected_indices->AppendNew<CPDF_Number>(4);
selected_indices->AppendNew<CPDF_Number>(26);
std::vector<int> expected_indices{1, 4};
std::vector<int> excluded_indices{-1, 5, 26};
TestMultiselectFieldDict(opt_array, values, selected_indices,
/*expected_use_indices=*/false, expected_indices,
excluded_indices);
}
{
// Values (/V) or Selected Indices (/I) objects conflict with both being
// invalid.
auto values = pdfium::MakeRetain<CPDF_Array>();
values->AppendNew<CPDF_String>(L"Beta");
values->AppendNew<CPDF_String>(L"Epsilon");
values->AppendNew<CPDF_String>(L"Omega");
auto selected_indices = pdfium::MakeRetain<CPDF_Array>();
selected_indices->AppendNew<CPDF_Number>(0);
selected_indices->AppendNew<CPDF_Number>(2);
selected_indices->AppendNew<CPDF_Number>(3);
selected_indices->AppendNew<CPDF_Number>(26);
std::vector<int> expected_indices{1, 4};
std::vector<int> excluded_indices{-1, 5, 26};
TestMultiselectFieldDict(opt_array, values, selected_indices,
/*expected_use_indices=*/false, expected_indices,
excluded_indices);
}
{
// Values (/V) or Selected Indices (/I) objects conflict with each not being
// an array.
auto values = pdfium::MakeRetain<CPDF_String>(/*pPool=*/nullptr, L"Gamma");
auto selected_indices = pdfium::MakeRetain<CPDF_Number>(4);
std::vector<int> expected_indices{2};
std::vector<int> excluded_indices{-1, 5};
TestMultiselectFieldDict(opt_array, values, selected_indices,
/*expected_use_indices=*/false, expected_indices,
excluded_indices);
}
}