tdf#151001: Add support for EXPAND function
[LibreOffice.git] / svx / source / svdraw / svdpdf.cxx
blob6a00bd286ce1b2c9e25566dc35cf1f5262956e3e
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 .
20 #include <svdpdf.hxx>
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())
79 , maScaleRect(rRect)
80 , mnMapScalingOfs(0)
81 , mpModel(&rModel)
82 , mnLayer(nLay)
83 , mnLineWidth(0)
84 , maDash(css::drawing::DashStyle_RECT, 0, 0, 0, 0, 0)
85 , mbMov(false)
86 , mbSize(false)
87 , maOfs(0, 0)
88 , mfScaleX(1.0)
89 , mfScaleY(1.0)
90 , maScaleX(1.0)
91 , maScaleY(1.0)
92 , mbFntDirty(true)
93 , mbLastObjWasPolyWithoutLine(false)
94 , mbNoLine(false)
95 , mbNoFill(false)
96 , mnPageCount(0)
97 , mdPageHeightPts(0)
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());
108 mpTextAttr
109 = std::make_unique<SfxItemSetFixed<EE_ITEMS_START, EE_ITEMS_END>>(rModel.GetItemPool());
111 checkClip();
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;
118 if (!mpPdfDocument)
119 return;
121 mnPageCount = mpPdfDocument->getPageCount();
124 ImpSdrPdfImport::~ImpSdrPdfImport() = default;
126 void ImpSdrPdfImport::DoObjects(SvdProgressInfo* pProgrInfo, sal_uInt32* pActionsToReport,
127 int nPageIndex)
129 const int nPageCount = mpPdfDocument->getPageCount();
130 if (!(nPageCount > 0 && nPageIndex >= 0 && nPageIndex < nPageCount))
131 return;
133 // Render next page.
134 auto pPdfPage = mpPdfDocument->openPage(nPageIndex);
135 if (!pPdfPage)
136 return;
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();
146 if (pProgrInfo)
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))
160 break;
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;
195 mbSize = false;
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());
202 mbSize = true;
205 if (aPageSize.Height() != (maScaleRect.GetHeight() - 1))
207 maScaleY = Fraction(maScaleRect.GetHeight() - 1, aPageSize.Height());
208 mbSize = true;
212 size_t ImpSdrPdfImport::DoImport(SdrObjList& rOL, size_t nInsPos, int nPageNumber,
213 SvdProgressInfo* pProgrInfo)
215 sal_uInt32 nActionsToReport(0);
217 // execute
218 DoObjects(pProgrInfo, &nActionsToReport, nPageNumber);
220 if (pProgrInfo)
222 pProgrInfo->ReportActions(nActionsToReport);
223 nActionsToReport = 0;
226 // MapMode scaling
227 MapScaling();
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
235 if (pProgrInfo)
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);
249 nInsPos++;
251 if (pProgrInfo)
253 nActionsToReport++;
255 if (nActionsToReport >= 32) // update all 32 actions
257 pProgrInfo->ReportInserts(nActionsToReport);
258 nActionsToReport = 0;
263 // report all remaining inserts for the last time
264 if (pProgrInfo)
266 pProgrInfo->ReportInserts(nActionsToReport);
269 return maTmpList.size();
272 void ImpSdrPdfImport::SetAttributes(SdrObject* pObj, bool bForceTextAttr)
274 mbNoLine = false;
275 mbNoFill = false;
276 bool bLine(!bForceTextAttr);
277 bool bFill(!pObj || (pObj->IsClosedObj() && !bForceTextAttr));
278 bool bText(bForceTextAttr || (pObj && pObj->GetOutlinerParaObject()));
280 if (bLine)
282 if (mnLineWidth)
284 mpLineAttr->Put(XLineWidthItem(mnLineWidth));
286 else
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()));
298 else
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));
314 else
316 mpLineAttr->Put(XLineDashItem(OUString(), XDash(css::drawing::DashStyle_RECT)));
319 else
321 mbNoLine = true;
324 if (bFill)
326 if (mpVD->IsFillColor())
328 mpFillAttr->Put(XFillStyleItem(drawing::FillStyle_SOLID));
329 mpFillAttr->Put(XFillColorItem(OUString(), mpVD->GetFillColor()));
331 else
333 mpFillAttr->Put(XFillStyleItem(drawing::FillStyle_NONE));
336 else
338 mbNoFill = true;
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
371 mbFntDirty = false;
374 if (!pObj)
375 return;
377 pObj->SetLayer(mnLayer);
379 if (bLine)
381 pObj->SetMergedItemSet(*mpLineAttr);
384 if (bFill)
386 pObj->SetMergedItemSet(*mpFillAttr);
389 if (bText)
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())
401 if (mbSize)
403 pObj->NbcResize(Point(), maScaleX, maScaleY);
406 if (mbMov)
408 pObj->NbcMove(Size(maOfs.X(), maOfs.Y()));
412 if (isClip())
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
425 while (true)
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))
434 pObj.clear();
435 break;
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
442 break;
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);
448 pObj.clear();
449 if (pConverted)
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()));
467 if (pNewClone)
469 InsertObj(pNewClone.get(), false);
471 else
473 OSL_ENSURE(false, "SdrObject::Clone() failed (!)");
478 break;
481 else
483 BitmapEx aBitmapEx;
485 if (pSdrGrafObj)
487 aBitmapEx = pSdrGrafObj->GetGraphic().GetBitmapEx();
490 pObj.clear();
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,
503 aNewPoly);
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));
545 if (!pObj)
546 return;
548 // #i111954# check object for visibility
549 // used are SdrPathObj, SdrRectObj, SdrCircObj, SdrGrafObj
550 bool bVisible(false);
552 if (pObj->HasLineStyle())
554 bVisible = true;
557 if (!bVisible && pObj->HasFillStyle())
559 bVisible = true;
562 if (!bVisible)
564 SdrTextObj* pTextObj = DynCastSdrTextObj(pObj.get());
566 if (pTextObj && pTextObj->HasText())
568 bVisible = true;
572 if (!bVisible)
574 SdrGrafObj* pGrafObj = dynamic_cast<SdrGrafObj*>(pObj.get());
576 if (pGrafObj)
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
580 // get created
581 bVisible = true;
585 if (bVisible)
587 maTmpList.push_back(pObj);
589 if (dynamic_cast<SdrPathObj*>(pObj.get()))
591 const bool bClosed(pObj->IsClosedObj());
593 mbLastObjWasPolyWithoutLine = mbNoLine && bClosed;
595 else
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);
610 if (pLastPoly)
612 if (pLastPoly->GetPathPoly() == rPolyPolygon)
614 SetAttributes(nullptr);
616 if (!mbNoLine && mbNoFill)
618 pLastPoly->SetMergedItemSet(*mpLineAttr);
620 return true;
626 return false;
629 void ImpSdrPdfImport::checkClip()
631 if (mpVD->IsClipRegion())
633 maClip = mpVD->GetClipRegion().GetAsB2DPolyPolygon();
635 if (isClip())
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)
650 if (!pPageObject)
651 return;
653 const vcl::pdf::PDFPageObjectType ePageObjectType = pPageObject->getType();
654 switch (ePageObjectType)
656 case vcl::pdf::PDFPageObjectType::Text:
657 ImportText(pPageObject, pTextPage, nPageObjectIndex);
658 break;
659 case vcl::pdf::PDFPageObjectType::Path:
660 ImportPath(pPageObject, nPageObjectIndex);
661 break;
662 case vcl::pdf::PDFPageObjectType::Image:
663 ImportImage(pPageObject, nPageObjectIndex);
664 break;
665 case vcl::pdf::PDFPageObjectType::Shading:
666 SAL_WARN("sd.filter", "Got page object SHADING: " << nPageObjectIndex);
667 break;
668 case vcl::pdf::PDFPageObjectType::Form:
669 ImportForm(pPageObject, pTextPage, nPageObjectIndex);
670 break;
671 default:
672 SAL_WARN("sd.filter", "Unknown PDF page object #" << nPageObjectIndex << " of type: "
673 << static_cast<int>(ePageObjectType));
674 break;
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);
726 mpVD->SetFont(aFnt);
727 mbFntDirty = true;
730 OUString sFontName = pPageObject->getFontName();
731 if (!sFontName.isEmpty() && sFontName != aFnt.GetFamilyName())
733 aFnt.SetFamilyName(sFontName);
734 mpVD->SetFont(aFnt);
735 mbFntDirty = true;
738 Color aTextColor(COL_TRANSPARENT);
739 bool bFill = false;
740 bool bUse = true;
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:
747 bFill = true;
748 break;
749 case vcl::pdf::PDFTextRenderMode::Stroke:
750 case vcl::pdf::PDFTextRenderMode::StrokeClip:
751 case vcl::pdf::PDFTextRenderMode::Unknown:
752 break;
753 case vcl::pdf::PDFTextRenderMode::Invisible:
754 case vcl::pdf::PDFTextRenderMode::Clip:
755 bUse = false;
756 break;
758 if (bUse)
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);
768 mbFntDirty = true;
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));
810 else
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());
828 if (nAngle)
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);
840 if (bMov2)
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();
857 if (!bitmap)
859 SAL_WARN("sd.filter", "Failed to get IMAGE");
860 return;
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");
867 return;
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);
876 switch (format)
878 case vcl::pdf::PDFBitmapType::BGR:
879 ReadRawDIB(aBitmap, pBuf, ScanlineFormat::N24BitTcBgr, nHeight, nStride);
880 break;
881 case vcl::pdf::PDFBitmapType::BGRx:
882 ReadRawDIB(aBitmap, pBuf, ScanlineFormat::N32BitTcBgra, nHeight, nStride);
883 break;
884 case vcl::pdf::PDFBitmapType::BGRA:
885 ReadRawDIB(aBitmap, pBuf, ScanlineFormat::N32BitTcBgra, nHeight, nStride);
886 break;
887 default:
888 SAL_WARN("sd.filter", "Got IMAGE width: " << nWidth << ", height: " << nHeight
889 << ", stride: " << nStride
890 << ", format: " << static_cast<int>(format));
891 break;
894 basegfx::B2DRectangle aBounds = pPageObject->getBounds();
895 float left = aBounds.getMinX();
896 // Upside down.
897 float bottom = aBounds.getMinY();
898 float right = aBounds.getMaxX();
899 // Upside down.
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();
934 if (bClose)
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);
946 break;
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]);
953 aBezier.clear();
955 break;
957 case vcl::pdf::PDFSegmentType::Moveto:
958 // New Poly.
959 if (aPoly.count() > 0)
961 aPolyPoly.append(aPoly, 1);
962 aPoly.clear();
965 aPoly.append(aB2DPoint);
966 break;
968 case vcl::pdf::PDFSegmentType::Unknown:
969 default:
970 SAL_WARN("sd.filter", "Unknown path segment type in PDF: "
971 << static_cast<int>(eSegmentType));
972 break;
977 if (aBezier.size() == 3)
979 aPoly.appendBezierSegment(aBezier[0], aBezier[1], aBezier[2]);
980 aBezier.clear();
983 if (aPoly.count() > 0)
985 aPolyPoly.append(aPoly, 1);
986 aPoly.clear();
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);
1005 else
1006 mpVD->SetDrawMode(DrawModeFlags::NoFill);
1009 mpVD->SetFillColor(pPageObject->getFillColor());
1011 if (bStroke)
1013 mpVD->SetLineColor(pPageObject->getStrokeColor());
1015 else
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));
1032 return aPos;
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: */