include/vcl/filter/PDFiumLibrary.hxx | 30 ++++ include/vcl/pdfread.hxx | 16 ++ sd/source/filter/pdf/sdpdffilter.cxx | 26 +++- vcl/qa/cppunit/PDFiumLibraryTest.cxx | 135 ++++++++++++++++++++++ vcl/qa/cppunit/data/PangramAcrobatAnnotations.pdf |binary vcl/qa/cppunit/data/PangramWithAnnotations.pdf |binary vcl/source/filter/ipdf/pdfread.cxx | 31 ++++- vcl/source/pdf/PDFiumLibrary.cxx | 73 +++++++++++ 8 files changed, 306 insertions(+), 5 deletions(-)
New commits: commit 0a96c423e0f21d4c59c226f860e7353b17851aea Author: Tomaž Vajngerl <tomaz.vajng...@collabora.co.uk> AuthorDate: Thu Jun 18 13:30:38 2020 +0200 Commit: Tomaž Vajngerl <qui...@gmail.com> CommitDate: Mon Jun 29 20:37:00 2020 +0200 sd: support adding PDF text / pop-up annotations as comments Change-Id: I3e072f011089864f3349a470a32412cc33bcc022 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/96758 Tested-by: Jenkins Reviewed-by: Tomaž Vajngerl <qui...@gmail.com> (cherry picked from commit 3870dd43e94c440a5094a57c47d3b7565658d73c) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/97439 Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com> diff --git a/include/vcl/pdfread.hxx b/include/vcl/pdfread.hxx index a65d230279b9..d79115f40249 100644 --- a/include/vcl/pdfread.hxx +++ b/include/vcl/pdfread.hxx @@ -14,6 +14,7 @@ #include <tools/gen.hxx> #include <tools/stream.hxx> #include <vcl/graph.hxx> +#include <basegfx/range/b2drectangle.hxx> namespace com::sun::star::uno { @@ -31,16 +32,27 @@ VCL_DLLPUBLIC size_t RenderPDFBitmaps(const void* pBuffer, int nSize, std::vecto /// Imports a PDF stream into rGraphic as VectorGraphicData. VCL_DLLPUBLIC bool ImportPDF(SvStream& rStream, Graphic& rGraphic); +struct PDFGraphicAnnotation +{ + OUString maAuthor; + OUString maText; + // In HMM + basegfx::B2DRectangle maRectangle; +}; + struct PDFGraphicResult { Graphic maGraphic; - // Size in HMM Size maSize; - PDFGraphicResult(Graphic const& rGraphic, Size const& rSize) + std::vector<PDFGraphicAnnotation> maAnnotations; + + PDFGraphicResult(Graphic const& rGraphic, Size const& rSize, + std::vector<PDFGraphicAnnotation> const& aAnnotations) : maGraphic(rGraphic) , maSize(rSize) + , maAnnotations(aAnnotations) { } }; diff --git a/sd/source/filter/pdf/sdpdffilter.cxx b/sd/source/filter/pdf/sdpdffilter.cxx index 91eb0d1a6ae4..552e19f597e9 100644 --- a/sd/source/filter/pdf/sdpdffilter.cxx +++ b/sd/source/filter/pdf/sdpdffilter.cxx @@ -28,6 +28,11 @@ #include <vcl/graph.hxx> #include <vcl/pdfread.hxx> +#include <com/sun/star/office/XAnnotation.hpp> +#include <com/sun/star/text/XText.hpp> + +using namespace css; + SdPdfFilter::SdPdfFilter(SfxMedium& rMedium, sd::DrawDocShell& rDocShell) : SdFilter(rMedium, rDocShell) { @@ -64,11 +69,28 @@ bool SdPdfFilter::Import() // Make the page size match the rendered image. pPage->SetSize(aSizeHMM); - Point aPosition(0, 0); SdrGrafObj* pSdrGrafObj = new SdrGrafObj(pPage->getSdrModelFromSdrPage(), rGraphic, - tools::Rectangle(aPosition, aSizeHMM)); + tools::Rectangle(Point(), aSizeHMM)); pPage->InsertObject(pSdrGrafObj); + + for (auto const& rPDFAnnotation : rPDFGraphicResult.maAnnotations) + { + uno::Reference<office::XAnnotation> xAnnotation; + pPage->createAnnotation(xAnnotation); + + xAnnotation->setAuthor(rPDFAnnotation.maAuthor); + + uno::Reference<text::XText> xText(xAnnotation->getTextRange()); + xText->setString(rPDFAnnotation.maText); + // position is in mm not 100thmm + geometry::RealPoint2D aUnoPosition(rPDFAnnotation.maRectangle.getMinX() / 100.0, + rPDFAnnotation.maRectangle.getMinY() / 100.00); + geometry::RealSize2D aUnoSize(rPDFAnnotation.maRectangle.getWidth() / 100.0, + rPDFAnnotation.maRectangle.getHeight() / 100.00); + xAnnotation->setPosition(aUnoPosition); + xAnnotation->setSize(aUnoSize); + } } return true; diff --git a/vcl/source/filter/ipdf/pdfread.cxx b/vcl/source/filter/ipdf/pdfread.cxx index 1a5d98b7e499..d3eb243bcbd4 100644 --- a/vcl/source/filter/ipdf/pdfread.cxx +++ b/vcl/source/filter/ipdf/pdfread.cxx @@ -274,7 +274,36 @@ size_t ImportPDFUnloaded(const OUString& rURL, std::vector<PDFGraphicResult>& rG Graphic aGraphic(aVectorGraphicDataPtr); aGraphic.SetGfxLink(pGfxLink); - rGraphics.emplace_back(std::move(aGraphic), Size(nPageWidth, nPageHeight)); + auto pPage = pPdfDocument->openPage(nPageIndex); + + std::vector<PDFGraphicAnnotation> aPDFGraphicAnnotations; + for (int nAnnotation = 0; nAnnotation < pPage->getAnnotationCount(); nAnnotation++) + { + auto pAnnotation = pPage->getAnnotation(nAnnotation); + if (pAnnotation && pAnnotation->getSubType() == 1 /*FPDF_ANNOT_TEXT*/ + && pAnnotation->hasKey(vcl::pdf::constDictionaryKeyPopup)) + { + OUString sAuthor = pAnnotation->getString(vcl::pdf::constDictionaryKeyTitle); + OUString sText = pAnnotation->getString(vcl::pdf::constDictionaryKeyContents); + auto pPopupAnnotation = pAnnotation->getLinked(vcl::pdf::constDictionaryKeyPopup); + + basegfx::B2DRectangle rRectangle = pAnnotation->getRectangle(); + basegfx::B2DRectangle rRectangleHMM( + convertPointToMm100(rRectangle.getMinX()), + convertPointToMm100(aPageSize.getY() - rRectangle.getMinY()), + convertPointToMm100(rRectangle.getMaxX()), + convertPointToMm100(aPageSize.getY() - rRectangle.getMaxY())); + + PDFGraphicAnnotation aPDFGraphicAnnotation; + aPDFGraphicAnnotation.maRectangle = rRectangleHMM; + aPDFGraphicAnnotation.maAuthor = sAuthor; + aPDFGraphicAnnotation.maText = sText; + aPDFGraphicAnnotations.push_back(aPDFGraphicAnnotation); + } + } + + rGraphics.emplace_back(std::move(aGraphic), Size(nPageWidth, nPageHeight), + aPDFGraphicAnnotations); } return rGraphics.size(); commit 1709ebc984e41073ade18a4428be7387ab581c17 Author: Tomaž Vajngerl <tomaz.vajng...@collabora.co.uk> AuthorDate: Mon Jun 15 14:44:19 2020 +0200 Commit: Tomaž Vajngerl <qui...@gmail.com> CommitDate: Mon Jun 29 20:36:45 2020 +0200 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 <qui...@gmail.com> Reviewed-by: Tomaž Vajngerl <qui...@gmail.com> (cherry picked from commit 7e4dc3b1eabcb1993d4143c046a2f32fedc852ed) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/97438 Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com> 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 new file mode 100644 index 000000000000..f97003a7f397 Binary files /dev/null and b/vcl/qa/cppunit/data/PangramAcrobatAnnotations.pdf differ diff --git a/vcl/qa/cppunit/data/PangramWithAnnotations.pdf b/vcl/qa/cppunit/data/PangramWithAnnotations.pdf new file mode 100644 index 000000000000..f69ddd987060 Binary files /dev/null and b/vcl/qa/cppunit/data/PangramWithAnnotations.pdf differ diff --git a/vcl/source/pdf/PDFiumLibrary.cxx b/vcl/source/pdf/PDFiumLibrary.cxx index 3ea7e5b49c4a..344fac51aa76 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 @@ -109,6 +110,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 _______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits