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 d01c4ebd00a7ec0249f34ddddb26715802036db4 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:44 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/+/152548 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 4236d2f3ea21..a03cee4dc362 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 002c1c5db4e6..b54703c3f0e7 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 cfd632dcc2bd..a44acdf74a6c 100644 --- a/sd/source/ui/annotations/annotationtag.cxx +++ b/sd/source/ui/annotations/annotationtag.cxx @@ -523,18 +523,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 ); @@ -562,7 +573,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 eac9ad74385b..6247d535dabd 100644 --- a/sd/source/ui/unoidl/unomodel.cxx +++ b/sd/source/ui/unoidl/unomodel.cxx @@ -1576,15 +1576,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 55c6fec5960b..d3dc6fc5a7d0 100644 --- a/vcl/source/filter/ipdf/pdfread.cxx +++ b/vcl/source/filter/ipdf/pdfread.cxx @@ -172,6 +172,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 8002d382ba28..b23a05b9c31a 100644 --- a/vcl/source/gdi/pdfwriter_impl.cxx +++ b/vcl/source/gdi/pdfwriter_impl.cxx @@ -57,6 +57,7 @@ #include <tools/helpers.hxx> #include <tools/stream.hxx> #include <tools/urlobj.hxx> +#include <tools/UnitConversion.hxx> #include <tools/zcodec.hxx> #include <svl/cryptosign.hxx> #include <vcl/bitmapex.hxx> @@ -3477,10 +3478,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 "); @@ -8741,6 +8796,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"); @@ -9815,6 +9872,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 e738b6697aaf..c5219ec9f7bc 100644 --- a/vcl/source/pdf/PDFiumLibrary.cxx +++ b/vcl/source/pdf/PDFiumLibrary.cxx @@ -1102,24 +1102,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()
