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 <tools/UnitConversion.hxx>
23 #include <vcl/graph.hxx>
24 #include <vcl/vectorgraphicdata.hxx>
26 #include <editeng/eeitem.hxx>
27 #include <editeng/fhgtitem.hxx>
28 #include <editeng/wghtitem.hxx>
29 #include <editeng/postitem.hxx>
30 #include <editeng/udlnitem.hxx>
31 #include <editeng/crossedoutitem.hxx>
32 #include <editeng/shdditem.hxx>
33 #include <svx/xlnclit.hxx>
34 #include <svx/xlncapit.hxx>
35 #include <svx/xlnwtit.hxx>
36 #include <svx/xflclit.hxx>
37 #include <editeng/fontitem.hxx>
38 #include <editeng/wrlmitem.hxx>
39 #include <editeng/contouritem.hxx>
40 #include <editeng/colritem.hxx>
41 #include <vcl/metric.hxx>
42 #include <editeng/charscaleitem.hxx>
43 #include <svx/sdtditm.hxx>
44 #include <svx/sdtagitm.hxx>
45 #include <svx/sdtfsitm.hxx>
46 #include <svx/svdmodel.hxx>
47 #include <svx/svdpage.hxx>
48 #include <svx/svdobj.hxx>
49 #include <svx/svdotext.hxx>
50 #include <svx/svdorect.hxx>
51 #include <svx/svdograf.hxx>
52 #include <svx/svdopath.hxx>
53 #include <svx/svdetc.hxx>
54 #include <svl/itemset.hxx>
55 #include <basegfx/polygon/b2dpolygon.hxx>
56 #include <tools/helpers.hxx>
57 #include <basegfx/matrix/b2dhommatrix.hxx>
58 #include <basegfx/matrix/b2dhommatrixtools.hxx>
59 #include <svx/xlinjoit.hxx>
60 #include <svx/xlndsit.hxx>
61 #include <basegfx/polygon/b2dpolygonclipper.hxx>
62 #include <svx/xbtmpit.hxx>
63 #include <svx/xfillit0.hxx>
64 #include <svx/xflbmtit.hxx>
65 #include <svx/xflbstit.hxx>
66 #include <svx/xlineit0.hxx>
67 #include <basegfx/polygon/b2dpolypolygontools.hxx>
68 #include <svx/svditer.hxx>
69 #include <svx/svdogrp.hxx>
70 #include <vcl/dibtools.hxx>
71 #include <sal/log.hxx>
72 #include <osl/diagnose.h>
74 using namespace com::sun::star
;
76 ImpSdrPdfImport::ImpSdrPdfImport(SdrModel
& rModel
, SdrLayerID nLay
, const tools::Rectangle
& rRect
,
77 Graphic
const& rGraphic
)
78 : mpVD(VclPtr
<VirtualDevice
>::Create())
84 , maDash(css::drawing::DashStyle_RECT
, 0, 0, 0, 0, 0)
93 , mbLastObjWasPolyWithoutLine(false)
98 , mpPDFium(vcl::pdf::PDFiumLibrary::get())
100 mpVD
->EnableOutput(false);
101 mpVD
->SetLineColor();
102 mpVD
->SetFillColor();
103 maOldLineColor
.SetRed(mpVD
->GetLineColor().GetRed() + 1);
104 mpLineAttr
= std::make_unique
<SfxItemSetFixed
<XATTR_LINE_FIRST
, XATTR_LINE_LAST
>>(
105 rModel
.GetItemPool());
106 mpFillAttr
= std::make_unique
<SfxItemSetFixed
<XATTR_FILL_FIRST
, XATTR_FILL_LAST
>>(
107 rModel
.GetItemPool());
109 = std::make_unique
<SfxItemSetFixed
<EE_ITEMS_START
, EE_ITEMS_END
>>(rModel
.GetItemPool());
113 // Load the buffer using pdfium.
114 auto const& rVectorGraphicData
= rGraphic
.getVectorGraphicData();
115 auto* pData
= rVectorGraphicData
->getBinaryDataContainer().getData();
116 sal_Int32 nSize
= rVectorGraphicData
->getBinaryDataContainer().getSize();
117 mpPdfDocument
= mpPDFium
? mpPDFium
->openDocument(pData
, nSize
, OString()) : nullptr;
121 mnPageCount
= mpPdfDocument
->getPageCount();
124 ImpSdrPdfImport::~ImpSdrPdfImport() = default;
126 void ImpSdrPdfImport::DoObjects(SvdProgressInfo
* pProgrInfo
, sal_uInt32
* pActionsToReport
,
129 const int nPageCount
= mpPdfDocument
->getPageCount();
130 if (!(nPageCount
> 0 && nPageIndex
>= 0 && nPageIndex
< nPageCount
))
134 auto pPdfPage
= mpPdfDocument
->openPage(nPageIndex
);
138 basegfx::B2DSize dPageSize
= mpPdfDocument
->getPageSize(nPageIndex
);
140 SetupPageScale(dPageSize
.getWidth(), dPageSize
.getHeight());
142 // Load the page text to extract it when we get text elements.
143 auto pTextPage
= pPdfPage
->getTextPage();
145 const int nPageObjectCount
= pPdfPage
->getObjectCount();
147 pProgrInfo
->SetActionCount(nPageObjectCount
);
149 for (int nPageObjectIndex
= 0; nPageObjectIndex
< nPageObjectCount
; ++nPageObjectIndex
)
151 auto pPageObject
= pPdfPage
->getObject(nPageObjectIndex
);
152 ImportPdfObject(pPageObject
, pTextPage
, nPageObjectIndex
);
153 if (pProgrInfo
&& pActionsToReport
)
155 (*pActionsToReport
)++;
157 if (*pActionsToReport
>= 16)
159 if (!pProgrInfo
->ReportActions(*pActionsToReport
))
162 *pActionsToReport
= 0;
168 void ImpSdrPdfImport::SetupPageScale(const double dPageWidth
, const double dPageHeight
)
170 mfScaleX
= mfScaleY
= 1.0;
172 // Store the page dimensions in Points.
173 mdPageHeightPts
= dPageHeight
;
175 Size
aPageSize(convertPointToMm100(dPageWidth
), convertPointToMm100(dPageHeight
));
177 if (aPageSize
.Width() && aPageSize
.Height() && (!maScaleRect
.IsEmpty()))
179 maOfs
= maScaleRect
.TopLeft();
181 if (aPageSize
.Width() != (maScaleRect
.GetWidth() - 1))
183 mfScaleX
= static_cast<double>(maScaleRect
.GetWidth() - 1)
184 / static_cast<double>(aPageSize
.Width());
187 if (aPageSize
.Height() != (maScaleRect
.GetHeight() - 1))
189 mfScaleY
= static_cast<double>(maScaleRect
.GetHeight() - 1)
190 / static_cast<double>(aPageSize
.Height());
194 mbMov
= maOfs
.X() != 0 || maOfs
.Y() != 0;
196 maScaleX
= Fraction(1, 1);
197 maScaleY
= Fraction(1, 1);
199 if (aPageSize
.Width() != (maScaleRect
.GetWidth() - 1))
201 maScaleX
= Fraction(maScaleRect
.GetWidth() - 1, aPageSize
.Width());
205 if (aPageSize
.Height() != (maScaleRect
.GetHeight() - 1))
207 maScaleY
= Fraction(maScaleRect
.GetHeight() - 1, aPageSize
.Height());
212 size_t ImpSdrPdfImport::DoImport(SdrObjList
& rOL
, size_t nInsPos
, int nPageNumber
,
213 SvdProgressInfo
* pProgrInfo
)
215 sal_uInt32
nActionsToReport(0);
218 DoObjects(pProgrInfo
, &nActionsToReport
, nPageNumber
);
222 pProgrInfo
->ReportActions(nActionsToReport
);
223 nActionsToReport
= 0;
229 // To calculate the progress meter, we use GetActionSize()*3.
230 // However, maTmpList has a lower entry count limit than GetActionSize(),
231 // so the actions that were assumed were too much have to be re-added.
232 // nActionsToReport = (rMtf.GetActionSize() - maTmpList.size()) * 2;
234 // announce all currently unannounced rescales
237 pProgrInfo
->ReportRescales(nActionsToReport
);
238 pProgrInfo
->SetInsertCount(maTmpList
.size());
241 nActionsToReport
= 0;
243 // insert all objects cached in aTmpList now into rOL from nInsPos
244 nInsPos
= std::min(nInsPos
, rOL
.GetObjCount());
246 for (rtl::Reference
<SdrObject
>& pObj
: maTmpList
)
248 rOL
.NbcInsertObject(pObj
.get(), nInsPos
);
255 if (nActionsToReport
>= 32) // update all 32 actions
257 pProgrInfo
->ReportInserts(nActionsToReport
);
258 nActionsToReport
= 0;
263 // report all remaining inserts for the last time
266 pProgrInfo
->ReportInserts(nActionsToReport
);
269 return maTmpList
.size();
272 void ImpSdrPdfImport::SetAttributes(SdrObject
* pObj
, bool bForceTextAttr
)
276 bool bLine(!bForceTextAttr
);
277 bool bFill(!pObj
|| (pObj
->IsClosedObj() && !bForceTextAttr
));
278 bool bText(bForceTextAttr
|| (pObj
&& pObj
->GetOutlinerParaObject()));
284 mpLineAttr
->Put(XLineWidthItem(mnLineWidth
));
288 mpLineAttr
->Put(XLineWidthItem(0));
291 maOldLineColor
= mpVD
->GetLineColor();
293 if (mpVD
->IsLineColor())
295 mpLineAttr
->Put(XLineStyleItem(drawing::LineStyle_SOLID
)); //TODO support dashed lines.
296 mpLineAttr
->Put(XLineColorItem(OUString(), mpVD
->GetLineColor()));
300 mpLineAttr
->Put(XLineStyleItem(drawing::LineStyle_NONE
));
303 mpLineAttr
->Put(XLineJointItem(css::drawing::LineJoint_NONE
));
305 // Add LineCap support
306 mpLineAttr
->Put(XLineCapItem(gaLineCap
));
308 if (((maDash
.GetDots() && maDash
.GetDotLen())
309 || (maDash
.GetDashes() && maDash
.GetDashLen()))
310 && maDash
.GetDistance())
312 mpLineAttr
->Put(XLineDashItem(OUString(), maDash
));
316 mpLineAttr
->Put(XLineDashItem(OUString(), XDash(css::drawing::DashStyle_RECT
)));
326 if (mpVD
->IsFillColor())
328 mpFillAttr
->Put(XFillStyleItem(drawing::FillStyle_SOLID
));
329 mpFillAttr
->Put(XFillColorItem(OUString(), mpVD
->GetFillColor()));
333 mpFillAttr
->Put(XFillStyleItem(drawing::FillStyle_NONE
));
341 if (bText
&& mbFntDirty
)
343 vcl::Font
aFnt(mpVD
->GetFont());
344 const sal_uInt32
nHeight(
345 basegfx::fround
<sal_uInt32
>(aFnt
.GetFontSize().Height() * mfScaleY
));
347 mpTextAttr
->Put(SvxFontItem(aFnt
.GetFamilyType(), aFnt
.GetFamilyName(), aFnt
.GetStyleName(),
348 aFnt
.GetPitch(), aFnt
.GetCharSet(), EE_CHAR_FONTINFO
));
349 mpTextAttr
->Put(SvxFontItem(aFnt
.GetFamilyType(), aFnt
.GetFamilyName(), aFnt
.GetStyleName(),
350 aFnt
.GetPitch(), aFnt
.GetCharSet(), EE_CHAR_FONTINFO_CJK
));
351 mpTextAttr
->Put(SvxFontItem(aFnt
.GetFamilyType(), aFnt
.GetFamilyName(), aFnt
.GetStyleName(),
352 aFnt
.GetPitch(), aFnt
.GetCharSet(), EE_CHAR_FONTINFO_CTL
));
353 mpTextAttr
->Put(SvxPostureItem(aFnt
.GetItalic(), EE_CHAR_ITALIC
));
354 mpTextAttr
->Put(SvxWeightItem(aFnt
.GetWeight(), EE_CHAR_WEIGHT
));
355 mpTextAttr
->Put(SvxFontHeightItem(nHeight
, 100, EE_CHAR_FONTHEIGHT
));
356 mpTextAttr
->Put(SvxFontHeightItem(nHeight
, 100, EE_CHAR_FONTHEIGHT_CJK
));
357 mpTextAttr
->Put(SvxFontHeightItem(nHeight
, 100, EE_CHAR_FONTHEIGHT_CTL
));
358 mpTextAttr
->Put(SvxCharScaleWidthItem(100, EE_CHAR_FONTWIDTH
));
359 mpTextAttr
->Put(SvxUnderlineItem(aFnt
.GetUnderline(), EE_CHAR_UNDERLINE
));
360 mpTextAttr
->Put(SvxOverlineItem(aFnt
.GetOverline(), EE_CHAR_OVERLINE
));
361 mpTextAttr
->Put(SvxCrossedOutItem(aFnt
.GetStrikeout(), EE_CHAR_STRIKEOUT
));
362 mpTextAttr
->Put(SvxShadowedItem(aFnt
.IsShadow(), EE_CHAR_SHADOW
));
364 // #i118485# Setting this item leads to problems (written #i118498# for this)
365 // mpTextAttr->Put(SvxAutoKernItem(aFnt.IsKerning(), EE_CHAR_KERNING));
367 mpTextAttr
->Put(SvxWordLineModeItem(aFnt
.IsWordLineMode(), EE_CHAR_WLM
));
368 mpTextAttr
->Put(SvxContourItem(aFnt
.IsOutline(), EE_CHAR_OUTLINE
));
369 mpTextAttr
->Put(SvxColorItem(mpVD
->GetTextColor(), EE_CHAR_COLOR
));
370 //... svxfont textitem svditext
377 pObj
->SetLayer(mnLayer
);
381 pObj
->SetMergedItemSet(*mpLineAttr
);
386 pObj
->SetMergedItemSet(*mpFillAttr
);
391 pObj
->SetMergedItemSet(*mpTextAttr
);
392 pObj
->SetMergedItem(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_LEFT
));
396 void ImpSdrPdfImport::InsertObj(SdrObject
* pObj1
, bool bScale
)
398 rtl::Reference
<SdrObject
> pObj
= pObj1
;
399 if (bScale
&& !maScaleRect
.IsEmpty())
403 pObj
->NbcResize(Point(), maScaleX
, maScaleY
);
408 pObj
->NbcMove(Size(maOfs
.X(), maOfs
.Y()));
414 const basegfx::B2DPolyPolygon
aPoly(pObj
->TakeXorPoly());
415 const basegfx::B2DRange
aOldRange(aPoly
.getB2DRange());
416 const SdrLayerID
aOldLayer(pObj
->GetLayer());
417 const SfxItemSet
aOldItemSet(pObj
->GetMergedItemSet());
418 const SdrGrafObj
* pSdrGrafObj
= dynamic_cast<SdrGrafObj
*>(pObj
.get());
419 const SdrTextObj
* pSdrTextObj
= DynCastSdrTextObj(pObj
.get());
421 if (pSdrTextObj
&& pSdrTextObj
->HasText())
423 // all text objects are created from ImportText and have no line or fill attributes, so
424 // it is okay to concentrate on the text itself
427 const basegfx::B2DPolyPolygon
aTextContour(pSdrTextObj
->TakeContour());
428 const basegfx::B2DRange
aTextRange(aTextContour
.getB2DRange());
429 const basegfx::B2DRange
aClipRange(maClip
.getB2DRange());
431 // no overlap -> completely outside
432 if (!aClipRange
.overlaps(aTextRange
))
438 // when the clip is a rectangle fast check for inside is possible
439 if (basegfx::utils::isRectangle(maClip
) && aClipRange
.isInside(aTextRange
))
441 // completely inside ClipRect
445 // here text needs to be clipped; to do so, convert to SdrObjects with polygons
446 // and add these recursively. Delete original object, do not add in this run
447 rtl::Reference
<SdrObject
> pConverted
= pSdrTextObj
->ConvertToPolyObj(true, true);
451 // recursively add created conversion; per definition this shall not
452 // contain further SdrTextObjs. Visit only non-group objects
453 SdrObjListIter
aIter(*pConverted
, SdrIterMode::DeepNoGroups
);
455 // work with clones; the created conversion may contain group objects
456 // and when working with the original objects the loop itself could
457 // break and the cleanup later would be pretty complicated (only delete group
458 // objects, are these empty, ...?)
459 while (aIter
.IsMore())
461 SdrObject
* pCandidate
= aIter
.Next();
462 OSL_ENSURE(pCandidate
&& dynamic_cast<SdrObjGroup
*>(pCandidate
) == nullptr,
463 "SdrObjListIter with SdrIterMode::DeepNoGroups error (!)");
464 rtl::Reference
<SdrObject
> pNewClone(
465 pCandidate
->CloneSdrObject(pCandidate
->getSdrModelFromSdrObject()));
469 InsertObj(pNewClone
.get(), false);
473 OSL_ENSURE(false, "SdrObject::Clone() failed (!)");
487 aBitmapEx
= pSdrGrafObj
->GetGraphic().GetBitmapEx();
492 if (!aOldRange
.isEmpty())
494 // clip against ClipRegion
495 const basegfx::B2DPolyPolygon
aNewPoly(basegfx::utils::clipPolyPolygonOnPolyPolygon(
496 aPoly
, maClip
, true, !aPoly
.isClosed()));
497 const basegfx::B2DRange
aNewRange(aNewPoly
.getB2DRange());
499 if (!aNewRange
.isEmpty())
501 pObj
= new SdrPathObj(
502 *mpModel
, aNewPoly
.isClosed() ? SdrObjKind::Polygon
: SdrObjKind::PolyLine
,
505 pObj
->SetLayer(aOldLayer
);
506 pObj
->SetMergedItemSet(aOldItemSet
);
508 if (!aBitmapEx
.IsEmpty())
510 // aNewRange is inside of aOldRange and defines which part of aBitmapEx is used
511 const double fScaleX(aBitmapEx
.GetSizePixel().Width()
512 / (aOldRange
.getWidth() ? aOldRange
.getWidth() : 1.0));
513 const double fScaleY(
514 aBitmapEx
.GetSizePixel().Height()
515 / (aOldRange
.getHeight() ? aOldRange
.getHeight() : 1.0));
516 basegfx::B2DRange
aPixel(aNewRange
);
517 basegfx::B2DHomMatrix aTrans
;
519 aTrans
.translate(-aOldRange
.getMinX(), -aOldRange
.getMinY());
520 aTrans
.scale(fScaleX
, fScaleY
);
521 aPixel
.transform(aTrans
);
523 const Size
aOrigSizePixel(aBitmapEx
.GetSizePixel());
524 const Point
aClipTopLeft(
525 basegfx::fround
<tools::Long
>(floor(std::max(0.0, aPixel
.getMinX()))),
526 basegfx::fround
<tools::Long
>(floor(std::max(0.0, aPixel
.getMinY()))));
527 const Size
aClipSize(
528 basegfx::fround
<tools::Long
>(ceil(std::min(
529 static_cast<double>(aOrigSizePixel
.Width()), aPixel
.getWidth()))),
530 basegfx::fround
<tools::Long
>(
531 ceil(std::min(static_cast<double>(aOrigSizePixel
.Height()),
532 aPixel
.getHeight()))));
533 const BitmapEx
aClippedBitmap(aBitmapEx
, aClipTopLeft
, aClipSize
);
535 pObj
->SetMergedItem(XFillStyleItem(drawing::FillStyle_BITMAP
));
536 pObj
->SetMergedItem(XFillBitmapItem(OUString(), Graphic(aClippedBitmap
)));
537 pObj
->SetMergedItem(XFillBmpTileItem(false));
538 pObj
->SetMergedItem(XFillBmpStretchItem(true));
548 // #i111954# check object for visibility
549 // used are SdrPathObj, SdrRectObj, SdrCircObj, SdrGrafObj
550 bool bVisible(false);
552 if (pObj
->HasLineStyle())
557 if (!bVisible
&& pObj
->HasFillStyle())
564 SdrTextObj
* pTextObj
= DynCastSdrTextObj(pObj
.get());
566 if (pTextObj
&& pTextObj
->HasText())
574 SdrGrafObj
* pGrafObj
= dynamic_cast<SdrGrafObj
*>(pObj
.get());
578 // this may be refined to check if the graphic really is visible. It
579 // is here to ensure that graphic objects without fill, line and text
587 maTmpList
.push_back(pObj
);
589 if (dynamic_cast<SdrPathObj
*>(pObj
.get()))
591 const bool bClosed(pObj
->IsClosedObj());
593 mbLastObjWasPolyWithoutLine
= mbNoLine
&& bClosed
;
597 mbLastObjWasPolyWithoutLine
= false;
602 bool ImpSdrPdfImport::CheckLastPolyLineAndFillMerge(const basegfx::B2DPolyPolygon
& rPolyPolygon
)
604 // #i73407# reformulation to use new B2DPolygon classes
605 if (mbLastObjWasPolyWithoutLine
)
607 SdrObject
* pTmpObj
= !maTmpList
.empty() ? maTmpList
[maTmpList
.size() - 1].get() : nullptr;
608 SdrPathObj
* pLastPoly
= dynamic_cast<SdrPathObj
*>(pTmpObj
);
612 if (pLastPoly
->GetPathPoly() == rPolyPolygon
)
614 SetAttributes(nullptr);
616 if (!mbNoLine
&& mbNoFill
)
618 pLastPoly
->SetMergedItemSet(*mpLineAttr
);
629 void ImpSdrPdfImport::checkClip()
631 if (mpVD
->IsClipRegion())
633 maClip
= mpVD
->GetClipRegion().GetAsB2DPolyPolygon();
637 const basegfx::B2DHomMatrix
aTransform(basegfx::utils::createScaleTranslateB2DHomMatrix(
638 mfScaleX
, mfScaleY
, maOfs
.X(), maOfs
.Y()));
640 maClip
.transform(aTransform
);
645 bool ImpSdrPdfImport::isClip() const { return !maClip
.getB2DRange().isEmpty(); }
646 void ImpSdrPdfImport::ImportPdfObject(
647 std::unique_ptr
<vcl::pdf::PDFiumPageObject
> const& pPageObject
,
648 std::unique_ptr
<vcl::pdf::PDFiumTextPage
> const& pTextPage
, int nPageObjectIndex
)
653 const vcl::pdf::PDFPageObjectType ePageObjectType
= pPageObject
->getType();
654 switch (ePageObjectType
)
656 case vcl::pdf::PDFPageObjectType::Text
:
657 ImportText(pPageObject
, pTextPage
, nPageObjectIndex
);
659 case vcl::pdf::PDFPageObjectType::Path
:
660 ImportPath(pPageObject
, nPageObjectIndex
);
662 case vcl::pdf::PDFPageObjectType::Image
:
663 ImportImage(pPageObject
, nPageObjectIndex
);
665 case vcl::pdf::PDFPageObjectType::Shading
:
666 SAL_WARN("sd.filter", "Got page object SHADING: " << nPageObjectIndex
);
668 case vcl::pdf::PDFPageObjectType::Form
:
669 ImportForm(pPageObject
, pTextPage
, nPageObjectIndex
);
672 SAL_WARN("sd.filter", "Unknown PDF page object #" << nPageObjectIndex
<< " of type: "
673 << static_cast<int>(ePageObjectType
));
678 void ImpSdrPdfImport::ImportForm(std::unique_ptr
<vcl::pdf::PDFiumPageObject
> const& pPageObject
,
679 std::unique_ptr
<vcl::pdf::PDFiumTextPage
> const& pTextPage
,
680 int /*nPageObjectIndex*/)
682 // Get the form matrix to perform correct translation/scaling of the form sub-objects.
683 const basegfx::B2DHomMatrix aOldMatrix
= maCurrentMatrix
;
685 maCurrentMatrix
= pPageObject
->getMatrix();
687 const int nCount
= pPageObject
->getFormObjectCount();
688 for (int nIndex
= 0; nIndex
< nCount
; ++nIndex
)
690 auto pFormObject
= pPageObject
->getFormObject(nIndex
);
692 ImportPdfObject(pFormObject
, pTextPage
, -1);
695 // Restore the old one.
696 maCurrentMatrix
= aOldMatrix
;
699 void ImpSdrPdfImport::ImportText(std::unique_ptr
<vcl::pdf::PDFiumPageObject
> const& pPageObject
,
700 std::unique_ptr
<vcl::pdf::PDFiumTextPage
> const& pTextPage
,
701 int /*nPageObjectIndex*/)
703 basegfx::B2DRectangle aTextRect
= pPageObject
->getBounds();
704 basegfx::B2DHomMatrix aMatrix
= pPageObject
->getMatrix();
706 basegfx::B2DHomMatrix
aTextMatrix(maCurrentMatrix
);
708 aTextRect
*= aTextMatrix
;
709 const tools::Rectangle aRect
= PointsToLogic(aTextRect
.getMinX(), aTextRect
.getMaxX(),
710 aTextRect
.getMinY(), aTextRect
.getMaxY());
712 OUString sText
= pPageObject
->getText(pTextPage
);
714 const double dFontSize
= pPageObject
->getFontSize();
715 double dFontSizeH
= fabs(std::hypot(aMatrix
.a(), aMatrix
.c()) * dFontSize
);
716 double dFontSizeV
= fabs(std::hypot(aMatrix
.b(), aMatrix
.d()) * dFontSize
);
718 dFontSizeH
= convertPointToMm100(dFontSizeH
);
719 dFontSizeV
= convertPointToMm100(dFontSizeV
);
721 const Size
aFontSize(dFontSizeH
, dFontSizeV
);
722 vcl::Font aFnt
= mpVD
->GetFont();
723 if (aFontSize
!= aFnt
.GetFontSize())
725 aFnt
.SetFontSize(aFontSize
);
730 OUString sFontName
= pPageObject
->getFontName();
731 if (!sFontName
.isEmpty() && sFontName
!= aFnt
.GetFamilyName())
733 aFnt
.SetFamilyName(sFontName
);
738 Color
aTextColor(COL_TRANSPARENT
);
741 switch (pPageObject
->getTextRenderMode())
743 case vcl::pdf::PDFTextRenderMode::Fill
:
744 case vcl::pdf::PDFTextRenderMode::FillClip
:
745 case vcl::pdf::PDFTextRenderMode::FillStroke
:
746 case vcl::pdf::PDFTextRenderMode::FillStrokeClip
:
749 case vcl::pdf::PDFTextRenderMode::Stroke
:
750 case vcl::pdf::PDFTextRenderMode::StrokeClip
:
751 case vcl::pdf::PDFTextRenderMode::Unknown
:
753 case vcl::pdf::PDFTextRenderMode::Invisible
:
754 case vcl::pdf::PDFTextRenderMode::Clip
:
760 Color aColor
= bFill
? pPageObject
->getFillColor() : pPageObject
->getStrokeColor();
761 if (aColor
!= COL_TRANSPARENT
)
762 aTextColor
= aColor
.GetRGBColor();
765 if (aTextColor
!= mpVD
->GetTextColor())
767 mpVD
->SetTextColor(aTextColor
);
771 InsertTextObject(aRect
.TopLeft(), aRect
.GetSize(), sText
);
774 void ImpSdrPdfImport::InsertTextObject(const Point
& rPos
, const Size
& rSize
, const OUString
& rStr
)
776 // calc text box size, add 5% to make it fit safely
778 FontMetric
aFontMetric(mpVD
->GetFontMetric());
779 vcl::Font
aFont(mpVD
->GetFont());
780 TextAlign
eAlignment(aFont
.GetAlignment());
782 // sal_Int32 nTextWidth = static_cast<sal_Int32>(mpVD->GetTextWidth(rStr) * mfScaleX);
783 sal_Int32 nTextHeight
= static_cast<sal_Int32
>(mpVD
->GetTextHeight() * mfScaleY
);
785 Point
aPosition(basegfx::fround
<tools::Long
>(rPos
.X() * mfScaleX
+ maOfs
.X()),
786 basegfx::fround
<tools::Long
>(rPos
.Y() * mfScaleY
+ maOfs
.Y()));
787 Size
aSize(basegfx::fround
<tools::Long
>(rSize
.Width() * mfScaleX
),
788 basegfx::fround
<tools::Long
>(rSize
.Height() * mfScaleY
));
790 if (eAlignment
== ALIGN_BASELINE
)
791 aPosition
.AdjustY(basegfx::fround
<tools::Long
>(aFontMetric
.GetAscent() * -mfScaleY
));
792 else if (eAlignment
== ALIGN_BOTTOM
)
793 aPosition
.AdjustY(-nTextHeight
);
795 tools::Rectangle
aTextRect(aPosition
, aSize
);
796 rtl::Reference
<SdrRectObj
> pText
= new SdrRectObj(*mpModel
, SdrObjKind::Text
, aTextRect
);
798 pText
->SetMergedItem(makeSdrTextUpperDistItem(0));
799 pText
->SetMergedItem(makeSdrTextLowerDistItem(0));
800 pText
->SetMergedItem(makeSdrTextRightDistItem(0));
801 pText
->SetMergedItem(makeSdrTextLeftDistItem(0));
803 if (aFont
.GetAverageFontWidth())
805 pText
->ClearMergedItem(SDRATTR_TEXT_AUTOGROWWIDTH
);
806 pText
->SetMergedItem(makeSdrTextAutoGrowHeightItem(false));
807 // don't let the margins eat the space needed for the text
808 pText
->SetMergedItem(SdrTextFitToSizeTypeItem(drawing::TextFitToSizeType_ALLLINES
));
812 pText
->SetMergedItem(makeSdrTextAutoGrowWidthItem(true));
815 pText
->SetLayer(mnLayer
);
816 pText
->NbcSetText(rStr
);
817 SetAttributes(pText
.get(), true);
818 pText
->SetSnapRect(aTextRect
);
820 if (!aFont
.IsTransparent())
822 SfxItemSetFixed
<XATTR_FILL_FIRST
, XATTR_FILL_LAST
> aAttr(*mpFillAttr
->GetPool());
823 aAttr
.Put(XFillStyleItem(drawing::FillStyle_SOLID
));
824 aAttr
.Put(XFillColorItem(OUString(), aFont
.GetFillColor()));
825 pText
->SetMergedItemSet(aAttr
);
827 Degree100 nAngle
= to
<Degree100
>(aFont
.GetOrientation());
829 pText
->SdrAttrObj::NbcRotate(aPosition
, nAngle
);
830 InsertObj(pText
.get(), false);
833 void ImpSdrPdfImport::MapScaling()
835 const size_t nCount(maTmpList
.size());
836 const MapMode
& rMap
= mpVD
->GetMapMode();
837 Point
aMapOrg(rMap
.GetOrigin());
838 bool bMov2(aMapOrg
.X() != 0 || aMapOrg
.Y() != 0);
842 for (size_t i
= mnMapScalingOfs
; i
< nCount
; i
++)
844 SdrObject
* pObj
= maTmpList
[i
].get();
846 pObj
->NbcMove(Size(aMapOrg
.X(), aMapOrg
.Y()));
850 mnMapScalingOfs
= nCount
;
853 void ImpSdrPdfImport::ImportImage(std::unique_ptr
<vcl::pdf::PDFiumPageObject
> const& pPageObject
,
854 int /*nPageObjectIndex*/)
856 std::unique_ptr
<vcl::pdf::PDFiumBitmap
> bitmap
= pPageObject
->getImageBitmap();
859 SAL_WARN("sd.filter", "Failed to get IMAGE");
863 const vcl::pdf::PDFBitmapType format
= bitmap
->getFormat();
864 if (format
== vcl::pdf::PDFBitmapType::Unknown
)
866 SAL_WARN("sd.filter", "Failed to get IMAGE format");
870 const unsigned char* pBuf
= bitmap
->getBuffer();
871 const int nWidth
= bitmap
->getWidth();
872 const int nHeight
= bitmap
->getHeight();
873 const int nStride
= bitmap
->getStride();
874 BitmapEx
aBitmap(Size(nWidth
, nHeight
), vcl::PixelFormat::N24_BPP
);
878 case vcl::pdf::PDFBitmapType::BGR
:
879 ReadRawDIB(aBitmap
, pBuf
, ScanlineFormat::N24BitTcBgr
, nHeight
, nStride
);
881 case vcl::pdf::PDFBitmapType::BGRx
:
882 ReadRawDIB(aBitmap
, pBuf
, ScanlineFormat::N32BitTcBgra
, nHeight
, nStride
);
884 case vcl::pdf::PDFBitmapType::BGRA
:
885 ReadRawDIB(aBitmap
, pBuf
, ScanlineFormat::N32BitTcBgra
, nHeight
, nStride
);
888 SAL_WARN("sd.filter", "Got IMAGE width: " << nWidth
<< ", height: " << nHeight
889 << ", stride: " << nStride
890 << ", format: " << static_cast<int>(format
));
894 basegfx::B2DRectangle aBounds
= pPageObject
->getBounds();
895 float left
= aBounds
.getMinX();
897 float bottom
= aBounds
.getMinY();
898 float right
= aBounds
.getMaxX();
900 float top
= aBounds
.getMaxY();
901 tools::Rectangle aRect
= PointsToLogic(left
, right
, top
, bottom
);
902 aRect
.AdjustRight(1);
903 aRect
.AdjustBottom(1);
905 rtl::Reference
<SdrGrafObj
> pGraf
= new SdrGrafObj(*mpModel
, Graphic(aBitmap
), aRect
);
907 // This action is not creating line and fill, set directly, do not use SetAttributes(..)
908 pGraf
->SetMergedItem(XLineStyleItem(drawing::LineStyle_NONE
));
909 pGraf
->SetMergedItem(XFillStyleItem(drawing::FillStyle_NONE
));
910 InsertObj(pGraf
.get());
913 void ImpSdrPdfImport::ImportPath(std::unique_ptr
<vcl::pdf::PDFiumPageObject
> const& pPageObject
,
914 int /*nPageObjectIndex*/)
916 auto aPathMatrix
= pPageObject
->getMatrix();
918 aPathMatrix
*= maCurrentMatrix
;
920 basegfx::B2DPolyPolygon aPolyPoly
;
921 basegfx::B2DPolygon aPoly
;
922 std::vector
<basegfx::B2DPoint
> aBezier
;
924 const int nSegments
= pPageObject
->getPathSegmentCount();
925 for (int nSegmentIndex
= 0; nSegmentIndex
< nSegments
; ++nSegmentIndex
)
927 auto pPathSegment
= pPageObject
->getPathSegment(nSegmentIndex
);
928 if (pPathSegment
!= nullptr)
930 basegfx::B2DPoint aB2DPoint
= pPathSegment
->getPoint();
931 aB2DPoint
*= aPathMatrix
;
933 const bool bClose
= pPathSegment
->isClosed();
935 aPoly
.setClosed(bClose
); // TODO: Review
937 Point aPoint
= PointsToLogic(aB2DPoint
.getX(), aB2DPoint
.getY());
938 aB2DPoint
.setX(aPoint
.X());
939 aB2DPoint
.setY(aPoint
.Y());
941 const vcl::pdf::PDFSegmentType eSegmentType
= pPathSegment
->getType();
942 switch (eSegmentType
)
944 case vcl::pdf::PDFSegmentType::Lineto
:
945 aPoly
.append(aB2DPoint
);
948 case vcl::pdf::PDFSegmentType::Bezierto
:
949 aBezier
.emplace_back(aB2DPoint
.getX(), aB2DPoint
.getY());
950 if (aBezier
.size() == 3)
952 aPoly
.appendBezierSegment(aBezier
[0], aBezier
[1], aBezier
[2]);
957 case vcl::pdf::PDFSegmentType::Moveto
:
959 if (aPoly
.count() > 0)
961 aPolyPoly
.append(aPoly
, 1);
965 aPoly
.append(aB2DPoint
);
968 case vcl::pdf::PDFSegmentType::Unknown
:
970 SAL_WARN("sd.filter", "Unknown path segment type in PDF: "
971 << static_cast<int>(eSegmentType
));
977 if (aBezier
.size() == 3)
979 aPoly
.appendBezierSegment(aBezier
[0], aBezier
[1], aBezier
[2]);
983 if (aPoly
.count() > 0)
985 aPolyPoly
.append(aPoly
, 1);
989 const basegfx::B2DHomMatrix
aTransform(
990 basegfx::utils::createScaleTranslateB2DHomMatrix(mfScaleX
, mfScaleY
, maOfs
.X(), maOfs
.Y()));
991 aPolyPoly
.transform(aTransform
);
993 float fWidth
= pPageObject
->getStrokeWidth();
994 const double dWidth
= 0.5 * fabs(std::hypot(aPathMatrix
.a(), aPathMatrix
.c()) * fWidth
);
995 mnLineWidth
= convertPointToMm100(dWidth
);
997 vcl::pdf::PDFFillMode nFillMode
= vcl::pdf::PDFFillMode::Alternate
;
998 bool bStroke
= true; // Assume we have to draw, unless told otherwise.
999 if (pPageObject
->getDrawMode(nFillMode
, bStroke
))
1001 if (nFillMode
== vcl::pdf::PDFFillMode::Alternate
)
1002 mpVD
->SetDrawMode(DrawModeFlags::Default
);
1003 else if (nFillMode
== vcl::pdf::PDFFillMode::Winding
)
1004 mpVD
->SetDrawMode(DrawModeFlags::Default
);
1006 mpVD
->SetDrawMode(DrawModeFlags::NoFill
);
1009 mpVD
->SetFillColor(pPageObject
->getFillColor());
1013 mpVD
->SetLineColor(pPageObject
->getStrokeColor());
1016 mpVD
->SetLineColor(COL_TRANSPARENT
);
1018 if (!mbLastObjWasPolyWithoutLine
|| !CheckLastPolyLineAndFillMerge(aPolyPoly
))
1020 rtl::Reference
<SdrPathObj
> pPath
1021 = new SdrPathObj(*mpModel
, SdrObjKind::Polygon
, std::move(aPolyPoly
));
1022 SetAttributes(pPath
.get());
1023 InsertObj(pPath
.get(), false);
1027 Point
ImpSdrPdfImport::PointsToLogic(double x
, double y
) const
1029 y
= correctVertOrigin(y
);
1031 Point
aPos(convertPointToMm100(x
), convertPointToMm100(y
));
1035 tools::Rectangle
ImpSdrPdfImport::PointsToLogic(double left
, double right
, double top
,
1036 double bottom
) const
1038 top
= correctVertOrigin(top
);
1039 bottom
= correctVertOrigin(bottom
);
1041 Point
aPos(convertPointToMm100(left
), convertPointToMm100(top
));
1042 Size
aSize(convertPointToMm100(right
- left
), convertPointToMm100(bottom
- top
));
1044 return tools::Rectangle(aPos
, aSize
);
1047 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */