diff options
-rw-r--r-- | sd/qa/unit/SdrPdfImportTest.cxx | 9 | ||||
-rw-r--r-- | vcl/source/gdi/pdfwriter_impl.cxx | 128 | ||||
-rw-r--r-- | vcl/source/gdi/pdfwriter_impl.hxx | 14 |
3 files changed, 113 insertions, 38 deletions
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 |