vcl/qa/cppunit/pdfexport/data/pdf-image-hyperlink.odg |binary vcl/qa/cppunit/pdfexport/pdfexport.cxx | 30 +++++++++++++++ vcl/source/gdi/pdfwriter_impl.cxx | 35 ++++++++++++++++++ 3 files changed, 65 insertions(+)
New commits: commit 58e6fce28f7d3e1c1504f048be5766dc732af4e7 Author: Miklos Vajna <[email protected]> AuthorDate: Fri Jan 7 12:23:34 2022 +0100 Commit: Tomaž Vajngerl <[email protected]> CommitDate: Mon Jan 10 14:10:14 2022 +0100 PDF export of PDF images: preserve hyperlinks The input file has a single page, with a full-page PDF image, which contains a hyperlink. Similar to pdfcrop, we used to wrap this into a form XObject, so page-level annotations like hyperlinks were lost. Explicitly merge page-level annotations from the source page to the page that contains the PDF image to preserve those annotations. (cherry picked from commit 1984a5c140cc3c7c291047dacf3bddd7061d2047) Conflicts: vcl/qa/cppunit/pdfexport/pdfexport.cxx Change-Id: I96e8bc9d33440b91f3514486d6a8bd75047546b2 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/128127 Tested-by: Jenkins CollaboraOffice <[email protected]> Reviewed-by: Tomaž Vajngerl <[email protected]> diff --git a/vcl/qa/cppunit/pdfexport/data/pdf-image-hyperlink.odg b/vcl/qa/cppunit/pdfexport/data/pdf-image-hyperlink.odg new file mode 100644 index 000000000000..aa0f89300b2c Binary files /dev/null and b/vcl/qa/cppunit/pdfexport/data/pdf-image-hyperlink.odg differ diff --git a/vcl/qa/cppunit/pdfexport/pdfexport.cxx b/vcl/qa/cppunit/pdfexport/pdfexport.cxx index 3a5b11784f27..ba1468af5253 100644 --- a/vcl/qa/cppunit/pdfexport/pdfexport.cxx +++ b/vcl/qa/cppunit/pdfexport/pdfexport.cxx @@ -2552,6 +2552,36 @@ CPPUNIT_TEST_FIXTURE(PdfExportTest, testPdfImageRotate180) // flip). CPPUNIT_ASSERT_DOUBLES_EQUAL(-1.0, aScale.getX(), 0.01); } + +CPPUNIT_TEST_FIXTURE(PdfExportTest, testPdfImageHyperlink) +{ + // Given a Draw file, containing a PDF image, which has a hyperlink in it: + OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "pdf-image-hyperlink.odg"; + mxComponent = loadFromDesktop(aURL); + CPPUNIT_ASSERT(mxComponent.is()); + + // When saving to PDF: + uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY); + utl::MediaDescriptor aMediaDescriptor; + aMediaDescriptor["FilterName"] <<= OUString("draw_pdf_Export"); + xStorable->storeToURL(maTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList()); + + // Then make sure that link is preserved: + SvFileStream aFile(maTempFile.GetURL(), StreamMode::READ); + maMemory.WriteStream(aFile); + std::shared_ptr<vcl::pdf::PDFium> pPDFium = vcl::pdf::PDFiumLibrary::get(); + std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument + = pPDFium->openDocument(maMemory.GetData(), maMemory.GetSize()); + CPPUNIT_ASSERT(pPdfDocument); + CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount()); + std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/0); + CPPUNIT_ASSERT(pPdfPage); + int nStartPos = 0; + FPDF_LINK pLinkAnnot = nullptr; + // Without the accompanying fix in place, this test would have failed, the hyperlink of the PDF + // image was lost. + CPPUNIT_ASSERT(FPDFLink_Enumerate(pPdfPage->getPointer(), &nStartPos, &pLinkAnnot)); +} } CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/vcl/source/gdi/pdfwriter_impl.cxx b/vcl/source/gdi/pdfwriter_impl.cxx index 811fdeb14ce3..63f466a962d1 100644 --- a/vcl/source/gdi/pdfwriter_impl.cxx +++ b/vcl/source/gdi/pdfwriter_impl.cxx @@ -8511,6 +8511,41 @@ void PDFWriterImpl::writeReferenceXObject(ReferenceXObjectEmit& rEmit) return; } + // Merge page annotations (links, etc) from pPage to our page. + std::vector<filter::PDFObjectElement*> aAnnots; + if (auto pArray = dynamic_cast<filter::PDFArrayElement*>(pPage->Lookup("Annots"))) + { + for (const auto pElement : pArray->GetElements()) + { + auto pReference = dynamic_cast<filter::PDFReferenceElement*>(pElement); + if (!pReference) + { + continue; + } + + filter::PDFObjectElement* pObject = pReference->LookupObject(); + if (!pObject) + { + continue; + } + + // Annotation refers to an object, remember it. + aAnnots.push_back(pObject); + } + } + if (!aAnnots.empty()) + { + PDFObjectCopier aCopier(*this); + SvMemoryStream& rDocBuffer = pPage->GetDocument().GetEditBuffer(); + std::map<sal_Int32, sal_Int32> aMap; + for (const auto& pAnnot : aAnnots) + { + // Copy over the annotation and refer to its new id. + sal_Int32 nNewId = aCopier.copyExternalResource(rDocBuffer, *pAnnot, aMap); + m_aPages.back().m_aAnnotations.push_back(nNewId); + } + } + nWrappedFormObject = createObject(); // Write the form XObject wrapped below. This is a separate object from // the wrapper, this way there is no need to alter the stream contents.
