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/.
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
;
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();
36 // Load the buffer using pdfium.
37 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
38 = pPdfium
->openDocument(pBuffer
, nSize
, OString());
42 static const double fResolutionDPI
= vcl::pdf::getDefaultPdfResolutionDpi();
44 const int nPageCount
= pPdfDocument
->getPageCount();
47 const size_t nLastPage
= std::min
<int>(nPageCount
, nFirstPage
+ nPages
) - 1;
48 for (size_t nPageIndex
= nFirstPage
; nPageIndex
<= nLastPage
; ++nPageIndex
)
51 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage
= pPdfDocument
->openPage(nPageIndex
);
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.
62 = o3tl::convert(pSizeHint
->getX(), o3tl::Length::mm100
, o3tl::Length::pt
);
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);
78 bool bTransparent
= pPdfPage
->hasTransparency();
81 // This is the PDF-in-EMF case: force transparency, even in case pdfium would tell us
82 // the PDF is not transparent.
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();
93 rBitmaps
.emplace_back(std::move(aBitmapEx
));
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");
115 = std::make_shared
<VectorGraphicData
>(aDataContainer
, VectorGraphicDataType::Pdf
);
120 bool ImportPDF(SvStream
& rStream
, Graphic
& rGraphic
)
122 std::shared_ptr
<VectorGraphicData
> pVectorGraphicData
;
123 if (!importPdfVectorGraphicData(rStream
, pVectorGraphicData
))
125 rGraphic
= Graphic(pVectorGraphicData
);
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());
139 std::vector
<PDFGraphicAnnotation
>
140 findAnnotations(const std::unique_ptr
<vcl::pdf::PDFiumPage
>& pPage
, basegfx::B2DSize aPageSize
)
142 std::vector
<PDFGraphicAnnotation
> aPDFGraphicAnnotations
;
145 return aPDFGraphicAnnotations
;
148 for (int nAnnotation
= 0; nAnnotation
< pPage
->getAnnotationCount(); nAnnotation
++)
150 auto pAnnotation
= pPage
->getAnnotation(nAnnotation
);
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();
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);
270 = convertFromPDFInternalToHMM(aAttachmentPoints
[0], aPageSize
);
271 aPolygon
.append(aPoint1
);
273 = convertFromPDFInternalToHMM(aAttachmentPoints
[1], aPageSize
);
274 aPolygon
.append(aPoint2
);
276 = convertFromPDFInternalToHMM(aAttachmentPoints
[3], aPageSize
);
277 aPolygon
.append(aPoint3
);
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();
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())
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();
369 // Load the buffer using pdfium.
371 = pPdfium
->openDocument(pGfxLink
->GetData(), pGfxLink
->GetDataSize(), OString());
376 const int nPageCount
= pPdfDocument
->getPageCount();
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)
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: */