diff options
author | Tomaž Vajngerl <tomaz.vajngerl@collabora.co.uk> | 2020-06-20 08:43:58 +0200 |
---|---|---|
committer | Tomaž Vajngerl <quikee@gmail.com> | 2020-06-29 21:53:18 +0200 |
commit | 601eeb015243937727926096f69fd53e3b410160 (patch) | |
tree | 8cfc478b3dbc4d7838a623278131c1a5e3a3d38a | |
parent | 2eb4b0e203fdf28f9151b7dc36213c2b560b4d7a (diff) |
pdf export: support pop-up annotation for notes / comments
This changes pdf export for annotation, so the comments are
exported as "text" annotation with an child "pop-up" annotations.
This seems to be more common what PDF readers do (even when text
annotation only should be enough).
Also changes the test, so that it now works as expected.
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/96765
Tested-by: Tomaž Vajngerl <quikee@gmail.com>
Reviewed-by: Tomaž Vajngerl <quikee@gmail.com>
(cherry picked from commit 51d529c18dadb05754590a01ce4c1f06f41cf412)
Change-Id: I95bc12939f490effe7759dad54e136c44e44a2f8
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/97443
Tested-by: Tomaž Vajngerl <quikee@gmail.com>
Reviewed-by: Tomaž Vajngerl <quikee@gmail.com>
-rw-r--r-- | sd/qa/unit/SdrPdfImportTest.cxx | 9 | ||||
-rw-r--r-- | vcl/source/gdi/pdfwriter_impl.cxx | 129 | ||||
-rw-r--r-- | vcl/source/gdi/pdfwriter_impl.hxx | 16 |
3 files changed, 115 insertions, 39 deletions
diff --git a/sd/qa/unit/SdrPdfImportTest.cxx b/sd/qa/unit/SdrPdfImportTest.cxx index 16b31774a784..ca022d585c48 100644 --- a/sd/qa/unit/SdrPdfImportTest.cxx +++ b/sd/qa/unit/SdrPdfImportTest.cxx @@ -232,14 +232,13 @@ CPPUNIT_TEST_FIXTURE(SdrPdfImportTest, testAnnotationsImportExport) auto pPDFPage = pPDFDocument->openPage(0); CPPUNIT_ASSERT(pPDFPage); - // TODO: Should be 2 really - CPPUNIT_ASSERT_EQUAL(1, pPDFPage->getAnnotationCount()); + CPPUNIT_ASSERT_EQUAL(2, pPDFPage->getAnnotationCount()); auto pPDFAnnotation1 = pPDFPage->getAnnotation(0); CPPUNIT_ASSERT_EQUAL(1, pPDFAnnotation1->getSubType()); // Text annotation - //auto pPDFAnnotation2 = pPDFPage->getAnnotation(1); - //CPPUNIT_ASSERT_EQUAL(16, pPDFAnnotation2->getSubType()); // Pop-up annotation + auto pPDFAnnotation2 = pPDFPage->getAnnotation(1); + CPPUNIT_ASSERT_EQUAL(16, pPDFAnnotation2->getSubType()); // Pop-up annotation // Load document again mxComponent = loadFromDesktop(aTempFile.GetURL()); @@ -250,7 +249,7 @@ CPPUNIT_TEST_FIXTURE(SdrPdfImportTest, testAnnotationsImportExport) SdPage* pPage = pNewViewShell->GetActualPage(); CPPUNIT_ASSERT(pPage); - //CPPUNIT_ASSERT_EQUAL(false, pPage->getAnnotations().empty()); + CPPUNIT_ASSERT(!pPage->getAnnotations().empty()); } #endif // HAVE_FEATURE_PDFIUM diff --git a/vcl/source/gdi/pdfwriter_impl.cxx b/vcl/source/gdi/pdfwriter_impl.cxx index 8e38a85dfa71..2ef065f61afd 100644 --- a/vcl/source/gdi/pdfwriter_impl.cxx +++ b/vcl/source/gdi/pdfwriter_impl.cxx @@ -3567,50 +3567,112 @@ we check in the following sequence: return true; } +namespace +{ + +void appendAnnotationRect(tools::Rectangle const & rRectangle, OStringBuffer & aLine) +{ + aLine.append("/Rect["); + appendFixedInt(rRectangle.Left(), aLine); + aLine.append(' '); + appendFixedInt(rRectangle.Top(), aLine); + aLine.append(' '); + appendFixedInt(rRectangle.Right(), aLine); + aLine.append(' '); + appendFixedInt(rRectangle.Bottom(), aLine); + aLine.append("] "); +} + +void appendObjectID(sal_Int32 nObjectID, OStringBuffer & aLine) +{ + aLine.append(nObjectID); + aLine.append(" 0 obj\n"); +} + +void appendObjectReference(sal_Int32 nObjectID, OStringBuffer & aLine) +{ + aLine.append(nObjectID); + aLine.append(" 0 R "); +} + +} // end anonymous namespace + +void PDFWriterImpl::emitTextAnnotationLine(OStringBuffer & aLine, PDFNoteEntry const & rNote) +{ + appendObjectID(rNote.m_nObject, aLine); + + aLine.append("<</Type /Annot /Subtype /Text "); + +// i59651: key /F set bits Print to 1 rest to 0. We don't set NoZoom NoRotate to 1, since it's a 'should' +// see PDF 8.4.2 and ISO 19005-1:2005 6.5.3 + if (m_bIsPDF_A1 || m_bIsPDF_A2) + aLine.append("/F 4 "); + + appendAnnotationRect(rNote.m_aRect, aLine); + + aLine.append("/Popup "); + appendObjectReference(rNote.m_aPopUpAnnotation.m_nObject, aLine); + + // contents of the note (type text string) + aLine.append("/Contents "); + appendUnicodeTextStringEncrypt(rNote.m_aContents.Contents, rNote.m_nObject, aLine); + aLine.append("\n"); + + // optional title + if (!rNote.m_aContents.Title.isEmpty()) + { + aLine.append("/T "); + appendUnicodeTextStringEncrypt(rNote.m_aContents.Title, rNote.m_nObject, aLine); + aLine.append("\n"); + } + aLine.append(">>\n"); + aLine.append("endobj\n\n"); +} + +void PDFWriterImpl::emitPopupAnnotationLine(OStringBuffer & aLine, PDFPopupAnnotation const & rPopUp) +{ + appendObjectID(rPopUp.m_nObject, aLine); + aLine.append("<</Type /Annot /Subtype /Popup "); + aLine.append("/Parent "); + appendObjectReference(rPopUp.m_nParentObject, aLine); + aLine.append(">>\n"); + aLine.append("endobj\n\n"); +} + bool PDFWriterImpl::emitNoteAnnotations() { // emit note annotations int nAnnots = m_aNotes.size(); for( int i = 0; i < nAnnots; i++ ) { - const PDFNoteEntry& rNote = m_aNotes[i]; - if( ! updateObject( rNote.m_nObject ) ) - return false; + const PDFNoteEntry& rNote = m_aNotes[i]; + const PDFPopupAnnotation& rPopUp = rNote.m_aPopUpAnnotation; - OStringBuffer aLine( 1024 ); - aLine.append( rNote.m_nObject ); - aLine.append( " 0 obj\n" ); -// i59651: key /F set bits Print to 1 rest to 0. We don't set NoZoom NoRotate to 1, since it's a 'should' -// see PDF 8.4.2 and ISO 19005-1:2005 6.5.3 - aLine.append( "<</Type/Annot" ); - if( m_bIsPDF_A1 || m_bIsPDF_A2 ) - aLine.append( "/F 4" ); - aLine.append( "/Subtype/Text/Rect[" ); - appendFixedInt( rNote.m_aRect.Left(), aLine ); - aLine.append( ' ' ); - appendFixedInt( rNote.m_aRect.Top(), aLine ); - aLine.append( ' ' ); - appendFixedInt( rNote.m_aRect.Right(), aLine ); - aLine.append( ' ' ); - appendFixedInt( rNote.m_aRect.Bottom(), aLine ); - aLine.append( "]" ); + { + if (!updateObject(rNote.m_nObject)) + return false; - // contents of the note (type text string) - aLine.append( "/Contents\n" ); - appendUnicodeTextStringEncrypt( rNote.m_aContents.Contents, rNote.m_nObject, aLine ); - aLine.append( "\n" ); + OStringBuffer aLine(1024); - // optional title - if( !rNote.m_aContents.Title.isEmpty() ) - { - aLine.append( "/T" ); - appendUnicodeTextStringEncrypt( rNote.m_aContents.Title, rNote.m_nObject, aLine ); - aLine.append( "\n" ); + emitTextAnnotationLine(aLine, rNote); + + if (!writeBuffer(aLine.getStr(), aLine.getLength())) + return false; } - aLine.append( ">>\nendobj\n\n" ); - CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); + { + + if (!updateObject(rPopUp.m_nObject)) + return false; + + OStringBuffer aLine(1024); + + emitPopupAnnotationLine(aLine, rPopUp); + + if (!writeBuffer(aLine.getStr(), aLine.getLength())) + return false; + } } return true; } @@ -9888,6 +9950,8 @@ void PDFWriterImpl::createNote( const tools::Rectangle& rRect, const PDFNote& rN m_aNotes.emplace_back(); auto & rNoteEntry = m_aNotes.back(); rNoteEntry.m_nObject = createObject(); + rNoteEntry.m_aPopUpAnnotation.m_nObject = createObject(); + rNoteEntry.m_aPopUpAnnotation.m_nParentObject = rNoteEntry.m_nObject; rNoteEntry.m_aContents = rNote; rNoteEntry.m_aRect = rRect; // convert to default user space now, since the mapmode may change @@ -9895,6 +9959,7 @@ void PDFWriterImpl::createNote( const tools::Rectangle& rRect, const PDFNote& rN // insert note to page's annotation list m_aPages[nPageNr].m_aAnnotations.push_back(rNoteEntry.m_nObject); + m_aPages[nPageNr].m_aAnnotations.push_back(rNoteEntry.m_aPopUpAnnotation.m_nObject); } sal_Int32 PDFWriterImpl::createLink( const tools::Rectangle& rRect, sal_Int32 nPageNr ) diff --git a/vcl/source/gdi/pdfwriter_impl.hxx b/vcl/source/gdi/pdfwriter_impl.hxx index 43774333a182..79df86f9b679 100644 --- a/vcl/source/gdi/pdfwriter_impl.hxx +++ b/vcl/source/gdi/pdfwriter_impl.hxx @@ -438,9 +438,17 @@ public: } }; + struct PDFPopupAnnotation : public PDFAnnotation + { + /// ID of the parent object. + sal_Int32 m_nParentObject; + }; + struct PDFNoteEntry : public PDFAnnotation { - PDFNote m_aContents; + PDFNote m_aContents; + + PDFPopupAnnotation m_aPopUpAnnotation; PDFNoteEntry() {} @@ -878,10 +886,14 @@ i12626 bool appendDest( sal_Int32 nDestID, OStringBuffer& rBuffer ); // write all links bool emitLinkAnnotations(); - /// Write all screen annotations. + // Write all screen annotations. bool emitScreenAnnotations(); + + void emitTextAnnotationLine(OStringBuffer & aLine, PDFNoteEntry const & rNote); + static void emitPopupAnnotationLine(OStringBuffer & aLine, PDFPopupAnnotation const & rPopUp); // write all notes bool emitNoteAnnotations(); + // write the appearance streams of a widget bool emitAppearances( PDFWidget& rWidget, OStringBuffer& rAnnotDict ); // clean up radio button "On" values |