| #include <QtTest/QTest> |
| |
| #include "PDFDoc.h" |
| #include "GlobalParams.h" |
| |
| #include <poppler-qt5.h> |
| #include <poppler-optcontent-private.h> |
| |
| class TestOptionalContent : public QObject |
| { |
| Q_OBJECT |
| public: |
| explicit TestOptionalContent(QObject *parent = nullptr) : QObject(parent) { } |
| private slots: |
| void checkVisPolicy(); |
| void checkNestedLayers(); |
| void checkNoOptionalContent(); |
| void checkIsVisible(); |
| void checkVisibilitySetting(); |
| void checkRadioButtons(); |
| }; |
| |
| void TestOptionalContent::checkVisPolicy() |
| { |
| Poppler::Document *doc; |
| doc = Poppler::Document::load(TESTDATADIR "/unittestcases/vis_policy_test.pdf"); |
| QVERIFY(doc); |
| |
| QVERIFY(doc->hasOptionalContent()); |
| |
| Poppler::OptContentModel *optContent = doc->optionalContentModel(); |
| QModelIndex index; |
| index = optContent->index(0, 0, QModelIndex()); |
| QCOMPARE(optContent->data(index, Qt::DisplayRole).toString(), QLatin1String("A")); |
| QCOMPARE(static_cast<Qt::CheckState>(optContent->data(index, Qt::CheckStateRole).toInt()), Qt::Checked); |
| index = optContent->index(1, 0, QModelIndex()); |
| QCOMPARE(optContent->data(index, Qt::DisplayRole).toString(), QLatin1String("B")); |
| QCOMPARE(static_cast<Qt::CheckState>(optContent->data(index, Qt::CheckStateRole).toInt()), Qt::Checked); |
| |
| delete doc; |
| } |
| |
| void TestOptionalContent::checkNestedLayers() |
| { |
| Poppler::Document *doc; |
| doc = Poppler::Document::load(TESTDATADIR "/unittestcases/NestedLayers.pdf"); |
| QVERIFY(doc); |
| |
| QVERIFY(doc->hasOptionalContent()); |
| |
| Poppler::OptContentModel *optContent = doc->optionalContentModel(); |
| QModelIndex index; |
| |
| index = optContent->index(0, 0, QModelIndex()); |
| QCOMPARE(optContent->data(index, Qt::DisplayRole).toString(), QLatin1String("Black Text and Green Snow")); |
| QCOMPARE(static_cast<Qt::CheckState>(optContent->data(index, Qt::CheckStateRole).toInt()), Qt::Unchecked); |
| |
| index = optContent->index(1, 0, QModelIndex()); |
| QCOMPARE(optContent->data(index, Qt::DisplayRole).toString(), QLatin1String("Mountains and Image")); |
| QCOMPARE(static_cast<Qt::CheckState>(optContent->data(index, Qt::CheckStateRole).toInt()), Qt::Checked); |
| |
| // This is a sub-item of "Mountains and Image" |
| QModelIndex subindex = optContent->index(0, 0, index); |
| QCOMPARE(optContent->data(subindex, Qt::DisplayRole).toString(), QLatin1String("Image")); |
| QCOMPARE(static_cast<Qt::CheckState>(optContent->data(index, Qt::CheckStateRole).toInt()), Qt::Checked); |
| |
| index = optContent->index(2, 0, QModelIndex()); |
| QCOMPARE(optContent->data(index, Qt::DisplayRole).toString(), QLatin1String("Starburst")); |
| QCOMPARE(static_cast<Qt::CheckState>(optContent->data(index, Qt::CheckStateRole).toInt()), Qt::Checked); |
| |
| index = optContent->index(3, 0, QModelIndex()); |
| QCOMPARE(optContent->data(index, Qt::DisplayRole).toString(), QLatin1String("Watermark")); |
| QCOMPARE(static_cast<Qt::CheckState>(optContent->data(index, Qt::CheckStateRole).toInt()), Qt::Unchecked); |
| |
| delete doc; |
| } |
| |
| void TestOptionalContent::checkNoOptionalContent() |
| { |
| Poppler::Document *doc; |
| doc = Poppler::Document::load(TESTDATADIR "/unittestcases/orientation.pdf"); |
| QVERIFY(doc); |
| |
| QCOMPARE(doc->hasOptionalContent(), false); |
| |
| delete doc; |
| } |
| |
| void TestOptionalContent::checkIsVisible() |
| { |
| globalParams = std::make_unique<GlobalParams>(); |
| PDFDoc *doc = new PDFDoc(std::make_unique<GooString>(TESTDATADIR "/unittestcases/vis_policy_test.pdf")); |
| QVERIFY(doc); |
| |
| OCGs *ocgs = doc->getOptContentConfig(); |
| QVERIFY(ocgs); |
| |
| XRef *xref = doc->getXRef(); |
| |
| Object obj; |
| |
| // In this test, both Ref(21,0) and Ref(2,0) are set to On |
| |
| // AnyOn, one element array: |
| // 22 0 obj<</Type/OCMD/OCGs[21 0 R]/P/AnyOn>>endobj |
| obj = xref->fetch(22, 0); |
| QVERIFY(obj.isDict()); |
| QVERIFY(ocgs->optContentIsVisible(&obj)); |
| |
| // Same again, looking for any leaks or dubious free()'s |
| obj = xref->fetch(22, 0); |
| QVERIFY(obj.isDict()); |
| QVERIFY(ocgs->optContentIsVisible(&obj)); |
| |
| // AnyOff, one element array: |
| // 29 0 obj<</Type/OCMD/OCGs[21 0 R]/P/AnyOff>>endobj |
| obj = xref->fetch(29, 0); |
| QVERIFY(obj.isDict()); |
| QCOMPARE(ocgs->optContentIsVisible(&obj), false); |
| |
| // AllOn, one element array: |
| // 36 0 obj<</Type/OCMD/OCGs[28 0 R]/P/AllOn>>endobj |
| obj = xref->fetch(36, 0); |
| QVERIFY(obj.isDict()); |
| QCOMPARE(ocgs->optContentIsVisible(&obj), true); |
| |
| // AllOff, one element array: |
| // 43 0 obj<</Type/OCMD/OCGs[28 0 R]/P/AllOff>>endobj |
| obj = xref->fetch(43, 0); |
| QVERIFY(obj.isDict()); |
| QCOMPARE(ocgs->optContentIsVisible(&obj), false); |
| |
| // AnyOn, multi-element array: |
| // 50 0 obj<</Type/OCMD/OCGs[21 0 R 28 0 R]/P/AnyOn>>endobj |
| obj = xref->fetch(50, 0); |
| QVERIFY(obj.isDict()); |
| QCOMPARE(ocgs->optContentIsVisible(&obj), true); |
| |
| // AnyOff, multi-element array: |
| // 57 0 obj<</Type/OCMD/P/AnyOff/OCGs[21 0 R 28 0 R]>>endobj |
| obj = xref->fetch(57, 0); |
| QVERIFY(obj.isDict()); |
| QCOMPARE(ocgs->optContentIsVisible(&obj), false); |
| |
| // AllOn, multi-element array: |
| // 64 0 obj<</Type/OCMD/P/AllOn/OCGs[21 0 R 28 0 R]>>endobj |
| obj = xref->fetch(64, 0); |
| QVERIFY(obj.isDict()); |
| QCOMPARE(ocgs->optContentIsVisible(&obj), true); |
| |
| // AllOff, multi-element array: |
| // 71 0 obj<</Type/OCMD/P/AllOff/OCGs[21 0 R 28 0 R]>>endobj |
| obj = xref->fetch(71, 0); |
| QVERIFY(obj.isDict()); |
| QCOMPARE(ocgs->optContentIsVisible(&obj), false); |
| |
| delete doc; |
| globalParams.reset(); |
| } |
| |
| void TestOptionalContent::checkVisibilitySetting() |
| { |
| globalParams = std::make_unique<GlobalParams>(); |
| PDFDoc *doc = new PDFDoc(std::make_unique<GooString>(TESTDATADIR "/unittestcases/vis_policy_test.pdf")); |
| QVERIFY(doc); |
| |
| OCGs *ocgs = doc->getOptContentConfig(); |
| QVERIFY(ocgs); |
| |
| XRef *xref = doc->getXRef(); |
| |
| Object obj; |
| |
| // In this test, both Ref(21,0) and Ref(28,0) start On, |
| // based on the file settings |
| Object ref21obj(Ref { 21, 0 }); |
| Ref ref21 = ref21obj.getRef(); |
| OptionalContentGroup *ocgA = ocgs->findOcgByRef(ref21); |
| QVERIFY(ocgA); |
| |
| QVERIFY((ocgA->getName()->cmp("A")) == 0); |
| QCOMPARE(ocgA->getState(), OptionalContentGroup::On); |
| |
| Object ref28obj(Ref { 28, 0 }); |
| Ref ref28 = ref28obj.getRef(); |
| OptionalContentGroup *ocgB = ocgs->findOcgByRef(ref28); |
| QVERIFY(ocgB); |
| |
| QVERIFY((ocgB->getName()->cmp("B")) == 0); |
| QCOMPARE(ocgB->getState(), OptionalContentGroup::On); |
| |
| // turn one Off |
| ocgA->setState(OptionalContentGroup::Off); |
| |
| // AnyOn, one element array: |
| // 22 0 obj<</Type/OCMD/OCGs[21 0 R]/P/AnyOn>>endobj |
| obj = xref->fetch(22, 0); |
| QVERIFY(obj.isDict()); |
| QCOMPARE(ocgs->optContentIsVisible(&obj), false); |
| |
| // Same again, looking for any leaks or dubious free()'s |
| obj = xref->fetch(22, 0); |
| QVERIFY(obj.isDict()); |
| QCOMPARE(ocgs->optContentIsVisible(&obj), false); |
| |
| // AnyOff, one element array: |
| // 29 0 obj<</Type/OCMD/OCGs[21 0 R]/P/AnyOff>>endobj |
| obj = xref->fetch(29, 0); |
| QVERIFY(obj.isDict()); |
| QCOMPARE(ocgs->optContentIsVisible(&obj), true); |
| |
| // AllOn, one element array: |
| // 36 0 obj<</Type/OCMD/OCGs[28 0 R]/P/AllOn>>endobj |
| obj = xref->fetch(36, 0); |
| QVERIFY(obj.isDict()); |
| QCOMPARE(ocgs->optContentIsVisible(&obj), true); |
| |
| // AllOff, one element array: |
| // 43 0 obj<</Type/OCMD/OCGs[28 0 R]/P/AllOff>>endobj |
| obj = xref->fetch(43, 0); |
| QVERIFY(obj.isDict()); |
| QCOMPARE(ocgs->optContentIsVisible(&obj), false); |
| |
| // AnyOn, multi-element array: |
| // 50 0 obj<</Type/OCMD/OCGs[21 0 R 28 0 R]/P/AnyOn>>endobj |
| obj = xref->fetch(50, 0); |
| QVERIFY(obj.isDict()); |
| QCOMPARE(ocgs->optContentIsVisible(&obj), true); |
| |
| // AnyOff, multi-element array: |
| // 57 0 obj<</Type/OCMD/P/AnyOff/OCGs[21 0 R 28 0 R]>>endobj |
| obj = xref->fetch(57, 0); |
| QVERIFY(obj.isDict()); |
| QCOMPARE(ocgs->optContentIsVisible(&obj), true); |
| |
| // AllOn, multi-element array: |
| // 64 0 obj<</Type/OCMD/P/AllOn/OCGs[21 0 R 28 0 R]>>endobj |
| obj = xref->fetch(64, 0); |
| QVERIFY(obj.isDict()); |
| QCOMPARE(ocgs->optContentIsVisible(&obj), false); |
| |
| // AllOff, multi-element array: |
| // 71 0 obj<</Type/OCMD/P/AllOff/OCGs[21 0 R 28 0 R]>>endobj |
| obj = xref->fetch(71, 0); |
| QVERIFY(obj.isDict()); |
| QCOMPARE(ocgs->optContentIsVisible(&obj), false); |
| |
| // Turn the other one off as well (i.e. both are Off) |
| ocgB->setState(OptionalContentGroup::Off); |
| |
| // AnyOn, one element array: |
| // 22 0 obj<</Type/OCMD/OCGs[21 0 R]/P/AnyOn>>endobj |
| obj = xref->fetch(22, 0); |
| QVERIFY(obj.isDict()); |
| QCOMPARE(ocgs->optContentIsVisible(&obj), false); |
| |
| // Same again, looking for any leaks or dubious free()'s |
| obj = xref->fetch(22, 0); |
| QVERIFY(obj.isDict()); |
| QCOMPARE(ocgs->optContentIsVisible(&obj), false); |
| |
| // AnyOff, one element array: |
| // 29 0 obj<</Type/OCMD/OCGs[21 0 R]/P/AnyOff>>endobj |
| obj = xref->fetch(29, 0); |
| QVERIFY(obj.isDict()); |
| QCOMPARE(ocgs->optContentIsVisible(&obj), true); |
| |
| // AllOn, one element array: |
| // 36 0 obj<</Type/OCMD/OCGs[28 0 R]/P/AllOn>>endobj |
| obj = xref->fetch(36, 0); |
| QVERIFY(obj.isDict()); |
| QCOMPARE(ocgs->optContentIsVisible(&obj), false); |
| |
| // AllOff, one element array: |
| // 43 0 obj<</Type/OCMD/OCGs[28 0 R]/P/AllOff>>endobj |
| obj = xref->fetch(43, 0); |
| QVERIFY(obj.isDict()); |
| QCOMPARE(ocgs->optContentIsVisible(&obj), true); |
| |
| // AnyOn, multi-element array: |
| // 50 0 obj<</Type/OCMD/OCGs[21 0 R 28 0 R]/P/AnyOn>>endobj |
| obj = xref->fetch(50, 0); |
| QVERIFY(obj.isDict()); |
| QCOMPARE(ocgs->optContentIsVisible(&obj), false); |
| |
| // AnyOff, multi-element array: |
| // 57 0 obj<</Type/OCMD/P/AnyOff/OCGs[21 0 R 28 0 R]>>endobj |
| obj = xref->fetch(57, 0); |
| QVERIFY(obj.isDict()); |
| QCOMPARE(ocgs->optContentIsVisible(&obj), true); |
| |
| // AllOn, multi-element array: |
| // 64 0 obj<</Type/OCMD/P/AllOn/OCGs[21 0 R 28 0 R]>>endobj |
| obj = xref->fetch(64, 0); |
| QVERIFY(obj.isDict()); |
| QCOMPARE(ocgs->optContentIsVisible(&obj), false); |
| |
| // AllOff, multi-element array: |
| // 71 0 obj<</Type/OCMD/P/AllOff/OCGs[21 0 R 28 0 R]>>endobj |
| obj = xref->fetch(71, 0); |
| QVERIFY(obj.isDict()); |
| QCOMPARE(ocgs->optContentIsVisible(&obj), true); |
| |
| // Turn the first one on again (21 is On, 28 is Off) |
| ocgA->setState(OptionalContentGroup::On); |
| |
| // AnyOn, one element array: |
| // 22 0 obj<</Type/OCMD/OCGs[21 0 R]/P/AnyOn>>endobj |
| obj = xref->fetch(22, 0); |
| QVERIFY(obj.isDict()); |
| QCOMPARE(ocgs->optContentIsVisible(&obj), true); |
| |
| // Same again, looking for any leaks or dubious free()'s |
| obj = xref->fetch(22, 0); |
| QVERIFY(obj.isDict()); |
| QCOMPARE(ocgs->optContentIsVisible(&obj), true); |
| |
| // AnyOff, one element array: |
| // 29 0 obj<</Type/OCMD/OCGs[21 0 R]/P/AnyOff>>endobj |
| obj = xref->fetch(29, 0); |
| QVERIFY(obj.isDict()); |
| QCOMPARE(ocgs->optContentIsVisible(&obj), false); |
| |
| // AllOn, one element array: |
| // 36 0 obj<</Type/OCMD/OCGs[28 0 R]/P/AllOn>>endobj |
| obj = xref->fetch(36, 0); |
| QVERIFY(obj.isDict()); |
| QCOMPARE(ocgs->optContentIsVisible(&obj), false); |
| |
| // AllOff, one element array: |
| // 43 0 obj<</Type/OCMD/OCGs[28 0 R]/P/AllOff>>endobj |
| obj = xref->fetch(43, 0); |
| QVERIFY(obj.isDict()); |
| QCOMPARE(ocgs->optContentIsVisible(&obj), true); |
| |
| // AnyOn, multi-element array: |
| // 50 0 obj<</Type/OCMD/OCGs[21 0 R 28 0 R]/P/AnyOn>>endobj |
| obj = xref->fetch(50, 0); |
| QVERIFY(obj.isDict()); |
| QCOMPARE(ocgs->optContentIsVisible(&obj), true); |
| |
| // AnyOff, multi-element array: |
| // 57 0 obj<</Type/OCMD/P/AnyOff/OCGs[21 0 R 28 0 R]>>endobj |
| obj = xref->fetch(57, 0); |
| QVERIFY(obj.isDict()); |
| QCOMPARE(ocgs->optContentIsVisible(&obj), true); |
| |
| // AllOn, multi-element array: |
| // 64 0 obj<</Type/OCMD/P/AllOn/OCGs[21 0 R 28 0 R]>>endobj |
| obj = xref->fetch(64, 0); |
| QVERIFY(obj.isDict()); |
| QCOMPARE(ocgs->optContentIsVisible(&obj), false); |
| |
| // AllOff, multi-element array: |
| // 71 0 obj<</Type/OCMD/P/AllOff/OCGs[21 0 R 28 0 R]>>endobj |
| obj = xref->fetch(71, 0); |
| QVERIFY(obj.isDict()); |
| QCOMPARE(ocgs->optContentIsVisible(&obj), false); |
| |
| delete doc; |
| globalParams.reset(); |
| } |
| |
| void TestOptionalContent::checkRadioButtons() |
| { |
| Poppler::Document *doc; |
| doc = Poppler::Document::load(TESTDATADIR "/unittestcases/ClarityOCGs.pdf"); |
| QVERIFY(doc); |
| |
| QVERIFY(doc->hasOptionalContent()); |
| |
| Poppler::OptContentModel *optContent = doc->optionalContentModel(); |
| QModelIndex index; |
| |
| index = optContent->index(0, 0, QModelIndex()); |
| QCOMPARE(optContent->data(index, Qt::DisplayRole).toString(), QLatin1String("Languages")); |
| QCOMPARE(static_cast<Qt::CheckState>(optContent->data(index, Qt::CheckStateRole).toInt()), Qt::Unchecked); |
| |
| // These are sub-items of the "Languages" label |
| QModelIndex subindex = optContent->index(0, 0, index); |
| QCOMPARE(optContent->data(subindex, Qt::DisplayRole).toString(), QLatin1String("English")); |
| QCOMPARE(static_cast<Qt::CheckState>(optContent->data(subindex, Qt::CheckStateRole).toInt()), Qt::Checked); |
| |
| subindex = optContent->index(1, 0, index); |
| QCOMPARE(optContent->data(subindex, Qt::DisplayRole).toString(), QLatin1String("French")); |
| QCOMPARE(static_cast<Qt::CheckState>(optContent->data(subindex, Qt::CheckStateRole).toInt()), Qt::Unchecked); |
| |
| subindex = optContent->index(2, 0, index); |
| QCOMPARE(optContent->data(subindex, Qt::DisplayRole).toString(), QLatin1String("Japanese")); |
| QCOMPARE(static_cast<Qt::CheckState>(optContent->data(subindex, Qt::CheckStateRole).toInt()), Qt::Unchecked); |
| |
| // RBGroup of languages, so turning on Japanese should turn off English |
| QVERIFY(optContent->setData(subindex, QVariant(true), Qt::CheckStateRole)); |
| |
| subindex = optContent->index(0, 0, index); |
| QCOMPARE(optContent->data(subindex, Qt::DisplayRole).toString(), QLatin1String("English")); |
| QCOMPARE(static_cast<Qt::CheckState>(optContent->data(subindex, Qt::CheckStateRole).toInt()), Qt::Unchecked); |
| QCOMPARE(static_cast<Poppler::OptContentItem *>(subindex.internalPointer())->group()->getState(), OptionalContentGroup::Off); |
| |
| subindex = optContent->index(2, 0, index); |
| QCOMPARE(optContent->data(subindex, Qt::DisplayRole).toString(), QLatin1String("Japanese")); |
| QCOMPARE(static_cast<Qt::CheckState>(optContent->data(subindex, Qt::CheckStateRole).toInt()), Qt::Checked); |
| QCOMPARE(static_cast<Poppler::OptContentItem *>(subindex.internalPointer())->group()->getState(), OptionalContentGroup::On); |
| |
| subindex = optContent->index(1, 0, index); |
| QCOMPARE(optContent->data(subindex, Qt::DisplayRole).toString(), QLatin1String("French")); |
| QCOMPARE(static_cast<Qt::CheckState>(optContent->data(subindex, Qt::CheckStateRole).toInt()), Qt::Unchecked); |
| QCOMPARE(static_cast<Poppler::OptContentItem *>(subindex.internalPointer())->group()->getState(), OptionalContentGroup::Off); |
| |
| // and turning on French should turn off Japanese |
| QVERIFY(optContent->setData(subindex, QVariant(true), Qt::CheckStateRole)); |
| |
| subindex = optContent->index(0, 0, index); |
| QCOMPARE(optContent->data(subindex, Qt::DisplayRole).toString(), QLatin1String("English")); |
| QCOMPARE(static_cast<Qt::CheckState>(optContent->data(subindex, Qt::CheckStateRole).toInt()), Qt::Unchecked); |
| QCOMPARE(static_cast<Poppler::OptContentItem *>(subindex.internalPointer())->group()->getState(), OptionalContentGroup::Off); |
| |
| subindex = optContent->index(2, 0, index); |
| QCOMPARE(optContent->data(subindex, Qt::DisplayRole).toString(), QLatin1String("Japanese")); |
| QCOMPARE(static_cast<Qt::CheckState>(optContent->data(subindex, Qt::CheckStateRole).toInt()), Qt::Unchecked); |
| QCOMPARE(static_cast<Poppler::OptContentItem *>(subindex.internalPointer())->group()->getState(), OptionalContentGroup::Off); |
| |
| subindex = optContent->index(1, 0, index); |
| QCOMPARE(optContent->data(subindex, Qt::DisplayRole).toString(), QLatin1String("French")); |
| QCOMPARE(static_cast<Qt::CheckState>(optContent->data(subindex, Qt::CheckStateRole).toInt()), Qt::Checked); |
| QCOMPARE(static_cast<Poppler::OptContentItem *>(subindex.internalPointer())->group()->getState(), OptionalContentGroup::On); |
| |
| // and turning off French should leave them all off |
| QVERIFY(optContent->setData(subindex, QVariant(false), Qt::CheckStateRole)); |
| |
| subindex = optContent->index(0, 0, index); |
| QCOMPARE(optContent->data(subindex, Qt::DisplayRole).toString(), QLatin1String("English")); |
| QCOMPARE(static_cast<Qt::CheckState>(optContent->data(subindex, Qt::CheckStateRole).toInt()), Qt::Unchecked); |
| QCOMPARE(static_cast<Poppler::OptContentItem *>(subindex.internalPointer())->group()->getState(), OptionalContentGroup::Off); |
| |
| subindex = optContent->index(2, 0, index); |
| QCOMPARE(optContent->data(subindex, Qt::DisplayRole).toString(), QLatin1String("Japanese")); |
| QCOMPARE(static_cast<Qt::CheckState>(optContent->data(subindex, Qt::CheckStateRole).toInt()), Qt::Unchecked); |
| QCOMPARE(static_cast<Poppler::OptContentItem *>(subindex.internalPointer())->group()->getState(), OptionalContentGroup::Off); |
| |
| subindex = optContent->index(1, 0, index); |
| QCOMPARE(optContent->data(subindex, Qt::DisplayRole).toString(), QLatin1String("French")); |
| QCOMPARE(static_cast<Qt::CheckState>(optContent->data(subindex, Qt::CheckStateRole).toInt()), Qt::Unchecked); |
| QCOMPARE(static_cast<Poppler::OptContentItem *>(subindex.internalPointer())->group()->getState(), OptionalContentGroup::Off); |
| |
| delete doc; |
| } |
| |
| QTEST_GUILESS_MAIN(TestOptionalContent) |
| |
| #include "check_optcontent.moc" |