Version 7.1.7.1, tag libreoffice-7.1.7.1
[LibreOffice.git] / svx / source / svdraw / svdpdf.cxx
blobc7e6f223b10f13dcba5e80019b65e42b7c6e6aff
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 <config_features.h>
24 #if HAVE_FEATURE_PDFIUM
25 #include <fpdfview.h>
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>
33 #include <math.h>
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>
81 namespace
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)
90 : maTmpList()
91 , mpVD(VclPtr<VirtualDevice>::Create())
92 , maScaleRect(rRect)
93 , mnMapScalingOfs(0)
94 , mpModel(&rModel)
95 , mnLayer(nLay)
96 , maOldLineColor()
97 , mnLineWidth(0)
98 , maDash(css::drawing::DashStyle_RECT, 0, 0, 0, 0, 0)
99 , mbMov(false)
100 , mbSize(false)
101 , maOfs(0, 0)
102 , mfScaleX(1.0)
103 , mfScaleY(1.0)
104 , maScaleX(1.0)
105 , maScaleY(1.0)
106 , mbFntDirty(true)
107 , mbLastObjWasPolyWithoutLine(false)
108 , mbNoLine(false)
109 , mbNoFill(false)
110 , maClip()
111 , mnPageCount(0)
112 , mdPageHeightPts(0)
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>{});
125 checkClip();
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);
132 if (!mpPdfDocument)
133 return;
135 mnPageCount = mpPdfDocument->getPageCount();
138 ImpSdrPdfImport::~ImpSdrPdfImport() = default;
140 void ImpSdrPdfImport::DoObjects(SvdProgressInfo* pProgrInfo, sal_uInt32* pActionsToReport,
141 int nPageIndex)
143 const int nPageCount = mpPdfDocument->getPageCount();
144 if (!(nPageCount > 0 && nPageIndex >= 0 && nPageIndex < nPageCount))
145 return;
147 // Render next page.
148 auto pPdfPage = mpPdfDocument->openPage(nPageIndex);
149 if (!pPdfPage)
150 return;
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();
163 if (pProgrInfo)
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))
177 break;
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;
212 mbSize = false;
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());
219 mbSize = true;
222 if (aPageSize.Height() != (maScaleRect.GetHeight() - 1))
224 maScaleY = Fraction(maScaleRect.GetHeight() - 1, aPageSize.Height());
225 mbSize = true;
229 size_t ImpSdrPdfImport::DoImport(SdrObjList& rOL, size_t nInsPos, int nPageNumber,
230 SvdProgressInfo* pProgrInfo)
232 sal_uInt32 nActionsToReport(0);
234 // execute
235 DoObjects(pProgrInfo, &nActionsToReport, nPageNumber);
237 if (pProgrInfo)
239 pProgrInfo->ReportActions(nActionsToReport);
240 nActionsToReport = 0;
243 // MapMode scaling
244 MapScaling();
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
252 if (pProgrInfo)
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);
266 nInsPos++;
268 if (pProgrInfo)
270 nActionsToReport++;
272 if (nActionsToReport >= 32) // update all 32 actions
274 pProgrInfo->ReportInserts(nActionsToReport);
275 nActionsToReport = 0;
280 // report all remaining inserts for the last time
281 if (pProgrInfo)
283 pProgrInfo->ReportInserts(nActionsToReport);
286 return maTmpList.size();
289 void ImpSdrPdfImport::SetAttributes(SdrObject* pObj, bool bForceTextAttr)
291 mbNoLine = false;
292 mbNoFill = false;
293 bool bLine(!bForceTextAttr);
294 bool bFill(!pObj || (pObj->IsClosedObj() && !bForceTextAttr));
295 bool bText(bForceTextAttr || (pObj && pObj->GetOutlinerParaObject()));
297 if (bLine)
299 if (mnLineWidth)
301 mpLineAttr->Put(XLineWidthItem(mnLineWidth));
303 else
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()));
315 else
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));
331 else
333 mpLineAttr->Put(XLineDashItem(OUString(), XDash(css::drawing::DashStyle_RECT)));
336 else
338 mbNoLine = true;
341 if (bFill)
343 if (mpVD->IsFillColor())
345 mpFillAttr->Put(XFillStyleItem(drawing::FillStyle_SOLID));
346 mpFillAttr->Put(XFillColorItem(OUString(), mpVD->GetFillColor()));
348 else
350 mpFillAttr->Put(XFillStyleItem(drawing::FillStyle_NONE));
353 else
355 mbNoFill = true;
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
387 mbFntDirty = false;
390 if (!pObj)
391 return;
393 pObj->SetLayer(mnLayer);
395 if (bLine)
397 pObj->SetMergedItemSet(*mpLineAttr);
400 if (bFill)
402 pObj->SetMergedItemSet(*mpFillAttr);
405 if (bText)
407 pObj->SetMergedItemSet(*mpTextAttr);
408 pObj->SetMergedItem(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_LEFT));
412 void ImpSdrPdfImport::InsertObj(SdrObject* pObj, bool bScale)
414 if (bScale && !maScaleRect.IsEmpty())
416 if (mbSize)
418 pObj->NbcResize(Point(), maScaleX, maScaleY);
421 if (mbMov)
423 pObj->NbcMove(Size(maOfs.X(), maOfs.Y()));
427 if (isClip())
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
440 while (true)
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);
450 break;
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
457 break;
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);
465 if (pConverted)
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()));
483 if (pNewClone)
485 InsertObj(pNewClone, false);
487 else
489 OSL_ENSURE(false, "SdrObject::Clone() failed (!)");
494 break;
497 else
499 BitmapEx aBitmapEx;
501 if (pSdrGrafObj)
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,
518 aNewPoly);
520 pObj->SetLayer(aOldLayer);
521 pObj->SetMergedItemSet(aOldItemSet);
523 if (!!aBitmapEx)
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()))),
545 basegfx::fround(
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));
560 if (!pObj)
561 return;
563 // #i111954# check object for visibility
564 // used are SdrPathObj, SdrRectObj, SdrCircObj, SdrGrafObj
565 bool bVisible(false);
567 if (pObj->HasLineStyle())
569 bVisible = true;
572 if (!bVisible && pObj->HasFillStyle())
574 bVisible = true;
577 if (!bVisible)
579 SdrTextObj* pTextObj = dynamic_cast<SdrTextObj*>(pObj);
581 if (pTextObj && pTextObj->HasText())
583 bVisible = true;
587 if (!bVisible)
589 SdrGrafObj* pGrafObj = dynamic_cast<SdrGrafObj*>(pObj);
591 if (pGrafObj)
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
595 // get created
596 bVisible = true;
600 if (!bVisible)
602 SdrObject::Free(pObj);
604 else
606 maTmpList.push_back(pObj);
608 if (dynamic_cast<SdrPathObj*>(pObj))
610 const bool bClosed(pObj->IsClosedObj());
612 mbLastObjWasPolyWithoutLine = mbNoLine && bClosed;
614 else
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);
629 if (pLastPoly)
631 if (pLastPoly->GetPathPoly() == rPolyPolygon)
633 SetAttributes(nullptr);
635 if (!mbNoLine && mbNoFill)
637 pLastPoly->SetMergedItemSet(*mpLineAttr);
639 return true;
645 return false;
648 void ImpSdrPdfImport::checkClip()
650 if (mpVD->IsClipRegion())
652 maClip = mpVD->GetClipRegion().GetAsB2DPolyPolygon();
654 if (isClip())
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)
669 if (!pPageObject)
670 return;
672 const vcl::pdf::PDFPageObjectType ePageObjectType = pPageObject->getType();
673 switch (ePageObjectType)
675 case vcl::pdf::PDFPageObjectType::Text:
676 ImportText(pPageObject, pTextPage, nPageObjectIndex);
677 break;
678 case vcl::pdf::PDFPageObjectType::Path:
679 ImportPath(pPageObject, nPageObjectIndex);
680 break;
681 case vcl::pdf::PDFPageObjectType::Image:
682 ImportImage(pPageObject, nPageObjectIndex);
683 break;
684 case vcl::pdf::PDFPageObjectType::Shading:
685 SAL_WARN("sd.filter", "Got page object SHADING: " << nPageObjectIndex);
686 break;
687 case vcl::pdf::PDFPageObjectType::Form:
688 ImportForm(pPageObject, pTextPage, nPageObjectIndex);
689 break;
690 default:
691 SAL_WARN("sd.filter", "Unknown PDF page object #" << nPageObjectIndex << " of type: "
692 << static_cast<int>(ePageObjectType));
693 break;
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);
745 mpVD->SetFont(aFnt);
746 mbFntDirty = true;
749 OUString sFontName = pPageObject->getFontName();
750 if (!sFontName.isEmpty() && sFontName != aFnt.GetFamilyName())
752 aFnt.SetFamilyName(sFontName);
753 mpVD->SetFont(aFnt);
754 mbFntDirty = true;
757 Color aTextColor(COL_TRANSPARENT);
758 bool bFill = false;
759 bool bUse = true;
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:
766 bFill = true;
767 break;
768 case FPDF_TEXTRENDERMODE_STROKE:
769 case FPDF_TEXTRENDERMODE_STROKE_CLIP:
770 case FPDF_TEXTRENDERMODE_UNKNOWN:
771 break;
772 case FPDF_TEXTRENDERMODE_INVISIBLE:
773 case FPDF_TEXTRENDERMODE_CLIP:
774 bUse = false;
775 break;
777 if (bUse)
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);
787 mbFntDirty = true;
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));
828 else
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();
846 if (nAngle)
848 nAngle *= 10;
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);
864 if (bMov2)
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();
881 if (!bitmap)
883 SAL_WARN("sd.filter", "Failed to get IMAGE");
884 return;
887 const int format = FPDFBitmap_GetFormat(bitmap->getPointer());
888 if (format == FPDFBitmap_Unknown)
890 SAL_WARN("sd.filter", "Failed to get IMAGE format");
891 return;
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);
901 switch (format)
903 case FPDFBitmap_BGR:
904 ReadRawDIB(aBitmap, pBuf, ScanlineFormat::N24BitTcBgr, nHeight, nStride);
905 break;
906 case FPDFBitmap_BGRx:
907 ReadRawDIB(aBitmap, pBuf, ScanlineFormat::N32BitTcRgba, nHeight, nStride);
908 break;
909 case FPDFBitmap_BGRA:
910 ReadRawDIB(aBitmap, pBuf, ScanlineFormat::N32BitTcBgra, nHeight, nStride);
911 break;
912 default:
913 SAL_WARN("sd.filter", "Got IMAGE width: " << nWidth << ", height: " << nHeight
914 << ", stride: " << nStride
915 << ", format: " << format);
916 break;
919 float left;
920 float bottom;
921 float right;
922 float top;
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));
937 InsertObj(pGraf);
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();
961 if (bClose)
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);
973 break;
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]);
980 aBezier.clear();
982 break;
984 case FPDF_SEGMENT_MOVETO:
985 // New Poly.
986 if (aPoly.count() > 0)
988 aPolyPoly.append(aPoly, 1);
989 aPoly.clear();
992 aPoly.append(aB2DPoint);
993 break;
995 case FPDF_SEGMENT_UNKNOWN:
996 default:
997 SAL_WARN("sd.filter", "Unknown path segment type in PDF: " << nSegmentType);
998 break;
1003 if (aBezier.size() == 3)
1005 aPoly.appendBezierSegment(aBezier[0], aBezier[1], aBezier[2]);
1006 aBezier.clear();
1009 if (aPoly.count() > 0)
1011 aPolyPoly.append(aPoly, 1);
1012 aPoly.clear();
1015 const basegfx::B2DHomMatrix aTransform(
1016 basegfx::utils::createScaleTranslateB2DHomMatrix(mfScaleX, mfScaleY, maOfs.X(), maOfs.Y()));
1017 aPolyPoly.transform(aTransform);
1019 float fWidth = 1;
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);
1032 else
1033 mpVD->SetDrawMode(DrawModeFlags::NoFill);
1036 unsigned int nR;
1037 unsigned int nG;
1038 unsigned int nB;
1039 unsigned int nA;
1040 FPDFPageObj_GetFillColor(pPageObject->getPointer(), &nR, &nG, &nB, &nA);
1041 mpVD->SetFillColor(Color(nR, nG, nB));
1043 if (bStroke)
1045 FPDFPageObj_GetStrokeColor(pPageObject->getPointer(), &nR, &nG, &nB, &nA);
1046 mpVD->SetLineColor(Color(nR, nG, nB));
1048 else
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));
1064 return aPos;
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: */