include/vcl/pdfwriter.hxx                  |    4 +
 sd/inc/Annotation.hxx                      |    5 ++
 sd/source/core/annotations/Annotation.cxx  |   12 +++--
 sd/source/filter/pdf/sdpdffilter.cxx       |    5 ++
 sd/source/ui/annotations/annotationtag.cxx |   23 +++++++---
 sd/source/ui/unoidl/unomodel.cxx           |   17 ++++++-
 vcl/source/filter/ipdf/pdfread.cxx         |    1 
 vcl/source/gdi/pdfwriter_impl.cxx          |   64 +++++++++++++++++++++++++++--
 vcl/source/pdf/PDFiumLibrary.cxx           |   24 ++++++++--
 9 files changed, 133 insertions(+), 22 deletions(-)

New commits:
commit 13c3bc9094c56136d1b7d35ecf6fbfa4655448bc
Author:     Jaume Pujantell <[email protected]>
AuthorDate: Mon Jun 5 11:49:41 2023 +0200
Commit:     Andras Timar <[email protected]>
CommitDate: Thu Jun 8 21:53:14 2023 +0200

    pdfium: better suport for annotations and some fixes
    
    Added suport to import FreeText annotations. Added some suport to export
    graphical annotations. Fixed some color issues to be more inline with
    the pdfium library.
    
    Change-Id: I7371595ebb95594ee765ae532ca7c7d4f0499592
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/152549
    Tested-by: Jenkins CollaboraOffice <[email protected]>
    Reviewed-by: Andras Timar <[email protected]>

diff --git a/include/vcl/pdfwriter.hxx b/include/vcl/pdfwriter.hxx
index 6fe273054b4b..5f5ad9a851ec 100644
--- a/include/vcl/pdfwriter.hxx
+++ b/include/vcl/pdfwriter.hxx
@@ -66,6 +66,10 @@ struct PDFNote
     OUString          Title;          // optional title for the popup 
containing the note
     OUString          Contents;       // contents of the note
     css::util::DateTime maModificationDate;
+    bool isFreeText;
+    std::vector<basegfx::B2DPolygon> maPolygons;
+    Color annotColor;
+    Color interiorColor;
 };
 
 class VCL_DLLPUBLIC PDFOutputStream
diff --git a/sd/inc/Annotation.hxx b/sd/inc/Annotation.hxx
index 707f2cdc8e78..00870dc703e8 100644
--- a/sd/inc/Annotation.hxx
+++ b/sd/inc/Annotation.hxx
@@ -133,6 +133,10 @@ public:
         return bool(m_pCustomAnnotationMarker);
     }
 
+    void setIsFreeText(bool value) { m_bIsFreeText = value; }
+
+    bool isFreeText() const { return m_bIsFreeText; }
+
 private:
     // destructor is private and will be called indirectly by the release call 
   virtual ~Annotation() {}
 
@@ -152,6 +156,7 @@ private:
     rtl::Reference<TextApiObject> m_TextRange;
 
     std::unique_ptr<CustomAnnotationMarker> m_pCustomAnnotationMarker;
+    bool m_bIsFreeText;
 };
 
 }
diff --git a/sd/source/core/annotations/Annotation.cxx 
b/sd/source/core/annotations/Annotation.cxx
index 991412f063d5..3656c54f241a 100644
--- a/sd/source/core/annotations/Annotation.cxx
+++ b/sd/source/core/annotations/Annotation.cxx
@@ -117,11 +117,13 @@ void 
createAnnotation(uno::Reference<office::XAnnotation>& xAnnotation, SdPage*
 
 sal_uInt32 Annotation::m_nLastId = 1;
 
-Annotation::Annotation( const uno::Reference<uno::XComponentContext>& context, 
SdPage* pPage )
-: ::cppu::WeakComponentImplHelper<office::XAnnotation>(m_aMutex)
-, ::cppu::PropertySetMixin<office::XAnnotation>(context, 
IMPLEMENTS_PROPERTY_SET, uno::Sequence<OUString>())
-, m_nId( m_nLastId++ )
-, mpPage( pPage )
+Annotation::Annotation(const uno::Reference<uno::XComponentContext>& context, 
SdPage* pPage)
+    : ::cppu::WeakComponentImplHelper<office::XAnnotation>(m_aMutex)
+    , ::cppu::PropertySetMixin<office::XAnnotation>(context, 
IMPLEMENTS_PROPERTY_SET,
+                                                    uno::Sequence<OUString>())
+    , m_nId(m_nLastId++)
+    , mpPage(pPage)
+    , m_bIsFreeText(false)
 {
 }
 
diff --git a/sd/source/filter/pdf/sdpdffilter.cxx 
b/sd/source/filter/pdf/sdpdffilter.cxx
index 39c6ada55f4e..35b1bffbcb73 100644
--- a/sd/source/filter/pdf/sdpdffilter.cxx
+++ b/sd/source/filter/pdf/sdpdffilter.cxx
@@ -189,6 +189,11 @@ bool SdPdfFilter::Import()
                     rCustomAnnotationMarker.maFillColor = COL_TRANSPARENT;
                 }
             }
