1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
22 #include <config_features.h>
24 #if HAVE_FEATURE_PDFIUM
26 #include <fpdf_edit.h>
27 #include <fpdf_text.h>
29 #include <tools/UnitConversion.hxx>
30 #include <vcl/graph.hxx>
31 #include <vcl/vectorgraphicdata.hxx>
34 #include <editeng/eeitem.hxx>
35 #include <editeng/fhgtitem.hxx>
36 #include <editeng/wghtitem.hxx>
37 #include <editeng/postitem.hxx>
38 #include <editeng/udlnitem.hxx>
39 #include <editeng/crossedoutitem.hxx>
40 #include <editeng/shdditem.hxx>
41 #include <svx/xlnclit.hxx>
42 #include <svx/xlncapit.hxx>
43 #include <svx/xlnwtit.hxx>
44 #include <svx/xflclit.hxx>
45 #include <editeng/fontitem.hxx>
46 #include <editeng/wrlmitem.hxx>
47 #include <editeng/contouritem.hxx>
48 #include <editeng/colritem.hxx>
49 #include <vcl/metric.hxx>
50 #include <editeng/charscaleitem.hxx>
51 #include <svx/sdtditm.hxx>
52 #include <svx/sdtagitm.hxx>
53 #include <svx/sdtfsitm.hxx>
54 #include <svx/svdmodel.hxx>
55 #include <svx/svdpage.hxx>
56 #include <svx/svdobj.hxx>
57 #include <svx/svdotext.hxx>
58 #include <svx/svdorect.hxx>
59 #include <svx/svdograf.hxx>
60 #include <svx/svdopath.hxx>
61 #include <svx/svdetc.hxx>
62 #include <svl/itemset.hxx>
63 #include <basegfx/polygon/b2dpolygon.hxx>
64 #include <tools/helpers.hxx>
65 #include <basegfx/matrix/b2dhommatrix.hxx>
66 #include <basegfx/matrix/b2dhommatrixtools.hxx>
67 #include <svx/xlinjoit.hxx>
68 #include <svx/xlndsit.hxx>
69 #include <basegfx/polygon/b2dpolygonclipper.hxx>
70 #include <svx/xbtmpit.hxx>
71 #include <svx/xfillit0.hxx>
72 #include <svx/xflbmtit.hxx>
73 #include <svx/xflbstit.hxx>
74 #include <svx/xlineit0.hxx>
75 #include <basegfx/polygon/b2dpolypolygontools.hxx>
76 #include <svx/svditer.hxx>
77 #include <svx/svdogrp.hxx>
78 #include <vcl/dibtools.hxx>
79 #include <sal/log.hxx>
83 double sqrt2(double a
, double b
) { return sqrt(a
* a
+ b
* b
); }
86 using namespace com::sun::star
;
88 ImpSdrPdfImport::ImpSdrPdfImport(SdrModel
& rModel
, SdrLayerID nLay
, const tools::Rectangle
& rRect
,
89 Graphic
const& rGraphic
)
91 , mpVD(VclPtr
<VirtualDevice
>::Create())
98 , maDash(css::drawing::DashStyle_RECT
, 0, 0, 0, 0, 0)
107 , mbLastObjWasPolyWithoutLine(false)
113 , mpPDFium(vcl::pdf::PDFiumLibrary::get())
115 mpVD
->EnableOutput(false);
116 mpVD
->SetLineColor();
117 mpVD
->SetFillColor();
118 maOldLineColor
.SetRed(mpVD
->GetLineColor().GetRed() + 1);
119 mpLineAttr
= std::make_unique
<SfxItemSet
>(rModel
.GetItemPool(),
120 svl::Items
<XATTR_LINE_FIRST
, XATTR_LINE_LAST
>{});
121 mpFillAttr
= std::make_unique
<SfxItemSet
>(rModel
.GetItemPool(),
122 svl::Items
<XATTR_FILL_FIRST
, XATTR_FILL_LAST
>{});
123 mpTextAttr
= std::make_unique
<SfxItemSet
>(rModel
.GetItemPool(),
124 svl::Items
<EE_ITEMS_START
, EE_ITEMS_END
>{});
127 // Load the buffer using pdfium.
128 auto const& rVectorGraphicData
= rGraphic
.getVectorGraphicData();
129 auto* pData
= rVectorGraphicData
->getVectorGraphicDataArray().getConstArray();
130 sal_Int32 nSize
= rVectorGraphicData
->getVectorGraphicDataArrayLength();
131 mpPdfDocument
= mpPDFium
->openDocument(pData
, nSize
);
135 mnPageCount
= mpPdfDocument
->getPageCount();
138 ImpSdrPdfImport::~ImpSdrPdfImport() = default;
140 void ImpSdrPdfImport::DoObjects(SvdProgressInfo
* pProgrInfo
, sal_uInt32
* pActionsToReport
,
143 const int nPageCount
= mpPdfDocument
->getPageCount();
144 if (!(nPageCount
> 0 && nPageIndex
>= 0 && nPageIndex
< nPageCount
))
148 auto pPdfPage
= mpPdfDocument
->openPage(nPageIndex
);
152 basegfx::B2DSize dPageSize
= mpPdfDocument
->getPageSize(nPageIndex
);
154 const double dPageWidth
= dPageSize
.getX();
155 const double dPageHeight
= dPageSize
.getY();
157 SetupPageScale(dPageWidth
, dPageHeight
);
159 // Load the page text to extract it when we get text elements.
160 auto pTextPage
= pPdfPage
->getTextPage();
162 const int nPageObjectCount
= pPdfPage
->getObjectCount();
164 pProgrInfo
->SetActionCount(nPageObjectCount
);
166 for (int nPageObjectIndex
= 0; nPageObjectIndex
< nPageObjectCount
; ++nPageObjectIndex
)
168 auto pPageObject
= pPdfPage
->getObject(nPageObjectIndex
);
169 ImportPdfObject(pPageObject
, pTextPage
, nPageObjectIndex
);
170 if (pProgrInfo
&& pActionsToReport
)
172 (*pActionsToReport
)++;
174 if (*pActionsToReport
>= 16)
176 if (!pProgrInfo
->ReportActions(*pActionsToReport
))
179 *pActionsToReport
= 0;
185 void ImpSdrPdfImport::SetupPageScale(const double dPageWidth
, const double dPageHeight
)
187 mfScaleX
= mfScaleY
= 1.0;
189 // Store the page dimensions in Points.
190 mdPageHeightPts
= dPageHeight
;
192 Size
aPageSize(convertPointToMm100(dPageWidth
), convertPointToMm100(dPageHeight
));
194 if (aPageSize
.Width() && aPageSize
.Height() && (!maScaleRect
.IsEmpty()))
196 maOfs
= maScaleRect
.TopLeft();
198 if (aPageSize
.Width() != (maScaleRect
.GetWidth() - 1))
200 mfScaleX
= static_cast<double>(maScaleRect
.GetWidth() - 1)
201 / static_cast<double>(aPageSize
.Width());
204 if (aPageSize
.Height() != (maScaleRect
.GetHeight() - 1))
206 mfScaleY
= static_cast<double>(maScaleRect
.GetHeight() - 1)
207 / static_cast<double>(aPageSize
.Height());
211 mbMov
= maOfs
.X() != 0 || maOfs
.Y() != 0;
213 maScaleX
= Fraction(1, 1);
214 maScaleY
= Fraction(1, 1);
216 if (aPageSize
.Width() != (maScaleRect
.GetWidth() - 1))
218 maScaleX
= Fraction(maScaleRect
.GetWidth() - 1, aPageSize
.Width());
222 if (aPageSize
.Height() != (maScaleRect
.GetHeight() - 1))
224 maScaleY
= Fraction(maScaleRect
.GetHeight() - 1, aPageSize
.Height());
229 size_t ImpSdrPdfImport::DoImport(SdrObjList
& rOL
, size_t nInsPos
, int nPageNumber
,
230 SvdProgressInfo
* pProgrInfo
)
232 sal_uInt32
nActionsToReport(0);
235 DoObjects(pProgrInfo
, &nActionsToReport
, nPageNumber
);
239 pProgrInfo
->ReportActions(nActionsToReport
);
240 nActionsToReport
= 0;
246 // To calculate the progress meter, we use GetActionSize()*3.
247 // However, maTmpList has a lower entry count limit than GetActionSize(),
248 // so the actions that were assumed were too much have to be re-added.
249 // nActionsToReport = (rMtf.GetActionSize() - maTmpList.size()) * 2;
251 // announce all currently unannounced rescales
254 pProgrInfo
->ReportRescales(nActionsToReport
);
255 pProgrInfo
->SetInsertCount(maTmpList
.size());
258 nActionsToReport
= 0;
260 // insert all objects cached in aTmpList now into rOL from nInsPos
261 nInsPos
= std::min(nInsPos
, rOL
.GetObjCount());
263 for (SdrObject
* pObj
: maTmpList
)
265 rOL
.NbcInsertObject(pObj
, nInsPos
);
272 if (nActionsToReport
>= 32) // update all 32 actions
274 pProgrInfo
->ReportInserts(nActionsToReport
);
275 nActionsToReport
= 0;
280 // report all remaining inserts for the last time
283 pProgrInfo
->ReportInserts(nActionsToReport
);
286 return maTmpList
.size();
289 void ImpSdrPdfImport::SetAttributes(SdrObject
* pObj
, bool bForceTextAttr
)
293 bool bLine(!bForceTextAttr
);
294 bool bFill(!pObj
|| (pObj
->IsClosedObj() && !bForceTextAttr
));
295 bool bText(bForceTextAttr
|| (pObj
&& pObj
->GetOutlinerParaObject()));
301 mpLineAttr
->Put(XLineWidthItem(mnLineWidth
));
305 mpLineAttr
->Put(XLineWidthItem(0));
308 maOldLineColor
= mpVD
->GetLineColor();
310 if (mpVD
->IsLineColor())
312 mpLineAttr
->Put(XLineStyleItem(drawing::LineStyle_SOLID
)); //TODO support dashed lines.
313 mpLineAttr
->Put(XLineColorItem(OUString(), mpVD
->GetLineColor()));
317 mpLineAttr
->Put(XLineStyleItem(drawing::LineStyle_NONE
));
320 mpLineAttr
->Put(XLineJointItem(css::drawing::LineJoint_NONE
));
322 // Add LineCap support
323 mpLineAttr
->Put(XLineCapItem(gaLineCap
));
325 if (((maDash
.GetDots() && maDash
.GetDotLen())
326 || (maDash
.GetDashes() && maDash
.GetDashLen()))
327 && maDash
.GetDistance())
329 mpLineAttr
->Put(XLineDashItem(OUString(), maDash
));
333 mpLineAttr
->Put(XLineDashItem(OUString(), XDash(css::drawing::DashStyle_RECT
)));
343 if (mpVD
->IsFillColor())
345 mpFillAttr
->Put(XFillStyleItem(drawing::FillStyle_SOLID
));
346 mpFillAttr
->Put(XFillColorItem(OUString(), mpVD
->GetFillColor()));
350 mpFillAttr
->Put(XFillStyleItem(drawing::FillStyle_NONE
));
358 if (bText
&& mbFntDirty
)
360 vcl::Font
aFnt(mpVD
->GetFont());
361 const sal_uInt32
nHeight(FRound(aFnt
.GetFontSize().Height() * mfScaleY
));
363 mpTextAttr
->Put(SvxFontItem(aFnt
.GetFamilyType(), aFnt
.GetFamilyName(), aFnt
.GetStyleName(),
364 aFnt
.GetPitch(), aFnt
.GetCharSet(), EE_CHAR_FONTINFO
));
365 mpTextAttr
->Put(SvxFontItem(aFnt
.GetFamilyType(), aFnt
.GetFamilyName(), aFnt
.GetStyleName(),
366 aFnt
.GetPitch(), aFnt
.GetCharSet(), EE_CHAR_FONTINFO_CJK
));
367 mpTextAttr
->Put(SvxFontItem(aFnt
.GetFamilyType(), aFnt
.GetFamilyName(), aFnt
.GetStyleName(),
368 aFnt
.GetPitch(), aFnt
.GetCharSet(), EE_CHAR_FONTINFO_CTL
));
369 mpTextAttr
->Put(SvxPostureItem(aFnt
.GetItalic(), EE_CHAR_ITALIC
));
370 mpTextAttr
->Put(SvxWeightItem(aFnt
.GetWeight(), EE_CHAR_WEIGHT
));
371 mpTextAttr
->Put(SvxFontHeightItem(nHeight
, 100, EE_CHAR_FONTHEIGHT
));
372 mpTextAttr
->Put(SvxFontHeightItem(nHeight
, 100, EE_CHAR_FONTHEIGHT_CJK
));
373 mpTextAttr
->Put(SvxFontHeightItem(nHeight
, 100, EE_CHAR_FONTHEIGHT_CTL
));
374 mpTextAttr
->Put(SvxCharScaleWidthItem(100, EE_CHAR_FONTWIDTH
));
375 mpTextAttr
->Put(SvxUnderlineItem(aFnt
.GetUnderline(), EE_CHAR_UNDERLINE
));
376 mpTextAttr
->Put(SvxOverlineItem(aFnt
.GetOverline(), EE_CHAR_OVERLINE
));
377 mpTextAttr
->Put(SvxCrossedOutItem(aFnt
.GetStrikeout(), EE_CHAR_STRIKEOUT
));
378 mpTextAttr
->Put(SvxShadowedItem(aFnt
.IsShadow(), EE_CHAR_SHADOW
));
380 // #i118485# Setting this item leads to problems (written #i118498# for this)
381 // mpTextAttr->Put(SvxAutoKernItem(aFnt.IsKerning(), EE_CHAR_KERNING));
383 mpTextAttr
->Put(SvxWordLineModeItem(aFnt
.IsWordLineMode(), EE_CHAR_WLM
));
384 mpTextAttr
->Put(SvxContourItem(aFnt
.IsOutline(), EE_CHAR_OUTLINE
));
385 mpTextAttr
->Put(SvxColorItem(mpVD
->GetTextColor(), EE_CHAR_COLOR
));
386 //... svxfont textitem svditext
393 pObj
->SetLayer(mnLayer
);
397 pObj
->SetMergedItemSet(*mpLineAttr
);
402 pObj
->SetMergedItemSet(*mpFillAttr
);
407 pObj
->SetMergedItemSet(*mpTextAttr
);
408 pObj
->SetMergedItem(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_LEFT
));
412 void ImpSdrPdfImport::InsertObj(SdrObject
* pObj
, bool bScale
)
414 if (bScale
&& !maScaleRect
.IsEmpty())
418 pObj
->NbcResize(Point(), maScaleX
, maScaleY
);
423 pObj
->NbcMove(Size(maOfs
.X(), maOfs
.Y()));
429 const basegfx::B2DPolyPolygon
aPoly(pObj
->TakeXorPoly());
430 const basegfx::B2DRange
aOldRange(aPoly
.getB2DRange());
431 const SdrLayerID
aOldLayer(pObj
->GetLayer());
432 const SfxItemSet
aOldItemSet(pObj
->GetMergedItemSet());
433 const SdrGrafObj
* pSdrGrafObj
= dynamic_cast<SdrGrafObj
*>(pObj
);
434 const SdrTextObj
* pSdrTextObj
= dynamic_cast<SdrTextObj
*>(pObj
);
436 if (pSdrTextObj
&& pSdrTextObj
->HasText())
438 // all text objects are created from ImportText and have no line or fill attributes, so
439 // it is okay to concentrate on the text itself
442 const basegfx::B2DPolyPolygon
aTextContour(pSdrTextObj
->TakeContour());
443 const basegfx::B2DRange
aTextRange(aTextContour
.getB2DRange());
444 const basegfx::B2DRange
aClipRange(maClip
.getB2DRange());
446 // no overlap -> completely outside
447 if (!aClipRange
.overlaps(aTextRange
))
449 SdrObject::Free(pObj
);
453 // when the clip is a rectangle fast check for inside is possible
454 if (basegfx::utils::isRectangle(maClip
) && aClipRange
.isInside(aTextRange
))
456 // completely inside ClipRect
460 // here text needs to be clipped; to do so, convert to SdrObjects with polygons
461 // and add these recursively. Delete original object, do not add in this run
462 SdrObjectUniquePtr pConverted
= pSdrTextObj
->ConvertToPolyObj(true, true);
463 SdrObject::Free(pObj
);
467 // recursively add created conversion; per definition this shall not
468 // contain further SdrTextObjs. Visit only non-group objects
469 SdrObjListIter
aIter(*pConverted
, SdrIterMode::DeepNoGroups
);
471 // work with clones; the created conversion may contain group objects
472 // and when working with the original objects the loop itself could
473 // break and the cleanup later would be pretty complicated (only delete group
474 // objects, are these empty, ...?)
475 while (aIter
.IsMore())
477 SdrObject
* pCandidate
= aIter
.Next();
478 OSL_ENSURE(pCandidate
&& dynamic_cast<SdrObjGroup
*>(pCandidate
) == nullptr,
479 "SdrObjListIter with SdrIterMode::DeepNoGroups error (!)");
480 SdrObject
* pNewClone(
481 pCandidate
->CloneSdrObject(pCandidate
->getSdrModelFromSdrObject()));
485 InsertObj(pNewClone
, false);
489 OSL_ENSURE(false, "SdrObject::Clone() failed (!)");
503 aBitmapEx
= pSdrGrafObj
->GetGraphic().GetBitmapEx();
506 SdrObject::Free(pObj
);
508 if (!aOldRange
.isEmpty())
510 // clip against ClipRegion
511 const basegfx::B2DPolyPolygon
aNewPoly(basegfx::utils::clipPolyPolygonOnPolyPolygon(
512 aPoly
, maClip
, true, !aPoly
.isClosed()));
513 const basegfx::B2DRange
aNewRange(aNewPoly
.getB2DRange());
515 if (!aNewRange
.isEmpty())
517 pObj
= new SdrPathObj(*mpModel
, aNewPoly
.isClosed() ? OBJ_POLY
: OBJ_PLIN
,
520 pObj
->SetLayer(aOldLayer
);
521 pObj
->SetMergedItemSet(aOldItemSet
);
525 // aNewRange is inside of aOldRange and defines which part of aBitmapEx is used
526 const double fScaleX(aBitmapEx
.GetSizePixel().Width()
527 / (aOldRange
.getWidth() ? aOldRange
.getWidth() : 1.0));
528 const double fScaleY(
529 aBitmapEx
.GetSizePixel().Height()
530 / (aOldRange
.getHeight() ? aOldRange
.getHeight() : 1.0));
531 basegfx::B2DRange
aPixel(aNewRange
);
532 basegfx::B2DHomMatrix aTrans
;
534 aTrans
.translate(-aOldRange
.getMinX(), -aOldRange
.getMinY());
535 aTrans
.scale(fScaleX
, fScaleY
);
536 aPixel
.transform(aTrans
);
538 const Size
aOrigSizePixel(aBitmapEx
.GetSizePixel());
539 const Point
aClipTopLeft(
540 basegfx::fround(floor(std::max(0.0, aPixel
.getMinX()))),
541 basegfx::fround(floor(std::max(0.0, aPixel
.getMinY()))));
542 const Size
aClipSize(
543 basegfx::fround(ceil(std::min(
544 static_cast<double>(aOrigSizePixel
.Width()), aPixel
.getWidth()))),
546 ceil(std::min(static_cast<double>(aOrigSizePixel
.Height()),
547 aPixel
.getHeight()))));
548 const BitmapEx
aClippedBitmap(aBitmapEx
, aClipTopLeft
, aClipSize
);
550 pObj
->SetMergedItem(XFillStyleItem(drawing::FillStyle_BITMAP
));
551 pObj
->SetMergedItem(XFillBitmapItem(OUString(), Graphic(aClippedBitmap
)));
552 pObj
->SetMergedItem(XFillBmpTileItem(false));
553 pObj
->SetMergedItem(XFillBmpStretchItem(true));
563 // #i111954# check object for visibility
564 // used are SdrPathObj, SdrRectObj, SdrCircObj, SdrGrafObj
565 bool bVisible(false);
567 if (pObj
->HasLineStyle())
572 if (!bVisible
&& pObj
->HasFillStyle())
579 SdrTextObj
* pTextObj
= dynamic_cast<SdrTextObj
*>(pObj
);
581 if (pTextObj
&& pTextObj
->HasText())
589 SdrGrafObj
* pGrafObj
= dynamic_cast<SdrGrafObj
*>(pObj
);
593 // this may be refined to check if the graphic really is visible. It
594 // is here to ensure that graphic objects without fill, line and text
602 SdrObject::Free(pObj
);
606 maTmpList
.push_back(pObj
);
608 if (dynamic_cast<SdrPathObj
*>(pObj
))
610 const bool bClosed(pObj
->IsClosedObj());
612 mbLastObjWasPolyWithoutLine
= mbNoLine
&& bClosed
;
616 mbLastObjWasPolyWithoutLine
= false;
621 bool ImpSdrPdfImport::CheckLastPolyLineAndFillMerge(const basegfx::B2DPolyPolygon
& rPolyPolygon
)
623 // #i73407# reformulation to use new B2DPolygon classes
624 if (mbLastObjWasPolyWithoutLine
)
626 SdrObject
* pTmpObj
= !maTmpList
.empty() ? maTmpList
[maTmpList
.size() - 1] : nullptr;
627 SdrPathObj
* pLastPoly
= dynamic_cast<SdrPathObj
*>(pTmpObj
);
631 if (pLastPoly
->GetPathPoly() == rPolyPolygon
)
633 SetAttributes(nullptr);
635 if (!mbNoLine
&& mbNoFill
)
637 pLastPoly
->SetMergedItemSet(*mpLineAttr
);
648 void ImpSdrPdfImport::checkClip()
650 if (mpVD
->IsClipRegion())
652 maClip
= mpVD
->GetClipRegion().GetAsB2DPolyPolygon();
656 const basegfx::B2DHomMatrix
aTransform(basegfx::utils::createScaleTranslateB2DHomMatrix(
657 mfScaleX
, mfScaleY
, maOfs
.X(), maOfs
.Y()));
659 maClip
.transform(aTransform
);
664 bool ImpSdrPdfImport::isClip() const { return !maClip
.getB2DRange().isEmpty(); }
665 void ImpSdrPdfImport::ImportPdfObject(
666 std::unique_ptr
<vcl::pdf::PDFiumPageObject
> const& pPageObject
,
667 std::unique_ptr
<vcl::pdf::PDFiumTextPage
> const& pTextPage
, int nPageObjectIndex
)
672 const vcl::pdf::PDFPageObjectType ePageObjectType
= pPageObject
->getType();
673 switch (ePageObjectType
)
675 case vcl::pdf::PDFPageObjectType::Text
:
676 ImportText(pPageObject
, pTextPage
, nPageObjectIndex
);
678 case vcl::pdf::PDFPageObjectType::Path
:
679 ImportPath(pPageObject
, nPageObjectIndex
);
681 case vcl::pdf::PDFPageObjectType::Image
:
682 ImportImage(pPageObject
, nPageObjectIndex
);
684 case vcl::pdf::PDFPageObjectType::Shading
:
685 SAL_WARN("sd.filter", "Got page object SHADING: " << nPageObjectIndex
);
687 case vcl::pdf::PDFPageObjectType::Form
:
688 ImportForm(pPageObject
, pTextPage
, nPageObjectIndex
);
691 SAL_WARN("sd.filter", "Unknown PDF page object #" << nPageObjectIndex
<< " of type: "
692 << static_cast<int>(ePageObjectType
));
697 void ImpSdrPdfImport::ImportForm(std::unique_ptr
<vcl::pdf::PDFiumPageObject
> const& pPageObject
,
698 std::unique_ptr
<vcl::pdf::PDFiumTextPage
> const& pTextPage
,
699 int /*nPageObjectIndex*/)
701 // Get the form matrix to perform correct translation/scaling of the form sub-objects.
702 const basegfx::B2DHomMatrix aOldMatrix
= maCurrentMatrix
;
704 maCurrentMatrix
= pPageObject
->getMatrix();
706 const int nCount
= pPageObject
->getFormObjectCount();
707 for (int nIndex
= 0; nIndex
< nCount
; ++nIndex
)
709 auto pFormObject
= pPageObject
->getFormObject(nIndex
);
711 ImportPdfObject(pFormObject
, pTextPage
, -1);
714 // Restore the old one.
715 maCurrentMatrix
= aOldMatrix
;
718 void ImpSdrPdfImport::ImportText(std::unique_ptr
<vcl::pdf::PDFiumPageObject
> const& pPageObject
,
719 std::unique_ptr
<vcl::pdf::PDFiumTextPage
> const& pTextPage
,
720 int /*nPageObjectIndex*/)
722 basegfx::B2DRectangle aTextRect
= pPageObject
->getBounds();
723 basegfx::B2DHomMatrix aMatrix
= pPageObject
->getMatrix();
725 basegfx::B2DHomMatrix
aTextMatrix(maCurrentMatrix
);
727 aTextRect
*= aTextMatrix
;
728 const tools::Rectangle aRect
= PointsToLogic(aTextRect
.getMinX(), aTextRect
.getMaxX(),
729 aTextRect
.getMinY(), aTextRect
.getMaxY());
731 OUString sText
= pPageObject
->getText(pTextPage
);
733 const double dFontSize
= pPageObject
->getFontSize();
734 double dFontSizeH
= fabs(sqrt2(aMatrix
.a(), aMatrix
.c()) * dFontSize
);
735 double dFontSizeV
= fabs(sqrt2(aMatrix
.b(), aMatrix
.d()) * dFontSize
);
737 dFontSizeH
= convertPointToMm100(dFontSizeH
);
738 dFontSizeV
= convertPointToMm100(dFontSizeV
);
740 const Size
aFontSize(dFontSizeH
, dFontSizeV
);
741 vcl::Font aFnt
= mpVD
->GetFont();
742 if (aFontSize
!= aFnt
.GetFontSize())
744 aFnt
.SetFontSize(aFontSize
);
749 OUString sFontName
= pPageObject
->getFontName();
750 if (!sFontName
.isEmpty() && sFontName
!= aFnt
.GetFamilyName())
752 aFnt
.SetFamilyName(sFontName
);
757 Color
aTextColor(COL_TRANSPARENT
);
760 switch (pPageObject
->getTextRenderMode())
762 case FPDF_TEXTRENDERMODE_FILL
:
763 case FPDF_TEXTRENDERMODE_FILL_CLIP
:
764 case FPDF_TEXTRENDERMODE_FILL_STROKE
:
765 case FPDF_TEXTRENDERMODE_FILL_STROKE_CLIP
:
768 case FPDF_TEXTRENDERMODE_STROKE
:
769 case FPDF_TEXTRENDERMODE_STROKE_CLIP
:
770 case FPDF_TEXTRENDERMODE_UNKNOWN
:
772 case FPDF_TEXTRENDERMODE_INVISIBLE
:
773 case FPDF_TEXTRENDERMODE_CLIP
:
779 Color aColor
= bFill
? pPageObject
->getFillColor() : pPageObject
->getStrokeColor();
780 if (aColor
!= COL_TRANSPARENT
)
781 aTextColor
= aColor
.GetRGBColor();
784 if (aTextColor
!= mpVD
->GetTextColor())
786 mpVD
->SetTextColor(aTextColor
);
790 InsertTextObject(aRect
.TopLeft(), aRect
.GetSize(), sText
);
793 void ImpSdrPdfImport::InsertTextObject(const Point
& rPos
, const Size
& rSize
, const OUString
& rStr
)
795 // calc text box size, add 5% to make it fit safely
797 FontMetric
aFontMetric(mpVD
->GetFontMetric());
798 vcl::Font
aFont(mpVD
->GetFont());
799 FontAlign
eAlignment(aFont
.GetAlignment());
801 // sal_Int32 nTextWidth = static_cast<sal_Int32>(mpVD->GetTextWidth(rStr) * mfScaleX);
802 sal_Int32 nTextHeight
= static_cast<sal_Int32
>(mpVD
->GetTextHeight() * mfScaleY
);
804 Point
aPosition(FRound(rPos
.X() * mfScaleX
+ maOfs
.X()),
805 FRound(rPos
.Y() * mfScaleY
+ maOfs
.Y()));
806 Size
aSize(FRound(rSize
.Width() * mfScaleX
), FRound(rSize
.Height() * mfScaleY
));
808 if (eAlignment
== ALIGN_BASELINE
)
809 aPosition
.AdjustY(-FRound(aFontMetric
.GetAscent() * mfScaleY
));
810 else if (eAlignment
== ALIGN_BOTTOM
)
811 aPosition
.AdjustY(-nTextHeight
);
813 tools::Rectangle
aTextRect(aPosition
, aSize
);
814 SdrRectObj
* pText
= new SdrRectObj(*mpModel
, OBJ_TEXT
, aTextRect
);
816 pText
->SetMergedItem(makeSdrTextUpperDistItem(0));
817 pText
->SetMergedItem(makeSdrTextLowerDistItem(0));
818 pText
->SetMergedItem(makeSdrTextRightDistItem(0));
819 pText
->SetMergedItem(makeSdrTextLeftDistItem(0));
821 if (aFont
.GetAverageFontWidth())
823 pText
->ClearMergedItem(SDRATTR_TEXT_AUTOGROWWIDTH
);
824 pText
->SetMergedItem(makeSdrTextAutoGrowHeightItem(false));
825 // don't let the margins eat the space needed for the text
826 pText
->SetMergedItem(SdrTextFitToSizeTypeItem(drawing::TextFitToSizeType_ALLLINES
));
830 pText
->SetMergedItem(makeSdrTextAutoGrowWidthItem(true));
833 pText
->SetLayer(mnLayer
);
834 pText
->NbcSetText(rStr
);
835 SetAttributes(pText
, true);
836 pText
->SetSnapRect(aTextRect
);
838 if (!aFont
.IsTransparent())
840 SfxItemSet
aAttr(*mpFillAttr
->GetPool(), svl::Items
<XATTR_FILL_FIRST
, XATTR_FILL_LAST
>{});
841 aAttr
.Put(XFillStyleItem(drawing::FillStyle_SOLID
));
842 aAttr
.Put(XFillColorItem(OUString(), aFont
.GetFillColor()));
843 pText
->SetMergedItemSet(aAttr
);
845 sal_uInt32 nAngle
= aFont
.GetOrientation().get();
849 double a
= nAngle
* F_PI18000
;
850 double nSin
= sin(a
);
851 double nCos
= cos(a
);
852 pText
->NbcRotate(aPosition
, nAngle
, nSin
, nCos
);
854 InsertObj(pText
, false);
857 void ImpSdrPdfImport::MapScaling()
859 const size_t nCount(maTmpList
.size());
860 const MapMode
& rMap
= mpVD
->GetMapMode();
861 Point
aMapOrg(rMap
.GetOrigin());
862 bool bMov2(aMapOrg
.X() != 0 || aMapOrg
.Y() != 0);
866 for (size_t i
= mnMapScalingOfs
; i
< nCount
; i
++)
868 SdrObject
* pObj
= maTmpList
[i
];
870 pObj
->NbcMove(Size(aMapOrg
.X(), aMapOrg
.Y()));
874 mnMapScalingOfs
= nCount
;
877 void ImpSdrPdfImport::ImportImage(std::unique_ptr
<vcl::pdf::PDFiumPageObject
> const& pPageObject
,
878 int /*nPageObjectIndex*/)
880 std::unique_ptr
<vcl::pdf::PDFiumBitmap
> bitmap
= pPageObject
->getImageBitmap();
883 SAL_WARN("sd.filter", "Failed to get IMAGE");
887 const int format
= FPDFBitmap_GetFormat(bitmap
->getPointer());
888 if (format
== FPDFBitmap_Unknown
)
890 SAL_WARN("sd.filter", "Failed to get IMAGE format");
894 const unsigned char* pBuf
895 = static_cast<const unsigned char*>(FPDFBitmap_GetBuffer(bitmap
->getPointer()));
896 const int nWidth
= FPDFBitmap_GetWidth(bitmap
->getPointer());
897 const int nHeight
= FPDFBitmap_GetHeight(bitmap
->getPointer());
898 const int nStride
= FPDFBitmap_GetStride(bitmap
->getPointer());
899 BitmapEx
aBitmap(Size(nWidth
, nHeight
), 24);
904 ReadRawDIB(aBitmap
, pBuf
, ScanlineFormat::N24BitTcBgr
, nHeight
, nStride
);
906 case FPDFBitmap_BGRx
:
907 ReadRawDIB(aBitmap
, pBuf
, ScanlineFormat::N32BitTcRgba
, nHeight
, nStride
);
909 case FPDFBitmap_BGRA
:
910 ReadRawDIB(aBitmap
, pBuf
, ScanlineFormat::N32BitTcBgra
, nHeight
, nStride
);
913 SAL_WARN("sd.filter", "Got IMAGE width: " << nWidth
<< ", height: " << nHeight
914 << ", stride: " << nStride
915 << ", format: " << format
);
923 if (!FPDFPageObj_GetBounds(pPageObject
->getPointer(), &left
, &bottom
, &right
, &top
))
925 SAL_WARN("sd.filter", "FAILED to get image bounds");
928 tools::Rectangle aRect
= PointsToLogic(left
, right
, top
, bottom
);
929 aRect
.AdjustRight(1);
930 aRect
.AdjustBottom(1);
932 SdrGrafObj
* pGraf
= new SdrGrafObj(*mpModel
, Graphic(aBitmap
), aRect
);
934 // This action is not creating line and fill, set directly, do not use SetAttributes(..)
935 pGraf
->SetMergedItem(XLineStyleItem(drawing::LineStyle_NONE
));
936 pGraf
->SetMergedItem(XFillStyleItem(drawing::FillStyle_NONE
));
940 void ImpSdrPdfImport::ImportPath(std::unique_ptr
<vcl::pdf::PDFiumPageObject
> const& pPageObject
,
941 int /*nPageObjectIndex*/)
943 auto aPathMatrix
= pPageObject
->getMatrix();
945 aPathMatrix
*= maCurrentMatrix
;
947 basegfx::B2DPolyPolygon aPolyPoly
;
948 basegfx::B2DPolygon aPoly
;
949 std::vector
<basegfx::B2DPoint
> aBezier
;
951 const int nSegments
= pPageObject
->getPathSegmentCount();
952 for (int nSegmentIndex
= 0; nSegmentIndex
< nSegments
; ++nSegmentIndex
)
954 auto pPathSegment
= pPageObject
->getPathSegment(nSegmentIndex
);
955 if (pPathSegment
!= nullptr)
957 basegfx::B2DPoint aB2DPoint
= pPathSegment
->getPoint();
958 aB2DPoint
*= aPathMatrix
;
960 const bool bClose
= pPathSegment
->isClosed();
962 aPoly
.setClosed(bClose
); // TODO: Review
964 Point aPoint
= PointsToLogic(aB2DPoint
.getX(), aB2DPoint
.getY());
965 aB2DPoint
.setX(aPoint
.X());
966 aB2DPoint
.setY(aPoint
.Y());
968 const int nSegmentType
= pPathSegment
->getType();
969 switch (nSegmentType
)
971 case FPDF_SEGMENT_LINETO
:
972 aPoly
.append(aB2DPoint
);
975 case FPDF_SEGMENT_BEZIERTO
:
976 aBezier
.emplace_back(aB2DPoint
.getX(), aB2DPoint
.getY());
977 if (aBezier
.size() == 3)
979 aPoly
.appendBezierSegment(aBezier
[0], aBezier
[1], aBezier
[2]);
984 case FPDF_SEGMENT_MOVETO
:
986 if (aPoly
.count() > 0)
988 aPolyPoly
.append(aPoly
, 1);
992 aPoly
.append(aB2DPoint
);
995 case FPDF_SEGMENT_UNKNOWN
:
997 SAL_WARN("sd.filter", "Unknown path segment type in PDF: " << nSegmentType
);
1003 if (aBezier
.size() == 3)
1005 aPoly
.appendBezierSegment(aBezier
[0], aBezier
[1], aBezier
[2]);
1009 if (aPoly
.count() > 0)
1011 aPolyPoly
.append(aPoly
, 1);
1015 const basegfx::B2DHomMatrix
aTransform(
1016 basegfx::utils::createScaleTranslateB2DHomMatrix(mfScaleX
, mfScaleY
, maOfs
.X(), maOfs
.Y()));
1017 aPolyPoly
.transform(aTransform
);
1020 FPDFPageObj_GetStrokeWidth(pPageObject
->getPointer(), &fWidth
);
1021 const double dWidth
= 0.5 * fabs(sqrt2(aPathMatrix
.a(), aPathMatrix
.c()) * fWidth
);
1022 mnLineWidth
= convertPointToMm100(dWidth
);
1024 int nFillMode
= FPDF_FILLMODE_ALTERNATE
;
1025 FPDF_BOOL bStroke
= 1; // Assume we have to draw, unless told otherwise.
1026 if (FPDFPath_GetDrawMode(pPageObject
->getPointer(), &nFillMode
, &bStroke
))
1028 if (nFillMode
== FPDF_FILLMODE_ALTERNATE
)
1029 mpVD
->SetDrawMode(DrawModeFlags::Default
);
1030 else if (nFillMode
== FPDF_FILLMODE_WINDING
)
1031 mpVD
->SetDrawMode(DrawModeFlags::Default
);
1033 mpVD
->SetDrawMode(DrawModeFlags::NoFill
);
1040 FPDFPageObj_GetFillColor(pPageObject
->getPointer(), &nR
, &nG
, &nB
, &nA
);
1041 mpVD
->SetFillColor(Color(nR
, nG
, nB
));
1045 FPDFPageObj_GetStrokeColor(pPageObject
->getPointer(), &nR
, &nG
, &nB
, &nA
);
1046 mpVD
->SetLineColor(Color(nR
, nG
, nB
));
1049 mpVD
->SetLineColor(COL_TRANSPARENT
);
1051 if (!mbLastObjWasPolyWithoutLine
|| !CheckLastPolyLineAndFillMerge(aPolyPoly
))
1053 SdrPathObj
* pPath
= new SdrPathObj(*mpModel
, OBJ_POLY
, aPolyPoly
);
1054 SetAttributes(pPath
);
1055 InsertObj(pPath
, false);
1059 Point
ImpSdrPdfImport::PointsToLogic(double x
, double y
) const
1061 y
= correctVertOrigin(y
);
1063 Point
aPos(convertPointToMm100(x
), convertPointToMm100(y
));
1067 tools::Rectangle
ImpSdrPdfImport::PointsToLogic(double left
, double right
, double top
,
1068 double bottom
) const
1070 top
= correctVertOrigin(top
);
1071 bottom
= correctVertOrigin(bottom
);
1073 Point
aPos(convertPointToMm100(left
), convertPointToMm100(top
));
1074 Size
aSize(convertPointToMm100(right
- left
), convertPointToMm100(bottom
- top
));
1076 return tools::Rectangle(aPos
, aSize
);
1079 #endif // HAVE_FEATURE_PDFIUM
1081 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */