Get the style color and number just once
[LibreOffice.git] / vcl / source / filter / ipdf / pdfread.cxx
blob584497a9cb77f38444739889e1d0c332e7e95a46
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/.
8 */
10 #include <vcl/pdfread.hxx>
11 #include <pdf/pdfcompat.hxx>
13 #include <pdf/PdfConfig.hxx>
14 #include <vcl/graph.hxx>
15 #include <vcl/BitmapWriteAccess.hxx>
16 #include <unotools/ucbstreamhelper.hxx>
17 #include <unotools/datetime.hxx>
18 #include <tools/UnitConversion.hxx>
20 #include <vcl/filter/PDFiumLibrary.hxx>
21 #include <sal/log.hxx>
23 using namespace com::sun::star;
25 namespace vcl
27 size_t RenderPDFBitmaps(const void* pBuffer, int nSize, std::vector<BitmapEx>& rBitmaps,
28 const size_t nFirstPage, int nPages, const basegfx::B2DTuple* pSizeHint)
30 auto pPdfium = vcl::pdf::PDFiumLibrary::get();
31 if (!pPdfium)
33 return 0;
36 // Load the buffer using pdfium.
37 std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument
38 = pPdfium->openDocument(pBuffer, nSize, OString());
39 if (!pPdfDocument)
40 return 0;
42 static const double fResolutionDPI = vcl::pdf::getDefaultPdfResolutionDpi();
44 const int nPageCount = pPdfDocument->getPageCount();
45 if (nPages <= 0)
46 nPages = nPageCount;
47 const size_t nLastPage = std::min<int>(nPageCount, nFirstPage + nPages) - 1;
48 for (size_t nPageIndex = nFirstPage; nPageIndex <= nLastPage; ++nPageIndex)
50 // Render next page.
51 std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(nPageIndex);
52 if (!pPdfPage)
53 break;
55 // Calculate the bitmap size in points.
56 double nPageWidthPoints = pPdfPage->getWidth();
57 double nPageHeightPoints = pPdfPage->getHeight();
58 if (pSizeHint && pSizeHint->getX() && pSizeHint->getY())
60 // Have a size hint, prefer that over the logic size from the PDF.
61 nPageWidthPoints
62 = o3tl::convert(pSizeHint->getX(), o3tl::Length::mm100, o3tl::Length::pt);
63 nPageHeightPoints
64 = o3tl::convert(pSizeHint->getY(), o3tl::Length::mm100, o3tl::Length::pt);
67 // Returned unit is points, convert that to pixel.
69 int nPageWidth = std::round(vcl::pdf::pointToPixel(nPageWidthPoints, fResolutionDPI)
70 * PDF_INSERT_MAGIC_SCALE_FACTOR);
71 int nPageHeight = std::round(vcl::pdf::pointToPixel(nPageHeightPoints, fResolutionDPI)
72 * PDF_INSERT_MAGIC_SCALE_FACTOR);
73 std::unique_ptr<vcl::pdf::PDFiumBitmap> pPdfBitmap
74 = pPdfium->createBitmap(nPageWidth, nPageHeight, /*nAlpha=*/1);
75 if (!pPdfBitmap)
76 break;
78 bool bTransparent = pPdfPage->hasTransparency();
79 if (pSizeHint)
81 // This is the PDF-in-EMF case: force transparency, even in case pdfium would tell us
82 // the PDF is not transparent.
83 bTransparent = true;
85 const sal_uInt32 nColor = bTransparent ? 0x00000000 : 0xFFFFFFFF;
86 pPdfBitmap->fillRect(0, 0, nPageWidth, nPageHeight, nColor);
87 pPdfBitmap->renderPageBitmap(pPdfDocument.get(), pPdfPage.get(), /*nStartX=*/0,
88 /*nStartY=*/0, nPageWidth, nPageHeight);
89 BitmapEx aBitmapEx = pPdfBitmap->createBitmapFromBuffer();
91 if (bTransparent)
93 rBitmaps.emplace_back(std::move(aBitmapEx));
95 else
97 rBitmaps.emplace_back(aBitmapEx.GetBitmap());
101 return rBitmaps.size();
104 bool importPdfVectorGraphicData(SvStream& rStream,
105 std::shared_ptr<VectorGraphicData>& rVectorGraphicData)
107 BinaryDataContainer aDataContainer = vcl::pdf::createBinaryDataContainer(rStream);
108 if (aDataContainer.isEmpty())
110 SAL_WARN("vcl.filter", "ImportPDF: empty PDF data array");
111 return false;
114 rVectorGraphicData
115 = std::make_shared<VectorGraphicData>(aDataContainer, VectorGraphicDataType::Pdf);
117 return true;
120 bool ImportPDF(SvStream& rStream, Graphic& rGraphic)
122 std::shared_ptr<VectorGraphicData> pVectorGraphicData;
123 if (!importPdfVectorGraphicData(rStream, pVectorGraphicData))
124 return false;
125 rGraphic = Graphic(pVectorGraphicData);
126 return true;
129 namespace
131 basegfx::B2DPoint convertFromPDFInternalToHMM(basegfx::B2DPoint const& rInputPoint,
132 basegfx::B2DSize const& rPageSize)
134 double x = convertPointToMm100(rInputPoint.getX());
135 double y = convertPointToMm100(rPageSize.getHeight() - rInputPoint.getY());
136 return { x, y };
139 std::vector<PDFGraphicAnnotation>
140 findAnnotations(const std::unique_ptr<vcl::pdf::PDFiumPage>& pPage, basegfx::B2DSize aPageSize)
142 std::vector<PDFGraphicAnnotation> aPDFGraphicAnnotations;
143 if (!pPage)
145 return aPDFGraphicAnnotations;
148 for (int nAnnotation = 0; nAnnotation < pPage->getAnnotationCount(); nAnnotation++)
150 auto pAnnotation = pPage->getAnnotation(nAnnotation);
151 if (pAnnotation)
153 auto eSubtype = pAnnotation->getSubType();
155 if (eSubtype == vcl::pdf::PDFAnnotationSubType::Text
156 || eSubtype == vcl::pdf::PDFAnnotationSubType::FreeText
157 || eSubtype == vcl::pdf::PDFAnnotationSubType::Polygon
158 || eSubtype == vcl::pdf::PDFAnnotationSubType::Circle
159 || eSubtype == vcl::pdf::PDFAnnotationSubType::Square
160 || eSubtype == vcl::pdf::PDFAnnotationSubType::Ink
161 || eSubtype == vcl::pdf::PDFAnnotationSubType::Highlight
162 || eSubtype == vcl::pdf::PDFAnnotationSubType::Line
163 || eSubtype == vcl::pdf::PDFAnnotationSubType::Stamp)
165 basegfx::B2DRectangle rRectangle = pAnnotation->getRectangle();
166 basegfx::B2DRectangle rRectangleHMM(
167 convertPointToMm100(rRectangle.getMinX()),
168 convertPointToMm100(aPageSize.getHeight() - rRectangle.getMinY()),
169 convertPointToMm100(rRectangle.getMaxX()),
170 convertPointToMm100(aPageSize.getHeight() - rRectangle.getMaxY()));
172 OUString sDateTimeString
173 = pAnnotation->getString(vcl::pdf::constDictionaryKeyModificationDate);
174 OUString sISO8601String = vcl::pdf::convertPdfDateToISO8601(sDateTimeString);
176 css::util::DateTime aDateTime;
177 if (!sISO8601String.isEmpty())
179 utl::ISO8601parseDateTime(sISO8601String, aDateTime);
182 Color aColor = pAnnotation->getColor();
184 aPDFGraphicAnnotations.emplace_back();
186 auto& rPDFGraphicAnnotation = aPDFGraphicAnnotations.back();
187 rPDFGraphicAnnotation.maRectangle = rRectangleHMM;
188 rPDFGraphicAnnotation.maAuthor
189 = pAnnotation->getString(vcl::pdf::constDictionaryKeyTitle);
190 rPDFGraphicAnnotation.maText
191 = pAnnotation->getString(vcl::pdf::constDictionaryKeyContents);
192 rPDFGraphicAnnotation.maDateTime = aDateTime;
193 rPDFGraphicAnnotation.meSubType = eSubtype;
194 rPDFGraphicAnnotation.maColor = aColor;
196 if (eSubtype == vcl::pdf::PDFAnnotationSubType::Polygon)
198 auto const aVertices = pAnnotation->getVertices();
199 if (!aVertices.empty())
201 auto pMarker = std::make_shared<vcl::pdf::PDFAnnotationMarkerPolygon>();
202 rPDFGraphicAnnotation.mpMarker = pMarker;
203 for (auto const& rVertex : aVertices)
205 auto aPoint = convertFromPDFInternalToHMM(rVertex, aPageSize);
206 pMarker->maPolygon.append(aPoint);
208 pMarker->maPolygon.setClosed(true);
209 pMarker->mnWidth = convertPointToMm100(pAnnotation->getBorderWidth());
210 if (pAnnotation->hasKey(vcl::pdf::constDictionaryKeyInteriorColor))
211 pMarker->maFillColor = pAnnotation->getInteriorColor();
214 else if (eSubtype == vcl::pdf::PDFAnnotationSubType::Square)
216 auto pMarker = std::make_shared<vcl::pdf::PDFAnnotationMarkerSquare>();
217 rPDFGraphicAnnotation.mpMarker = pMarker;
218 pMarker->mnWidth = convertPointToMm100(pAnnotation->getBorderWidth());
219 if (pAnnotation->hasKey(vcl::pdf::constDictionaryKeyInteriorColor))
220 pMarker->maFillColor = pAnnotation->getInteriorColor();
222 else if (eSubtype == vcl::pdf::PDFAnnotationSubType::Circle)
224 auto pMarker = std::make_shared<vcl::pdf::PDFAnnotationMarkerCircle>();
225 rPDFGraphicAnnotation.mpMarker = pMarker;
226 pMarker->mnWidth = convertPointToMm100(pAnnotation->getBorderWidth());
227 if (pAnnotation->hasKey(vcl::pdf::constDictionaryKeyInteriorColor))
228 pMarker->maFillColor = pAnnotation->getInteriorColor();
230 else if (eSubtype == vcl::pdf::PDFAnnotationSubType::Ink)
232 auto const aStrokesList = pAnnotation->getInkStrokes();
233 if (!aStrokesList.empty())
235 auto pMarker = std::make_shared<vcl::pdf::PDFAnnotationMarkerInk>();
236 rPDFGraphicAnnotation.mpMarker = pMarker;
237 for (auto const& rStrokes : aStrokesList)
239 basegfx::B2DPolygon aPolygon;
240 for (auto const& rVertex : rStrokes)
242 auto aPoint = convertFromPDFInternalToHMM(rVertex, aPageSize);
243 aPolygon.append(aPoint);
245 pMarker->maStrokes.push_back(aPolygon);
247 float fWidth = pAnnotation->getBorderWidth();
248 pMarker->mnWidth = convertPointToMm100(fWidth);
249 if (pAnnotation->hasKey(vcl::pdf::constDictionaryKeyInteriorColor))
250 pMarker->maFillColor = pAnnotation->getInteriorColor();
253 else if (eSubtype == vcl::pdf::PDFAnnotationSubType::Highlight)
255 size_t nCount = pAnnotation->getAttachmentPointsCount();
256 if (nCount > 0)
258 auto pMarker = std::make_shared<vcl::pdf::PDFAnnotationMarkerHighlight>(
259 vcl::pdf::PDFTextMarkerType::Highlight);
260 rPDFGraphicAnnotation.mpMarker = pMarker;
261 for (size_t i = 0; i < nCount; ++i)
263 auto aAttachmentPoints = pAnnotation->getAttachmentPoints(i);
264 if (!aAttachmentPoints.empty())
266 basegfx::B2DPolygon aPolygon;
267 aPolygon.setClosed(true);
269 auto aPoint1
270 = convertFromPDFInternalToHMM(aAttachmentPoints[0], aPageSize);
271 aPolygon.append(aPoint1);
272 auto aPoint2
273 = convertFromPDFInternalToHMM(aAttachmentPoints[1], aPageSize);
274 aPolygon.append(aPoint2);
275 auto aPoint3
276 = convertFromPDFInternalToHMM(aAttachmentPoints[3], aPageSize);
277 aPolygon.append(aPoint3);
278 auto aPoint4
279 = convertFromPDFInternalToHMM(aAttachmentPoints[2], aPageSize);
280 aPolygon.append(aPoint4);
282 pMarker->maQuads.push_back(aPolygon);
287 else if (eSubtype == vcl::pdf::PDFAnnotationSubType::Line)
289 auto const aLineGeometry = pAnnotation->getLineGeometry();
290 if (!aLineGeometry.empty())
292 auto pMarker = std::make_shared<vcl::pdf::PDFAnnotationMarkerLine>();
293 rPDFGraphicAnnotation.mpMarker = pMarker;
295 auto aPoint1 = convertFromPDFInternalToHMM(aLineGeometry[0], aPageSize);
296 pMarker->maLineStart = aPoint1;
298 auto aPoint2 = convertFromPDFInternalToHMM(aLineGeometry[1], aPageSize);
299 pMarker->maLineEnd = aPoint2;
301 float fWidth = pAnnotation->getBorderWidth();
302 pMarker->mnWidth = convertPointToMm100(fWidth);
305 else if (eSubtype == vcl::pdf::PDFAnnotationSubType::FreeText)
307 auto pMarker = std::make_shared<vcl::pdf::PDFAnnotationMarkerFreeText>();
308 rPDFGraphicAnnotation.mpMarker = pMarker;
309 if (pAnnotation->hasKey(vcl::pdf::constDictionaryKey_DefaultStyle))
311 pMarker->maDefaultStyle
312 = pAnnotation->getString(vcl::pdf::constDictionaryKey_DefaultStyle);
314 if (pAnnotation->hasKey(vcl::pdf::constDictionaryKey_RichContent))
316 pMarker->maRichContent
317 = pAnnotation->getString(vcl::pdf::constDictionaryKey_RichContent);
320 else if (eSubtype == vcl::pdf::PDFAnnotationSubType::Stamp)
322 auto pMarker = std::make_shared<vcl::pdf::PDFAnnotationMarkerStamp>();
323 rPDFGraphicAnnotation.mpMarker = pMarker;
325 auto nObjects = pAnnotation->getObjectCount();
327 for (int nIndex = 0; nIndex < nObjects; nIndex++)
329 auto pPageObject = pAnnotation->getObject(nIndex);
330 if (pPageObject->getType() == vcl::pdf::PDFPageObjectType::Image)
332 std::unique_ptr<vcl::pdf::PDFiumBitmap> pBitmap
333 = pPageObject->getImageBitmap();
334 if (!pBitmap)
336 continue;
338 pMarker->maBitmapEx = pBitmap->createBitmapFromBuffer();
345 return aPDFGraphicAnnotations;
348 } // end anonymous namespace
350 size_t ImportPDFUnloaded(const OUString& rURL, std::vector<PDFGraphicResult>& rGraphics)
352 std::unique_ptr<SvStream> xStream(
353 ::utl::UcbStreamHelper::CreateStream(rURL, StreamMode::READ | StreamMode::SHARE_DENYNONE));
355 // Save the original PDF stream for later use.
356 BinaryDataContainer aDataContainer = vcl::pdf::createBinaryDataContainer(*xStream);
357 if (aDataContainer.isEmpty())
358 return 0;
360 // Prepare the link with the PDF stream.
361 auto pGfxLink = std::make_shared<GfxLink>(aDataContainer, GfxLinkType::NativePdf);
363 auto pPdfium = vcl::pdf::PDFiumLibrary::get();
364 if (!pPdfium)
366 return 0;
369 // Load the buffer using pdfium.
370 auto pPdfDocument
371 = pPdfium->openDocument(pGfxLink->GetData(), pGfxLink->GetDataSize(), OString());
373 if (!pPdfDocument)
374 return 0;
376 const int nPageCount = pPdfDocument->getPageCount();
377 if (nPageCount <= 0)
378 return 0;
380 for (int nPageIndex = 0; nPageIndex < nPageCount; ++nPageIndex)
382 basegfx::B2DSize aPageSize = pPdfDocument->getPageSize(nPageIndex);
383 if (aPageSize.getWidth() <= 0.0 || aPageSize.getHeight() <= 0.0)
384 continue;
386 // Returned unit is points
388 tools::Long nPageWidth = std::round(convertPointToMm100(aPageSize.getWidth()));
389 tools::Long nPageHeight = std::round(convertPointToMm100(aPageSize.getHeight()));
391 // Create the Graphic with the VectorGraphicDataPtr and link the original PDF stream.
392 // We swap out this Graphic as soon as possible, and a later swap in
393 // actually renders the correct Bitmap on demand.
394 Graphic aGraphic(pGfxLink, nPageIndex);
396 auto pPage = pPdfDocument->openPage(nPageIndex);
398 std::vector<PDFGraphicAnnotation> aPDFGraphicAnnotations
399 = findAnnotations(pPage, aPageSize);
401 rGraphics.emplace_back(std::move(aGraphic), Size(nPageWidth, nPageHeight),
402 aPDFGraphicAnnotations);
405 return rGraphics.size();
409 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */