[Libreoffice-commits] core.git: sd/qa vcl/source

Tomaž Vajngerl (via logerrit) logerrit at kemper.freedesktop.org
Mon Jun 22 06:14:12 UTC 2020


 sd/qa/unit/SdrPdfImportTest.cxx   |    9 +-
 vcl/source/gdi/pdfwriter_impl.cxx |  128 ++++++++++++++++++++++++++++----------
 vcl/source/gdi/pdfwriter_impl.hxx |   14 +++-
 3 files changed, 113 insertions(+), 38 deletions(-)

New commits:
commit 51d529c18dadb05754590a01ce4c1f06f41cf412
Author:     Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
AuthorDate: Sat Jun 20 08:43:58 2020 +0200
Commit:     Tomaž Vajngerl <quikee at gmail.com>
CommitDate: Mon Jun 22 08:13:31 2020 +0200

    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.
    
    Change-Id: I95bc12939f490effe7759dad54e136c44e44a2f8
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/96765
    Tested-by: Tomaž Vajngerl <quikee at gmail.com>
    Reviewed-by: Tomaž Vajngerl <quikee at gmail.com>

diff --git a/sd/qa/unit/SdrPdfImportTest.cxx b/sd/qa/unit/SdrPdfImportTest.cxx
index 4bbb24de4035..e8dd60b0a806 100644
--- a/sd/qa/unit/SdrPdfImportTest.cxx
+++ b/sd/qa/unit/SdrPdfImportTest.cxx
@@ -231,14 +231,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());
@@ -249,7 +248,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 04fcb70a152f..c7e56544f58a 100644
--- a/vcl/source/gdi/pdfwriter_impl.cxx
+++ b/vcl/source/gdi/pdfwriter_impl.cxx
@@ -3418,50 +3418,111 @@ 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 || m_bIsPDF_A3)
+        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 || m_bIsPDF_A3 )
-            aLine.append( "/F 4" );
-        aLine.append( "/Subtype/Text/Rect[" );
+        {
+            if (!updateObject(rNote.m_nObject))
+                return false;
 
-        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( "]" );
+            OStringBuffer aLine(1024);
 
-        // contents of the note (type text string)
-        aLine.append( "/Contents\n" );
-        appendUnicodeTextStringEncrypt( rNote.m_aContents.Contents, rNote.m_nObject, aLine );
-        aLine.append( "\n" );
+            emitTextAnnotationLine(aLine, rNote);
 
-        // optional title
-        if( !rNote.m_aContents.Title.isEmpty() )
-        {
-            aLine.append( "/T" );
-            appendUnicodeTextStringEncrypt( rNote.m_aContents.Title, rNote.m_nObject, aLine );
-            aLine.append( "\n" );
+            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;
 }
@@ -9658,6 +9719,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
@@ -9665,6 +9728,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 eb53db7d5877..b5d86a2d2486 100644
--- a/vcl/source/gdi/pdfwriter_impl.hxx
+++ b/vcl/source/gdi/pdfwriter_impl.hxx
@@ -403,10 +403,18 @@ struct PDFEmbeddedFile
     }
 };
 
+struct PDFPopupAnnotation : public PDFAnnotation
+{
+    /// ID of the parent object.
+    sal_Int32 m_nParentObject;
+};
+
 struct PDFNoteEntry : public PDFAnnotation
 {
     PDFNote m_aContents;
 
+    PDFPopupAnnotation m_aPopUpAnnotation;
+
     PDFNoteEntry()
     {}
 };
@@ -873,10 +881,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


More information about the Libreoffice-commits mailing list