+            else if (rPDFAnnotation.meSubType == 
vcl::pdf::PDFAnnotationSubType::FreeText)
+            {
+                auto* pAnnotation = 
static_cast<sd::Annotation*>(xAnnotation.get());
+                pAnnotation->setIsFreeText(true);
+            }
         }
     }
     mrDocument.setLock(bWasLocked);
diff --git a/sd/source/ui/annotations/annotationtag.cxx 
b/sd/source/ui/annotations/annotationtag.cxx
index 7afe26ee54b0..dbadf4cb6fe1 100644
--- a/sd/source/ui/annotations/annotationtag.cxx
+++ b/sd/source/ui/annotations/annotationtag.cxx
@@ -524,18 +524,29 @@ BitmapEx AnnotationTag::CreateAnnotationBitmap( bool 
bSelected )
 {
     ScopedVclPtrInstance< VirtualDevice > pVDev;
 
-    OUString sInitials(mxAnnotation->getInitials());
-    if (sInitials.isEmpty())
-        sInitials = getInitials(mxAnnotation->getAuthor());
+    OUString sText;
+    auto* pAnnotation = dynamic_cast<sd::Annotation*>(mxAnnotation.get());
+    if (pAnnotation && pAnnotation->isFreeText())
+    {
+        sText = mxAnnotation->getTextRange()->getString();
+    }
+    else
+    {
+        OUString sInitials(mxAnnotation->getInitials());
+        if (sInitials.isEmpty())
+        {
+            sInitials = getInitials(mxAnnotation->getAuthor());
+        }
 
-    OUString sAuthor(sInitials + " " + OUString::number(mnIndex));
+        sText = sInitials + " " + OUString::number(mnIndex);
+    }
 
     pVDev->SetFont( mrFont );
 
     const int BORDER_X = 4; // pixels
     const int BORDER_Y = 4; // pixels
 
-    maSize = Size( pVDev->GetTextWidth( sAuthor ) + 2*BORDER_X, 
pVDev->GetTextHeight() + 2*BORDER_Y );
+    maSize = Size(pVDev->GetTextWidth(sText) + 2 * BORDER_X, 
pVDev->GetTextHeight() + 2 * BORDER_Y);
     pVDev->SetOutputSizePixel( maSize, false );
 
     Color aBorderColor( maColor );
@@ -563,7 +574,7 @@ BitmapEx AnnotationTag::CreateAnnotationBitmap( bool 
bSelected )
     pVDev->DrawRect( aBorderRect );
 
     pVDev->SetTextColor( maColor.IsDark() ? COL_WHITE : COL_BLACK );
-    pVDev->DrawText( Point( BORDER_X, BORDER_Y ), sAuthor );
+    pVDev->DrawText(Point(BORDER_X, BORDER_Y), sText);
 
     return pVDev->GetBitmapEx( aPos, maSize );
 }
diff --git a/sd/source/ui/unoidl/unomodel.cxx b/sd/source/ui/unoidl/unomodel.cxx
index f2bceac96fd4..5a88fe21c8f8 100644
--- a/sd/source/ui/unoidl/unomodel.cxx
+++ b/sd/source/ui/unoidl/unomodel.cxx
@@ -1605,15 +1605,28 @@ static void ImplPDFExportComments( const 
uno::Reference< drawing::XDrawPage >& x
             uno::Reference< office::XAnnotation > xAnnotation( 
xAnnotationEnumeration->nextElement() );
 
             geometry::RealPoint2D aRealPoint2D( xAnnotation->getPosition() );
+            geometry::RealSize2D aRealSize2D(xAnnotation->getSize());
             uno::Reference< text::XText > xText( xAnnotation->getTextRange() );
 
             vcl::PDFNote aNote;
             aNote.Title = xAnnotation->getAuthor();
             aNote.Contents = xText->getString();
             aNote.maModificationDate = xAnnotation->getDateTime();
+            auto* pAnnotation = 
dynamic_cast<sd::Annotation*>(xAnnotation.get());
+            aNote.isFreeText = pAnnotation && pAnnotation->isFreeText();
+            if (pAnnotation && pAnnotation->hasCustomAnnotationMarker())
+            {
+                aNote.maPolygons = 
pAnnotation->getCustomAnnotationMarker().maPolygons;
+                aNote.annotColor = 
pAnnotation->getCustomAnnotationMarker().maLineColor;
+                aNote.interiorColor = 
pAnnotation->getCustomAnnotationMarker().maFillColor;
+            }
 
-            rPDFExtOutDevData.CreateNote( ::tools::Rectangle( Point( 
static_cast< ::tools::Long >( aRealPoint2D.X * 100 ),
-                static_cast< ::tools::Long >( aRealPoint2D.Y * 100 ) ), Size( 
1000, 1000 ) ), aNote );
+            rPDFExtOutDevData.CreateNote(
+                
::tools::Rectangle(Point(static_cast<::tools::Long>(aRealPoint2D.X * 100),
+                                         
static_cast<::tools::Long>(aRealPoint2D.Y * 100)),
+                                   
Size(static_cast<::tools::Long>(aRealSize2D.Width * 100),
+                                        
static_cast<::tools::Long>(aRealSize2D.Height * 100))),
+                aNote);
         }
     }
     catch (const uno::Exception&)
diff --git a/vcl/source/filter/ipdf/pdfread.cxx 
b/vcl/source/filter/ipdf/pdfread.cxx
index deb341157137..e3d70f7c0ade 100644
--- a/vcl/source/filter/ipdf/pdfread.cxx
+++ b/vcl/source/filter/ipdf/pdfread.cxx
@@ -176,6 +176,7 @@ findAnnotations(const 
std::unique_ptr<vcl::pdf::PDFiumPage>& pPage, basegfx::B2D
             auto eSubtype = pAnnotation->getSubType();
 
             if (eSubtype == vcl::pdf::PDFAnnotationSubType::Text
+                || eSubtype == vcl::pdf::PDFAnnotationSubType::FreeText
                 || eSubtype == vcl::pdf::PDFAnnotationSubType::Polygon
                 || eSubtype == vcl::pdf::PDFAnnotationSubType::Circle
                 || eSubtype == vcl::pdf::PDFAnnotationSubType::Square
diff --git a/vcl/source/gdi/pdfwriter_impl.cxx 
b/vcl/source/gdi/pdfwriter_impl.cxx
index 41cafe805249..ba76d642a67c 100644
--- a/vcl/source/gdi/pdfwriter_impl.cxx
+++ b/vcl/source/gdi/pdfwriter_impl.cxx
@@ -58,6 +58,7 @@
 #include <tools/stream.hxx>
 #include <tools/helpers.hxx>
 #include <tools/urlobj.hxx>
+#include <tools/UnitConversion.hxx>
 #include <tools/zcodec.hxx>
 #include <svl/cryptosign.hxx>
 #include <vcl/bitmapex.hxx>
@@ -3903,10 +3904,64 @@ void 
PDFWriterImpl::emitTextAnnotationLine(OStringBuffer & aLine, PDFNoteEntry c
 {
     appendObjectID(rNote.m_nObject, aLine);
 
-    aLine.append("<</Type /Annot /Subtype /Text ");
+    aLine.append("<</Type /Annot /Subtype ");
+    if (rNote.m_aContents.maPolygons.size() == 1)
+    {
+        auto const& rPolygon = rNote.m_aContents.maPolygons[0];
+        aLine.append(rPolygon.isClosed() ? "/Polygon " : "/Polyline ");
+        aLine.append("/Vertices [");
+        for (sal_uInt32 i = 0; i < rPolygon.count(); ++i)
+        {
+            appendDouble(convertMm100ToPoint(rPolygon.getB2DPoint(i).getX()), 
aLine, nLog10Divisor);
+            aLine.append(" ");
+            appendDouble(m_aPages[rNote.m_nPage].getHeight()
+                             - 
convertMm100ToPoint(rPolygon.getB2DPoint(i).getY()),
+                         aLine, nLog10Divisor);
+            aLine.append(" ");
+        }
+        aLine.append("] ");
+        aLine.append("/C [");
+        appendColor(rNote.m_aContents.annotColor, aLine, false);
+        aLine.append("] ");
+        if (rPolygon.isClosed())
+        {
+            aLine.append("/IC [");
+            appendColor(rNote.m_aContents.interiorColor, aLine, false);
+            aLine.append("] ");
+        }
+    }
+    else if (rNote.m_aContents.maPolygons.size() > 1)
+    {
+        aLine.append("/Ink /InkList [");
+        for (auto const& rPolygon : rNote.m_aContents.maPolygons)
+        {
+            aLine.append("[");
+            for (sal_uInt32 i = 0; i < rPolygon.count(); ++i)
+            {
+                
appendDouble(convertMm100ToPoint(rPolygon.getB2DPoint(i).getX()), aLine,
+                             nLog10Divisor);
+                aLine.append(" ");
+                appendDouble(m_aPages[rNote.m_nPage].getHeight()
+                                 - 
convertMm100ToPoint(rPolygon.getB2DPoint(i).getY()),
+                             aLine, nLog10Divisor);
+                aLine.append(" ");
+            }
+            aLine.append("]");
+            aLine.append("/C [");
+            appendColor(rNote.m_aContents.annotColor, aLine, false);
+            aLine.append("] ");
+        }
+        aLine.append("] ");
+    }
+    else if (rNote.m_aContents.isFreeText)
+        aLine.append("/FreeText ");
+    else
+        aLine.append("/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
+    aLine.append("/BS<</W 0>>");
+
+    // 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 ");
 
@@ -9236,6 +9291,8 @@ void PDFWriterImpl::writeReferenceXObject(const 
ReferenceXObjectEmit& rEmit)
         aStream.append(" re\n");
         aStream.append("f*\n");
 
+        // Reset non-stroking color in case the XObject uses the default
+        aStream.append("0 0 0 rg\n");
         // No reference XObject, draw the form XObject containing the original
         // page streams.
         aStream.append("/Im");
@@ -10315,6 +10372,7 @@ void PDFWriterImpl::createNote( const tools::Rectangle& 
rRect, const PDFNote& rN
     rNoteEntry.m_aPopUpAnnotation.m_nParentObject = rNoteEntry.m_nObject;
     rNoteEntry.m_aContents = rNote;
     rNoteEntry.m_aRect = rRect;
+    rNoteEntry.m_nPage = nPageNr;
     // convert to default user space now, since the mapmode may change
     m_aPages[nPageNr].convertRect(rNoteEntry.m_aRect);
 
diff --git a/vcl/source/pdf/PDFiumLibrary.cxx b/vcl/source/pdf/PDFiumLibrary.cxx
index e0562c4dbf9b..c445e5e5ad98 100644
--- a/vcl/source/pdf/PDFiumLibrary.cxx
+++ b/vcl/source/pdf/PDFiumLibrary.cxx
@@ -1126,24 +1126,36 @@ basegfx::B2DRectangle 
PDFiumAnnotationImpl::getRectangle()
 
 Color PDFiumAnnotationImpl::getColor()
 {
-    Color aColor = COL_TRANSPARENT;
     unsigned int nR, nG, nB, nA;
     if (FPDFAnnot_GetColor(mpAnnotation, FPDFANNOT_COLORTYPE_Color, &nR, &nG, 
&nB, &nA))
     {
-        aColor = Color(ColorAlpha, nA, nR, nG, nB);
+        return Color(ColorAlpha, nA, nR, nG, nB);
     }
-    return aColor;
+    // FPDFAnnot_GetColor can return false if there is an appearance stream
+    // So we search for a color with getStrokeColor
+    for (int i = 0; i < getObjectCount(); ++i)
+    {
+        if (getObject(i)->getType() == PDFPageObjectType::Path)
+            return getObject(i)->getStrokeColor();
+    }
+    return COL_TRANSPARENT;
 }
 
 Color PDFiumAnnotationImpl::getInteriorColor()
 {
-    Color aColor = COL_TRANSPARENT;
     unsigned int nR, nG, nB, nA;
     if (FPDFAnnot_GetColor(mpAnnotation, FPDFANNOT_COLORTYPE_InteriorColor, 
&nR, &nG, &nB, &nA))
     {
-        aColor = Color(ColorAlpha, nA, nR, nG, nB);
+        return Color(ColorAlpha, nA, nR, nG, nB);
     }
-    return aColor;
+    // FPDFAnnot_GetColor can return false if there is an appearance stream
+    // So we search for a color with getFillColor
+    for (int i = 0; i < getObjectCount(); ++i)
+    {
+        if (getObject(i)->getType() == PDFPageObjectType::Path)
+            return getObject(i)->getFillColor();
+    }
+    return COL_TRANSPARENT;
 }
 
 size_t PDFiumAnnotationImpl::getAttachmentPointsCount()

Reply via email to