diff options
author | Tomaž Vajngerl <tomaz.vajngerl@collabora.co.uk> | 2020-06-15 14:44:19 +0200 |
---|---|---|
committer | Tomaž Vajngerl <quikee@gmail.com> | 2020-06-20 14:24:33 +0200 |
commit | 7e4dc3b1eabcb1993d4143c046a2f32fedc852ed (patch) | |
tree | 20af7c9e4d2502e713e2ed0f672c1cc2d9c293e5 | |
parent | 03f0ea92bc381ef5a8df7de1ae9edf4aed45a3b2 (diff) |
vcl: Add annotation reading to PDFiumLibrary c++ wrapper
Also add tests readin annotations from Evince and Acrobat modified
PDF files.
Change-Id: I4565c6b621774fc8485a6c33bc18708664917b73
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/96756
Tested-by: Tomaž Vajngerl <quikee@gmail.com>
Reviewed-by: Tomaž Vajngerl <quikee@gmail.com>
-rw-r--r-- | include/vcl/filter/PDFiumLibrary.hxx | 30 | ||||
-rw-r--r-- | vcl/qa/cppunit/PDFiumLibraryTest.cxx | 135 | ||||
-rw-r--r-- | vcl/qa/cppunit/data/PangramAcrobatAnnotations.pdf | bin | 0 -> 21709 bytes | |||
-rw-r--r-- | vcl/qa/cppunit/data/PangramWithAnnotations.pdf | bin | 0 -> 17785 bytes | |||
-rw-r--r-- | vcl/source/pdf/PDFiumLibrary.cxx | 73 |
5 files changed, 238 insertions, 0 deletions
diff --git a/include/vcl/filter/PDFiumLibrary.hxx b/include/vcl/filter/PDFiumLibrary.hxx index 6dde31f2927b..9deff47e0ab9 100644 --- a/include/vcl/filter/PDFiumLibrary.hxx +++ b/include/vcl/filter/PDFiumLibrary.hxx @@ -19,12 +19,17 @@ #include <memory> #include <rtl/instance.hxx> #include <basegfx/vector/b2dsize.hxx> +#include <basegfx/range/b2drectangle.hxx> #include <rtl/ustring.hxx> #include <fpdf_doc.h> namespace vcl::pdf { +constexpr char constDictionaryKeyTitle[] = "T"; +constexpr char constDictionaryKeyContents[] = "Contents"; +constexpr char constDictionaryKeyPopup[] = "Popup"; + class PDFiumDocument; class VCL_DLLPUBLIC PDFium final @@ -44,6 +49,26 @@ public: std::unique_ptr<PDFiumDocument> openDocument(const void* pData, int nSize); }; +class VCL_DLLPUBLIC PDFiumAnnotation final +{ +private: + FPDF_ANNOTATION mpAnnotation; + + PDFiumAnnotation(const PDFiumAnnotation&) = delete; + PDFiumAnnotation& operator=(const PDFiumAnnotation&) = delete; + +public: + PDFiumAnnotation(FPDF_ANNOTATION pAnnotation); + ~PDFiumAnnotation(); + FPDF_ANNOTATION getPointer() { return mpAnnotation; } + + int getSubType(); + basegfx::B2DRectangle getRectangle(); + bool hasKey(OString const& rKey); + OUString getString(OString const& rKey); + std::unique_ptr<PDFiumAnnotation> getLinked(OString const& rKey); +}; + class VCL_DLLPUBLIC PDFiumPage final { private: @@ -64,6 +89,11 @@ public: if (mpPage) FPDF_ClosePage(mpPage); } + + int getAnnotationCount(); + int getAnnotationIndex(std::unique_ptr<PDFiumAnnotation> const& rAnnotation); + + std::unique_ptr<PDFiumAnnotation> getAnnotation(int nIndex); }; class VCL_DLLPUBLIC PDFiumDocument final diff --git a/vcl/qa/cppunit/PDFiumLibraryTest.cxx b/vcl/qa/cppunit/PDFiumLibraryTest.cxx index 422325aa9b1d..c786b6edc211 100644 --- a/vcl/qa/cppunit/PDFiumLibraryTest.cxx +++ b/vcl/qa/cppunit/PDFiumLibraryTest.cxx @@ -32,10 +32,14 @@ class PDFiumLibraryTest : public test::BootstrapFixtureBase void testDocument(); void testPages(); + void testAnnotationsMadeInEvince(); + void testAnnotationsMadeInAcrobat(); CPPUNIT_TEST_SUITE(PDFiumLibraryTest); CPPUNIT_TEST(testDocument); CPPUNIT_TEST(testPages); + CPPUNIT_TEST(testAnnotationsMadeInEvince); + CPPUNIT_TEST(testAnnotationsMadeInAcrobat); CPPUNIT_TEST_SUITE_END(); }; @@ -96,6 +100,137 @@ void PDFiumLibraryTest::testPages() CPPUNIT_ASSERT(pPage); } +void PDFiumLibraryTest::testAnnotationsMadeInEvince() +{ + OUString aURL = getFullUrl("PangramWithAnnotations.pdf"); + SvFileStream aStream(aURL, StreamMode::READ); + GraphicFilter& rGraphicFilter = GraphicFilter::GetGraphicFilter(); + Graphic aGraphic = rGraphicFilter.ImportUnloadedGraphic(aStream); + aGraphic.makeAvailable(); + + auto pVectorGraphicData = aGraphic.getVectorGraphicData(); + CPPUNIT_ASSERT(pVectorGraphicData); + CPPUNIT_ASSERT_EQUAL(VectorGraphicDataType::Pdf, + pVectorGraphicData->getVectorGraphicDataType()); + + const void* pData = pVectorGraphicData->getVectorGraphicDataArray().getConstArray(); + int nLength = pVectorGraphicData->getVectorGraphicDataArrayLength(); + + auto pPdfium = vcl::pdf::PDFiumLibrary::get(); + auto pDocument = pPdfium->openDocument(pData, nLength); + CPPUNIT_ASSERT(pDocument); + + CPPUNIT_ASSERT_EQUAL(1, pDocument->getPageCount()); + + auto pPage = pDocument->openPage(0); + CPPUNIT_ASSERT(pPage); + + CPPUNIT_ASSERT_EQUAL(2, pPage->getAnnotationCount()); + + { + auto pAnnotation = pPage->getAnnotation(0); + CPPUNIT_ASSERT(pAnnotation); + CPPUNIT_ASSERT_EQUAL(1, pAnnotation->getSubType()); // FPDF_ANNOT_TEXT + + OUString aPopupString = pAnnotation->getString(vcl::pdf::constDictionaryKeyTitle); + CPPUNIT_ASSERT_EQUAL(OUString("quikee"), aPopupString); + + OUString aContentsString = pAnnotation->getString(vcl::pdf::constDictionaryKeyContents); + CPPUNIT_ASSERT_EQUAL(OUString("Annotation test"), aContentsString); + + CPPUNIT_ASSERT_EQUAL(true, pAnnotation->hasKey(vcl::pdf::constDictionaryKeyPopup)); + auto pPopupAnnotation = pAnnotation->getLinked(vcl::pdf::constDictionaryKeyPopup); + CPPUNIT_ASSERT(pPopupAnnotation); + + CPPUNIT_ASSERT_EQUAL(1, pPage->getAnnotationIndex(pPopupAnnotation)); + CPPUNIT_ASSERT_EQUAL(16, pPopupAnnotation->getSubType()); + } + + { + auto pAnnotation = pPage->getAnnotation(1); + CPPUNIT_ASSERT(pAnnotation); + CPPUNIT_ASSERT_EQUAL(16, pAnnotation->getSubType()); // FPDF_ANNOT_POPUP + } +} + +void PDFiumLibraryTest::testAnnotationsMadeInAcrobat() +{ + OUString aURL = getFullUrl("PangramAcrobatAnnotations.pdf"); + SvFileStream aStream(aURL, StreamMode::READ); + GraphicFilter& rGraphicFilter = GraphicFilter::GetGraphicFilter(); + Graphic aGraphic = rGraphicFilter.ImportUnloadedGraphic(aStream); + aGraphic.makeAvailable(); + + auto pVectorGraphicData = aGraphic.getVectorGraphicData(); + CPPUNIT_ASSERT(pVectorGraphicData); + CPPUNIT_ASSERT_EQUAL(VectorGraphicDataType::Pdf, + pVectorGraphicData->getVectorGraphicDataType()); + + const void* pData = pVectorGraphicData->getVectorGraphicDataArray().getConstArray(); + int nLength = pVectorGraphicData->getVectorGraphicDataArrayLength(); + + auto pPdfium = vcl::pdf::PDFiumLibrary::get(); + auto pDocument = pPdfium->openDocument(pData, nLength); + CPPUNIT_ASSERT(pDocument); + + CPPUNIT_ASSERT_EQUAL(1, pDocument->getPageCount()); + + auto pPage = pDocument->openPage(0); + CPPUNIT_ASSERT(pPage); + + CPPUNIT_ASSERT_EQUAL(4, pPage->getAnnotationCount()); + + { + auto pAnnotation = pPage->getAnnotation(0); + CPPUNIT_ASSERT(pAnnotation); + CPPUNIT_ASSERT_EQUAL(1, pAnnotation->getSubType()); // FPDF_ANNOT_TEXT + + OUString aPopupString = pAnnotation->getString(vcl::pdf::constDictionaryKeyTitle); + CPPUNIT_ASSERT_EQUAL(OUString("quikee"), aPopupString); + + OUString aContentsString = pAnnotation->getString(vcl::pdf::constDictionaryKeyContents); + CPPUNIT_ASSERT_EQUAL(OUString("YEEEY"), aContentsString); + + CPPUNIT_ASSERT_EQUAL(true, pAnnotation->hasKey(vcl::pdf::constDictionaryKeyPopup)); + auto pPopupAnnotation = pAnnotation->getLinked(vcl::pdf::constDictionaryKeyPopup); + CPPUNIT_ASSERT(pPopupAnnotation); + + CPPUNIT_ASSERT_EQUAL(1, pPage->getAnnotationIndex(pPopupAnnotation)); + CPPUNIT_ASSERT_EQUAL(16, pPopupAnnotation->getSubType()); + } + + { + auto pAnnotation = pPage->getAnnotation(1); + CPPUNIT_ASSERT(pAnnotation); + CPPUNIT_ASSERT_EQUAL(16, pAnnotation->getSubType()); // FPDF_ANNOT_POPUP + } + + { + auto pAnnotation = pPage->getAnnotation(2); + CPPUNIT_ASSERT(pAnnotation); + CPPUNIT_ASSERT_EQUAL(1, pAnnotation->getSubType()); // FPDF_ANNOT_TEXT + + OUString aPopupString = pAnnotation->getString(vcl::pdf::constDictionaryKeyTitle); + CPPUNIT_ASSERT_EQUAL(OUString("quikee"), aPopupString); + + OUString aContentsString = pAnnotation->getString(vcl::pdf::constDictionaryKeyContents); + CPPUNIT_ASSERT_EQUAL(OUString("Note"), aContentsString); + + CPPUNIT_ASSERT_EQUAL(true, pAnnotation->hasKey(vcl::pdf::constDictionaryKeyPopup)); + auto pPopupAnnotation = pAnnotation->getLinked(vcl::pdf::constDictionaryKeyPopup); + CPPUNIT_ASSERT(pPopupAnnotation); + + CPPUNIT_ASSERT_EQUAL(3, pPage->getAnnotationIndex(pPopupAnnotation)); + CPPUNIT_ASSERT_EQUAL(16, pPopupAnnotation->getSubType()); + } + + { + auto pAnnotation = pPage->getAnnotation(3); + CPPUNIT_ASSERT(pAnnotation); + CPPUNIT_ASSERT_EQUAL(16, pAnnotation->getSubType()); // FPDF_ANNOT_POPUP + } +} + CPPUNIT_TEST_SUITE_REGISTRATION(PDFiumLibraryTest); CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/vcl/qa/cppunit/data/PangramAcrobatAnnotations.pdf b/vcl/qa/cppunit/data/PangramAcrobatAnnotations.pdf Binary files differnew file mode 100644 index 000000000000..f97003a7f397 --- /dev/null +++ b/vcl/qa/cppunit/data/PangramAcrobatAnnotations.pdf diff --git a/vcl/qa/cppunit/data/PangramWithAnnotations.pdf b/vcl/qa/cppunit/data/PangramWithAnnotations.pdf Binary files differnew file mode 100644 index 000000000000..f69ddd987060 --- /dev/null +++ b/vcl/qa/cppunit/data/PangramWithAnnotations.pdf diff --git a/vcl/source/pdf/PDFiumLibrary.cxx b/vcl/source/pdf/PDFiumLibrary.cxx index edd7fe9f5283..faf59c24a5a3 100644 --- a/vcl/source/pdf/PDFiumLibrary.cxx +++ b/vcl/source/pdf/PDFiumLibrary.cxx @@ -13,6 +13,7 @@ #if HAVE_FEATURE_PDFIUM #include <vcl/filter/PDFiumLibrary.hxx> +#include <fpdf_annot.h> #include <fpdf_edit.h> namespace vcl::pdf @@ -108,6 +109,78 @@ basegfx::B2DSize PDFiumDocument::getPageSize(int nIndex) int PDFiumDocument::getPageCount() { return FPDF_GetPageCount(mpPdfDocument); } +int PDFiumPage::getAnnotationCount() { return FPDFPage_GetAnnotCount(mpPage); } + +int PDFiumPage::getAnnotationIndex(std::unique_ptr<PDFiumAnnotation> const& rAnnotation) +{ + return FPDFPage_GetAnnotIndex(mpPage, rAnnotation->getPointer()); +} + +std::unique_ptr<PDFiumAnnotation> PDFiumPage::getAnnotation(int nIndex) +{ + std::unique_ptr<PDFiumAnnotation> pPDFiumAnnotation; + FPDF_ANNOTATION pAnnotation = FPDFPage_GetAnnot(mpPage, nIndex); + if (pAnnotation) + { + pPDFiumAnnotation = std::make_unique<PDFiumAnnotation>(pAnnotation); + } + return pPDFiumAnnotation; +} + +PDFiumAnnotation::PDFiumAnnotation(FPDF_ANNOTATION pAnnotation) + : mpAnnotation(pAnnotation) +{ +} + +PDFiumAnnotation::~PDFiumAnnotation() +{ + if (mpAnnotation) + FPDFPage_CloseAnnot(mpAnnotation); +} + +int PDFiumAnnotation::getSubType() { return FPDFAnnot_GetSubtype(mpAnnotation); } + +basegfx::B2DRectangle PDFiumAnnotation::getRectangle() +{ + basegfx::B2DRectangle aB2DRectangle; + FS_RECTF aRect; + if (FPDFAnnot_GetRect(mpAnnotation, &aRect)) + { + aB2DRectangle = basegfx::B2DRectangle(aRect.left, aRect.top, aRect.right, aRect.bottom); + } + return aB2DRectangle; +} + +bool PDFiumAnnotation::hasKey(OString const& rKey) +{ + return FPDFAnnot_HasKey(mpAnnotation, rKey.getStr()); +} + +OUString PDFiumAnnotation::getString(OString const& rKey) +{ + OUString rString; + unsigned long nSize = FPDFAnnot_GetStringValue(mpAnnotation, rKey.getStr(), nullptr, 0); + if (nSize > 2) + { + std::unique_ptr<sal_Unicode[]> pText(new sal_Unicode[nSize]); + unsigned long nStringSize = FPDFAnnot_GetStringValue( + mpAnnotation, rKey.getStr(), reinterpret_cast<FPDF_WCHAR*>(pText.get()), nSize); + if (nStringSize > 0) + rString = OUString(pText.get()); + } + return rString; +} + +std::unique_ptr<PDFiumAnnotation> PDFiumAnnotation::getLinked(OString const& rKey) +{ + std::unique_ptr<PDFiumAnnotation> pPDFiumAnnotation; + FPDF_ANNOTATION pAnnotation = FPDFAnnot_GetLinkedAnnot(mpAnnotation, rKey.getStr()); + if (pAnnotation) + { + pPDFiumAnnotation = std::make_unique<PDFiumAnnotation>(pAnnotation); + } + return pPDFiumAnnotation; +} } // end vcl::pdf #endif // HAVE_FEATURE_PDFIUM |