LibreOffice Module vcl (master) 1
pdfread.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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 */
9
10#include <vcl/pdfread.hxx>
11#include <pdf/pdfcompat.hxx>
12
13#include <pdf/PdfConfig.hxx>
14#include <vcl/graph.hxx>
17#include <unotools/datetime.hxx>
18
20#include <sal/log.hxx>
21
22using namespace com::sun::star;
23
24namespace vcl
25{
26size_t RenderPDFBitmaps(const void* pBuffer, int nSize, std::vector<BitmapEx>& rBitmaps,
27 const size_t nFirstPage, int nPages, const basegfx::B2DTuple* pSizeHint)
28{
29 auto pPdfium = vcl::pdf::PDFiumLibrary::get();
30 if (!pPdfium)
31 {
32 return 0;
33 }
34
35 // Load the buffer using pdfium.
36 std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument
37 = pPdfium->openDocument(pBuffer, nSize, OString());
38 if (!pPdfDocument)
39 return 0;
40
41 static const double fResolutionDPI = vcl::pdf::getDefaultPdfResolutionDpi();
42
43 const int nPageCount = pPdfDocument->getPageCount();
44 if (nPages <= 0)
45 nPages = nPageCount;
46 const size_t nLastPage = std::min<int>(nPageCount, nFirstPage + nPages) - 1;
47 for (size_t nPageIndex = nFirstPage; nPageIndex <= nLastPage; ++nPageIndex)
48 {
49 // Render next page.
50 std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(nPageIndex);
51 if (!pPdfPage)
52 break;
53
54 // Calculate the bitmap size in points.
55 double nPageWidthPoints = pPdfPage->getWidth();
56 double nPageHeightPoints = pPdfPage->getHeight();
57 if (pSizeHint && pSizeHint->getX() && pSizeHint->getY())
58 {
59 // Have a size hint, prefer that over the logic size from the PDF.
60 nPageWidthPoints
62 nPageHeightPoints
64 }
65
66 // Returned unit is points, convert that to pixel.
67
68 int nPageWidth = std::round(vcl::pdf::pointToPixel(nPageWidthPoints, fResolutionDPI)
70 int nPageHeight = std::round(vcl::pdf::pointToPixel(nPageHeightPoints, fResolutionDPI)
72 std::unique_ptr<vcl::pdf::PDFiumBitmap> pPdfBitmap
73 = pPdfium->createBitmap(nPageWidth, nPageHeight, /*nAlpha=*/1);
74 if (!pPdfBitmap)
75 break;
76
77 bool bTransparent = pPdfPage->hasTransparency();
78 if (pSizeHint)
79 {
80 // This is the PDF-in-EMF case: force transparency, even in case pdfium would tell us
81 // the PDF is not transparent.
82 bTransparent = true;
83 }
84 const sal_uInt32 nColor = bTransparent ? 0x00000000 : 0xFFFFFFFF;
85 pPdfBitmap->fillRect(0, 0, nPageWidth, nPageHeight, nColor);
86 pPdfBitmap->renderPageBitmap(pPdfDocument.get(), pPdfPage.get(), /*nStartX=*/0,
87 /*nStartY=*/0, nPageWidth, nPageHeight);
88
89 // Save the buffer as a bitmap.
90 Bitmap aBitmap(Size(nPageWidth, nPageHeight), vcl::PixelFormat::N24_BPP);
91 AlphaMask aMask(Size(nPageWidth, nPageHeight));
92 {
93 BitmapScopedWriteAccess pWriteAccess(aBitmap);
94 AlphaScopedWriteAccess pMaskAccess(aMask);
95 ConstScanline pPdfBuffer = pPdfBitmap->getBuffer();
96 const int nStride = pPdfBitmap->getStride();
97 std::vector<sal_uInt8> aScanlineAlpha(nPageWidth);
98 for (int nRow = 0; nRow < nPageHeight; ++nRow)
99 {
100 ConstScanline pPdfLine = pPdfBuffer + (nStride * nRow);
101 // pdfium byte order is BGRA.
102 pWriteAccess->CopyScanline(nRow, pPdfLine, ScanlineFormat::N32BitTcBgra, nStride);
103 for (int nCol = 0; nCol < nPageWidth; ++nCol)
104 {
105 aScanlineAlpha[nCol] = pPdfLine[3];
106 pPdfLine += 4;
107 }
108 pMaskAccess->CopyScanline(nRow, aScanlineAlpha.data(), ScanlineFormat::N8BitPal,
109 nPageWidth);
110 }
111 }
112
113 if (bTransparent)
114 {
115 rBitmaps.emplace_back(aBitmap, aMask);
116 }
117 else
118 {
119 rBitmaps.emplace_back(std::move(aBitmap));
120 }
121 }
122
123 return rBitmaps.size();
124}
125
127 std::shared_ptr<VectorGraphicData>& rVectorGraphicData)
128{
130 if (aDataContainer.isEmpty())
131 {
132 SAL_WARN("vcl.filter", "ImportPDF: empty PDF data array");
133 return false;
134 }
135
136 rVectorGraphicData
137 = std::make_shared<VectorGraphicData>(aDataContainer, VectorGraphicDataType::Pdf);
138
139 return true;
140}
141
142bool ImportPDF(SvStream& rStream, Graphic& rGraphic)
143{
144 std::shared_ptr<VectorGraphicData> pVectorGraphicData;
145 if (!importPdfVectorGraphicData(rStream, pVectorGraphicData))
146 return false;
147 rGraphic = Graphic(pVectorGraphicData);
148 return true;
149}
150
151namespace
152{
153basegfx::B2DPoint convertFromPDFInternalToHMM(basegfx::B2DPoint const& rInputPoint,
154 basegfx::B2DSize const& rPageSize)
155{
156 double x = convertPointToMm100(rInputPoint.getX());
157 double y = convertPointToMm100(rPageSize.getHeight() - rInputPoint.getY());
158 return { x, y };
159}
160
161std::vector<PDFGraphicAnnotation>
162findAnnotations(const std::unique_ptr<vcl::pdf::PDFiumPage>& pPage, basegfx::B2DSize aPageSize)
163{
164 std::vector<PDFGraphicAnnotation> aPDFGraphicAnnotations;
165 if (!pPage)
166 {
167 return aPDFGraphicAnnotations;
168 }
169
170 for (int nAnnotation = 0; nAnnotation < pPage->getAnnotationCount(); nAnnotation++)
171 {
172 auto pAnnotation = pPage->getAnnotation(nAnnotation);
173 if (pAnnotation)
174 {
175 auto eSubtype = pAnnotation->getSubType();
176
185 {
186 OUString sAuthor = pAnnotation->getString(vcl::pdf::constDictionaryKeyTitle);
187 OUString sText = pAnnotation->getString(vcl::pdf::constDictionaryKeyContents);
188
189 basegfx::B2DRectangle rRectangle = pAnnotation->getRectangle();
190 basegfx::B2DRectangle rRectangleHMM(
191 convertPointToMm100(rRectangle.getMinX()),
192 convertPointToMm100(aPageSize.getHeight() - rRectangle.getMinY()),
193 convertPointToMm100(rRectangle.getMaxX()),
194 convertPointToMm100(aPageSize.getHeight() - rRectangle.getMaxY()));
195
196 OUString sDateTimeString
197 = pAnnotation->getString(vcl::pdf::constDictionaryKeyModificationDate);
198 OUString sISO8601String = vcl::pdf::convertPdfDateToISO8601(sDateTimeString);
199
200 css::util::DateTime aDateTime;
201 if (!sISO8601String.isEmpty())
202 {
203 utl::ISO8601parseDateTime(sISO8601String, aDateTime);
204 }
205
206 Color aColor = pAnnotation->getColor();
207
208 aPDFGraphicAnnotations.emplace_back();
209
210 auto& rPDFGraphicAnnotation = aPDFGraphicAnnotations.back();
211 rPDFGraphicAnnotation.maRectangle = rRectangleHMM;
212 rPDFGraphicAnnotation.maAuthor = sAuthor;
213 rPDFGraphicAnnotation.maText = sText;
214 rPDFGraphicAnnotation.maDateTime = aDateTime;
215 rPDFGraphicAnnotation.meSubType = eSubtype;
216 rPDFGraphicAnnotation.maColor = aColor;
217
219 {
220 auto const& rVertices = pAnnotation->getVertices();
221 if (!rVertices.empty())
222 {
223 auto pMarker = std::make_shared<vcl::pdf::PDFAnnotationMarkerPolygon>();
224 rPDFGraphicAnnotation.mpMarker = pMarker;
225 for (auto const& rVertex : rVertices)
226 {
227 auto aPoint = convertFromPDFInternalToHMM(rVertex, aPageSize);
228 pMarker->maPolygon.append(aPoint);
229 }
230 pMarker->maPolygon.setClosed(true);
231 pMarker->mnWidth = convertPointToMm100(pAnnotation->getBorderWidth());
232 if (pAnnotation->hasKey(vcl::pdf::constDictionaryKeyInteriorColor))
233 pMarker->maFillColor = pAnnotation->getInteriorColor();
234 }
235 }
236 else if (eSubtype == vcl::pdf::PDFAnnotationSubType::Square)
237 {
238 auto pMarker = std::make_shared<vcl::pdf::PDFAnnotationMarkerSquare>();
239 rPDFGraphicAnnotation.mpMarker = pMarker;
240 pMarker->mnWidth = convertPointToMm100(pAnnotation->getBorderWidth());
241 if (pAnnotation->hasKey(vcl::pdf::constDictionaryKeyInteriorColor))
242 pMarker->maFillColor = pAnnotation->getInteriorColor();
243 }
244 else if (eSubtype == vcl::pdf::PDFAnnotationSubType::Circle)
245 {
246 auto pMarker = std::make_shared<vcl::pdf::PDFAnnotationMarkerCircle>();
247 rPDFGraphicAnnotation.mpMarker = pMarker;
248 pMarker->mnWidth = convertPointToMm100(pAnnotation->getBorderWidth());
249 if (pAnnotation->hasKey(vcl::pdf::constDictionaryKeyInteriorColor))
250 pMarker->maFillColor = pAnnotation->getInteriorColor();
251 }
252 else if (eSubtype == vcl::pdf::PDFAnnotationSubType::Ink)
253 {
254 auto const& rStrokesList = pAnnotation->getInkStrokes();
255 if (!rStrokesList.empty())
256 {
257 auto pMarker = std::make_shared<vcl::pdf::PDFAnnotationMarkerInk>();
258 rPDFGraphicAnnotation.mpMarker = pMarker;
259 for (auto const& rStrokes : rStrokesList)
260 {
261 basegfx::B2DPolygon aPolygon;
262 for (auto const& rVertex : rStrokes)
263 {
264 auto aPoint = convertFromPDFInternalToHMM(rVertex, aPageSize);
265 aPolygon.append(aPoint);
266 }
267 pMarker->maStrokes.push_back(aPolygon);
268 }
269 float fWidth = pAnnotation->getBorderWidth();
270 pMarker->mnWidth = convertPointToMm100(fWidth);
271 if (pAnnotation->hasKey(vcl::pdf::constDictionaryKeyInteriorColor))
272 pMarker->maFillColor = pAnnotation->getInteriorColor();
273 }
274 }
276 {
277 size_t nCount = pAnnotation->getAttachmentPointsCount();
278 if (nCount > 0)
279 {
280 auto pMarker = std::make_shared<vcl::pdf::PDFAnnotationMarkerHighlight>(
282 rPDFGraphicAnnotation.mpMarker = pMarker;
283 for (size_t i = 0; i < nCount; ++i)
284 {
285 auto aAttachmentPoints = pAnnotation->getAttachmentPoints(i);
286 if (!aAttachmentPoints.empty())
287 {
288 basegfx::B2DPolygon aPolygon;
289 aPolygon.setClosed(true);
290
291 auto aPoint1
292 = convertFromPDFInternalToHMM(aAttachmentPoints[0], aPageSize);
293 aPolygon.append(aPoint1);
294 auto aPoint2
295 = convertFromPDFInternalToHMM(aAttachmentPoints[1], aPageSize);
296 aPolygon.append(aPoint2);
297 auto aPoint3
298 = convertFromPDFInternalToHMM(aAttachmentPoints[3], aPageSize);
299 aPolygon.append(aPoint3);
300 auto aPoint4
301 = convertFromPDFInternalToHMM(aAttachmentPoints[2], aPageSize);
302 aPolygon.append(aPoint4);
303
304 pMarker->maQuads.push_back(aPolygon);
305 }
306 }
307 }
308 }
309 else if (eSubtype == vcl::pdf::PDFAnnotationSubType::Line)
310 {
311 auto const& rLineGeometry = pAnnotation->getLineGeometry();
312 if (!rLineGeometry.empty())
313 {
314 auto pMarker = std::make_shared<vcl::pdf::PDFAnnotationMarkerLine>();
315 rPDFGraphicAnnotation.mpMarker = pMarker;
316
317 auto aPoint1 = convertFromPDFInternalToHMM(rLineGeometry[0], aPageSize);
318 pMarker->maLineStart = aPoint1;
319
320 auto aPoint2 = convertFromPDFInternalToHMM(rLineGeometry[1], aPageSize);
321 pMarker->maLineEnd = aPoint2;
322
323 float fWidth = pAnnotation->getBorderWidth();
324 pMarker->mnWidth = convertPointToMm100(fWidth);
325 }
326 }
327 }
328 }
329 }
330 return aPDFGraphicAnnotations;
331}
332
333} // end anonymous namespace
334
335size_t ImportPDFUnloaded(const OUString& rURL, std::vector<PDFGraphicResult>& rGraphics)
336{
337 std::unique_ptr<SvStream> xStream(
338 ::utl::UcbStreamHelper::CreateStream(rURL, StreamMode::READ | StreamMode::SHARE_DENYNONE));
339
340 // Save the original PDF stream for later use.
342 if (aDataContainer.isEmpty())
343 return 0;
344
345 // Prepare the link with the PDF stream.
346 auto pGfxLink = std::make_shared<GfxLink>(aDataContainer, GfxLinkType::NativePdf);
347
348 auto pPdfium = vcl::pdf::PDFiumLibrary::get();
349 if (!pPdfium)
350 {
351 return 0;
352 }
353
354 // Load the buffer using pdfium.
355 auto pPdfDocument
356 = pPdfium->openDocument(pGfxLink->GetData(), pGfxLink->GetDataSize(), OString());
357
358 if (!pPdfDocument)
359 return 0;
360
361 const int nPageCount = pPdfDocument->getPageCount();
362 if (nPageCount <= 0)
363 return 0;
364
365 for (int nPageIndex = 0; nPageIndex < nPageCount; ++nPageIndex)
366 {
367 basegfx::B2DSize aPageSize = pPdfDocument->getPageSize(nPageIndex);
368 if (aPageSize.getWidth() <= 0.0 || aPageSize.getHeight() <= 0.0)
369 continue;
370
371 // Returned unit is points, convert that to twip
372 // 1 pt = 20 twips
373 constexpr double pointToTwipconversionRatio = 20;
374
375 tools::Long nPageWidth
376 = convertTwipToMm100(aPageSize.getWidth() * pointToTwipconversionRatio);
377 tools::Long nPageHeight
378 = convertTwipToMm100(aPageSize.getHeight() * pointToTwipconversionRatio);
379
380 // Create the Graphic with the VectorGraphicDataPtr and link the original PDF stream.
381 // We swap out this Graphic as soon as possible, and a later swap in
382 // actually renders the correct Bitmap on demand.
383 Graphic aGraphic(pGfxLink, nPageIndex);
384
385 auto pPage = pPdfDocument->openPage(nPageIndex);
386
387 std::vector<PDFGraphicAnnotation> aPDFGraphicAnnotations
388 = findAnnotations(pPage, aPageSize);
389
390 rGraphics.emplace_back(std::move(aGraphic), Size(nPageWidth, nPageHeight),
391 aPDFGraphicAnnotations);
392 }
393
394 return rGraphics.size();
395}
396}
397
398/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
const sal_uInt8 * ConstScanline
Definition: Scanline.hxx:27
constexpr auto convertPointToMm100(N n)
constexpr auto convertTwipToMm100(N n)
Reference< XInputStream > xStream
Container for the binary data, whose responsibility is to manage the make it as simple as possible to...
void append(const basegfx::B2DPoint &rPoint, sal_uInt32 nCount)
void setClosed(bool bNew)
TYPE getMaxX() const
TYPE getMinX() const
TYPE getMinY() const
TYPE getMaxY() const
TYPE getWidth() const
TYPE getHeight() const
TYPE getX() const
TYPE getY() const
static std::unique_ptr< SvStream > CreateStream(const OUString &rFileName, StreamMode eOpenMode, css::uno::Reference< css::awt::XWindow > xParentWin=nullptr)
This template handles BitmapAccess the RAII way.
int nCount
float y
float x
#define SAL_WARN(area, stream)
int i
constexpr Point convert(const Point &rPoint, o3tl::Length eFrom, o3tl::Length eTo)
long Long
bool ISO8601parseDateTime(std::u16string_view rString, css::util::DateTime &rDateTime)
double getDefaultPdfResolutionDpi()
Get the default PDF rendering resolution in DPI.
Definition: PdfConfig.cxx:20
double pointToPixel(const double fPoint, const double fResolutionDPI)
Convert to inch, then assume 96 DPI.
Definition: pdfcompat.hxx:21
BinaryDataContainer createBinaryDataContainer(SvStream &rStream)
Definition: pdfcompat.cxx:93
constexpr OStringLiteral constDictionaryKeyInteriorColor
constexpr OStringLiteral constDictionaryKeyTitle
constexpr OStringLiteral constDictionaryKeyContents
constexpr OStringLiteral constDictionaryKeyModificationDate
OUString convertPdfDateToISO8601(std::u16string_view rInput)
Definition: PDFiumTools.cxx:15
size_t RenderPDFBitmaps(const void *pBuffer, int nSize, std::vector< BitmapEx > &rBitmaps, const size_t nFirstPage, int nPages, const basegfx::B2DTuple *pSizeHint)
Fills the rBitmaps vector with rendered pages.
Definition: pdfread.cxx:26
bool ImportPDF(SvStream &rStream, Graphic &rGraphic)
Imports a PDF stream into rGraphic.
Definition: pdfread.cxx:142
bool importPdfVectorGraphicData(SvStream &rStream, std::shared_ptr< VectorGraphicData > &rVectorGraphicData)
Imports a PDF stream as a VectorGraphicData.
Definition: pdfread.cxx:126
constexpr int PDF_INSERT_MAGIC_SCALE_FACTOR
Definition: pdfread.hxx:60
size_t ImportPDFUnloaded(const OUString &rURL, std::vector< PDFGraphicResult > &rGraphics)
Import PDF as Graphic images (1 per page), but not loaded yet.
Definition: pdfread.cxx:335
static std::shared_ptr< PDFium > & get()