summaryrefslogtreecommitdiffstats
path: root/vcl/source/filter/ipdf/pdfread.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'vcl/source/filter/ipdf/pdfread.cxx')
-rw-r--r--vcl/source/filter/ipdf/pdfread.cxx399
1 files changed, 399 insertions, 0 deletions
diff --git a/vcl/source/filter/ipdf/pdfread.cxx b/vcl/source/filter/ipdf/pdfread.cxx
new file mode 100644
index 000000000..c6bc4fd5b
--- /dev/null
+++ b/vcl/source/filter/ipdf/pdfread.cxx
@@ -0,0 +1,399 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <vcl/pdfread.hxx>
+#include <pdf/pdfcompat.hxx>
+
+#include <pdf/PdfConfig.hxx>
+#include <vcl/graph.hxx>
+#include <bitmap/BitmapWriteAccess.hxx>
+#include <unotools/ucbstreamhelper.hxx>
+#include <unotools/datetime.hxx>
+
+#include <vcl/filter/PDFiumLibrary.hxx>
+#include <sal/log.hxx>
+#include <o3tl/string_view.hxx>
+
+using namespace com::sun::star;
+
+namespace vcl
+{
+size_t RenderPDFBitmaps(const void* pBuffer, int nSize, std::vector<BitmapEx>& rBitmaps,
+ const size_t nFirstPage, int nPages, const basegfx::B2DTuple* pSizeHint)
+{
+ auto pPdfium = vcl::pdf::PDFiumLibrary::get();
+ if (!pPdfium)
+ {
+ return 0;
+ }
+
+ // Load the buffer using pdfium.
+ std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument
+ = pPdfium->openDocument(pBuffer, nSize, OString());
+ if (!pPdfDocument)
+ return 0;
+
+ static const double fResolutionDPI = vcl::pdf::getDefaultPdfResolutionDpi();
+
+ const int nPageCount = pPdfDocument->getPageCount();
+ if (nPages <= 0)
+ nPages = nPageCount;
+ const size_t nLastPage = std::min<int>(nPageCount, nFirstPage + nPages) - 1;
+ for (size_t nPageIndex = nFirstPage; nPageIndex <= nLastPage; ++nPageIndex)
+ {
+ // Render next page.
+ std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(nPageIndex);
+ if (!pPdfPage)
+ break;
+
+ // Calculate the bitmap size in points.
+ double nPageWidthPoints = pPdfPage->getWidth();
+ double nPageHeightPoints = pPdfPage->getHeight();
+ if (pSizeHint && pSizeHint->getX() && pSizeHint->getY())
+ {
+ // Have a size hint, prefer that over the logic size from the PDF.
+ nPageWidthPoints
+ = o3tl::convert(pSizeHint->getX(), o3tl::Length::mm100, o3tl::Length::pt);
+ nPageHeightPoints
+ = o3tl::convert(pSizeHint->getY(), o3tl::Length::mm100, o3tl::Length::pt);
+ }
+
+ // Returned unit is points, convert that to pixel.
+
+ const size_t nPageWidth
+ = std::round(vcl::pdf::pointToPixel(nPageWidthPoints, fResolutionDPI)
+ * PDF_INSERT_MAGIC_SCALE_FACTOR);
+ const size_t nPageHeight
+ = std::round(vcl::pdf::pointToPixel(nPageHeightPoints, fResolutionDPI)
+ * PDF_INSERT_MAGIC_SCALE_FACTOR);
+ std::unique_ptr<vcl::pdf::PDFiumBitmap> pPdfBitmap
+ = pPdfium->createBitmap(nPageWidth, nPageHeight, /*nAlpha=*/1);
+ if (!pPdfBitmap)
+ break;
+
+ bool bTransparent = pPdfPage->hasTransparency();
+ if (pSizeHint)
+ {
+ // This is the PDF-in-EMF case: force transparency, even in case pdfium would tell us
+ // the PDF is not transparent.
+ bTransparent = true;
+ }
+ const sal_uInt32 nColor = bTransparent ? 0x00000000 : 0xFFFFFFFF;
+ pPdfBitmap->fillRect(0, 0, nPageWidth, nPageHeight, nColor);
+ pPdfBitmap->renderPageBitmap(pPdfDocument.get(), pPdfPage.get(), /*nStartX=*/0,
+ /*nStartY=*/0, nPageWidth, nPageHeight);
+
+ // Save the buffer as a bitmap.
+ Bitmap aBitmap(Size(nPageWidth, nPageHeight), vcl::PixelFormat::N24_BPP);
+ AlphaMask aMask(Size(nPageWidth, nPageHeight));
+ {
+ BitmapScopedWriteAccess pWriteAccess(aBitmap);
+ AlphaScopedWriteAccess pMaskAccess(aMask);
+ ConstScanline pPdfBuffer = pPdfBitmap->getBuffer();
+ const int nStride = pPdfBitmap->getStride();
+ std::vector<sal_uInt8> aScanlineAlpha(nPageWidth);
+ for (size_t nRow = 0; nRow < nPageHeight; ++nRow)
+ {
+ ConstScanline pPdfLine = pPdfBuffer + (nStride * nRow);
+ // pdfium byte order is BGRA.
+ pWriteAccess->CopyScanline(nRow, pPdfLine, ScanlineFormat::N32BitTcBgra, nStride);
+ for (size_t nCol = 0; nCol < nPageWidth; ++nCol)
+ {
+ // Invert alpha (source is alpha, target is opacity).
+ aScanlineAlpha[nCol] = ~pPdfLine[3];
+ pPdfLine += 4;
+ }
+ pMaskAccess->CopyScanline(nRow, aScanlineAlpha.data(), ScanlineFormat::N8BitPal,
+ nPageWidth);
+ }
+ }
+
+ if (bTransparent)
+ {
+ rBitmaps.emplace_back(aBitmap, aMask);
+ }
+ else
+ {
+ rBitmaps.emplace_back(std::move(aBitmap));
+ }
+ }
+
+ return rBitmaps.size();
+}
+
+bool importPdfVectorGraphicData(SvStream& rStream,
+ std::shared_ptr<VectorGraphicData>& rVectorGraphicData)
+{
+ BinaryDataContainer aDataContainer = vcl::pdf::createBinaryDataContainer(rStream);
+ if (aDataContainer.isEmpty())
+ {
+ SAL_WARN("vcl.filter", "ImportPDF: empty PDF data array");
+ return false;
+ }
+
+ rVectorGraphicData
+ = std::make_shared<VectorGraphicData>(aDataContainer, VectorGraphicDataType::Pdf);
+
+ return true;
+}
+
+bool ImportPDF(SvStream& rStream, Graphic& rGraphic)
+{
+ std::shared_ptr<VectorGraphicData> pVectorGraphicData;
+ if (!importPdfVectorGraphicData(rStream, pVectorGraphicData))
+ return false;
+ rGraphic = Graphic(pVectorGraphicData);
+ return true;
+}
+
+namespace
+{
+basegfx::B2DPoint convertFromPDFInternalToHMM(basegfx::B2DSize const& rInputPoint,
+ basegfx::B2DSize const& rPageSize)
+{
+ double x = convertPointToMm100(rInputPoint.getX());
+ double y = convertPointToMm100(rPageSize.getY() - rInputPoint.getY());
+ return { x, y };
+}
+
+std::vector<PDFGraphicAnnotation>
+findAnnotations(const std::unique_ptr<vcl::pdf::PDFiumPage>& pPage, basegfx::B2DSize aPageSize)
+{
+ std::vector<PDFGraphicAnnotation> aPDFGraphicAnnotations;
+ if (!pPage)
+ {
+ return aPDFGraphicAnnotations;
+ }
+
+ for (int nAnnotation = 0; nAnnotation < pPage->getAnnotationCount(); nAnnotation++)
+ {
+ auto pAnnotation = pPage->getAnnotation(nAnnotation);
+ if (pAnnotation)
+ {
+ auto eSubtype = pAnnotation->getSubType();
+
+ if (eSubtype == vcl::pdf::PDFAnnotationSubType::Text
+ || eSubtype == vcl::pdf::PDFAnnotationSubType::Polygon
+ || eSubtype == vcl::pdf::PDFAnnotationSubType::Circle
+ || eSubtype == vcl::pdf::PDFAnnotationSubType::Square
+ || eSubtype == vcl::pdf::PDFAnnotationSubType::Ink
+ || eSubtype == vcl::pdf::PDFAnnotationSubType::Highlight
+ || eSubtype == vcl::pdf::PDFAnnotationSubType::Line)
+ {
+ OUString sAuthor = pAnnotation->getString(vcl::pdf::constDictionaryKeyTitle);
+ OUString sText = pAnnotation->getString(vcl::pdf::constDictionaryKeyContents);
+
+ basegfx::B2DRectangle rRectangle = pAnnotation->getRectangle();
+ basegfx::B2DRectangle rRectangleHMM(
+ convertPointToMm100(rRectangle.getMinX()),
+ convertPointToMm100(aPageSize.getY() - rRectangle.getMinY()),
+ convertPointToMm100(rRectangle.getMaxX()),
+ convertPointToMm100(aPageSize.getY() - rRectangle.getMaxY()));
+
+ OUString sDateTimeString
+ = pAnnotation->getString(vcl::pdf::constDictionaryKeyModificationDate);
+ OUString sISO8601String = vcl::pdf::convertPdfDateToISO8601(sDateTimeString);
+
+ css::util::DateTime aDateTime;
+ if (!sISO8601String.isEmpty())
+ {
+ utl::ISO8601parseDateTime(sISO8601String, aDateTime);
+ }
+
+ Color aColor = pAnnotation->getColor();
+
+ aPDFGraphicAnnotations.emplace_back();
+
+ auto& rPDFGraphicAnnotation = aPDFGraphicAnnotations.back();
+ rPDFGraphicAnnotation.maRectangle = rRectangleHMM;
+ rPDFGraphicAnnotation.maAuthor = sAuthor;
+ rPDFGraphicAnnotation.maText = sText;
+ rPDFGraphicAnnotation.maDateTime = aDateTime;
+ rPDFGraphicAnnotation.meSubType = eSubtype;
+ rPDFGraphicAnnotation.maColor = aColor;
+
+ if (eSubtype == vcl::pdf::PDFAnnotationSubType::Polygon)
+ {
+ auto const& rVertices = pAnnotation->getVertices();
+ if (!rVertices.empty())
+ {
+ auto pMarker = std::make_shared<vcl::pdf::PDFAnnotationMarkerPolygon>();
+ rPDFGraphicAnnotation.mpMarker = pMarker;
+ for (auto const& rVertex : rVertices)
+ {
+ auto aPoint = convertFromPDFInternalToHMM(rVertex, aPageSize);
+ pMarker->maPolygon.append(aPoint);
+ }
+ pMarker->maPolygon.setClosed(true);
+ pMarker->mnWidth = convertPointToMm100(pAnnotation->getBorderWidth());
+ if (pAnnotation->hasKey(vcl::pdf::constDictionaryKeyInteriorColor))
+ pMarker->maFillColor = pAnnotation->getInteriorColor();
+ }
+ }
+ else if (eSubtype == vcl::pdf::PDFAnnotationSubType::Square)
+ {
+ auto pMarker = std::make_shared<vcl::pdf::PDFAnnotationMarkerSquare>();
+ rPDFGraphicAnnotation.mpMarker = pMarker;
+ pMarker->mnWidth = convertPointToMm100(pAnnotation->getBorderWidth());
+ if (pAnnotation->hasKey(vcl::pdf::constDictionaryKeyInteriorColor))
+ pMarker->maFillColor = pAnnotation->getInteriorColor();
+ }
+ else if (eSubtype == vcl::pdf::PDFAnnotationSubType::Circle)
+ {
+ auto pMarker = std::make_shared<vcl::pdf::PDFAnnotationMarkerCircle>();
+ rPDFGraphicAnnotation.mpMarker = pMarker;
+ pMarker->mnWidth = convertPointToMm100(pAnnotation->getBorderWidth());
+ if (pAnnotation->hasKey(vcl::pdf::constDictionaryKeyInteriorColor))
+ pMarker->maFillColor = pAnnotation->getInteriorColor();
+ }
+ else if (eSubtype == vcl::pdf::PDFAnnotationSubType::Ink)
+ {
+ auto const& rStrokesList = pAnnotation->getInkStrokes();
+ if (!rStrokesList.empty())
+ {
+ auto pMarker = std::make_shared<vcl::pdf::PDFAnnotationMarkerInk>();
+ rPDFGraphicAnnotation.mpMarker = pMarker;
+ for (auto const& rStrokes : rStrokesList)
+ {
+ basegfx::B2DPolygon aPolygon;
+ for (auto const& rVertex : rStrokes)
+ {
+ auto aPoint = convertFromPDFInternalToHMM(rVertex, aPageSize);
+ aPolygon.append(aPoint);
+ }
+ pMarker->maStrokes.push_back(aPolygon);
+ }
+ float fWidth = pAnnotation->getBorderWidth();
+ pMarker->mnWidth = convertPointToMm100(fWidth);
+ if (pAnnotation->hasKey(vcl::pdf::constDictionaryKeyInteriorColor))
+ pMarker->maFillColor = pAnnotation->getInteriorColor();
+ }
+ }
+ else if (eSubtype == vcl::pdf::PDFAnnotationSubType::Highlight)
+ {
+ size_t nCount = pAnnotation->getAttachmentPointsCount();
+ if (nCount > 0)
+ {
+ auto pMarker = std::make_shared<vcl::pdf::PDFAnnotationMarkerHighlight>(
+ vcl::pdf::PDFTextMarkerType::Highlight);
+ rPDFGraphicAnnotation.mpMarker = pMarker;
+ for (size_t i = 0; i < nCount; ++i)
+ {
+ auto aAttachmentPoints = pAnnotation->getAttachmentPoints(i);
+ if (!aAttachmentPoints.empty())
+ {
+ basegfx::B2DPolygon aPolygon;
+ aPolygon.setClosed(true);
+
+ auto aPoint1
+ = convertFromPDFInternalToHMM(aAttachmentPoints[0], aPageSize);
+ aPolygon.append(aPoint1);
+ auto aPoint2
+ = convertFromPDFInternalToHMM(aAttachmentPoints[1], aPageSize);
+ aPolygon.append(aPoint2);
+ auto aPoint3
+ = convertFromPDFInternalToHMM(aAttachmentPoints[3], aPageSize);
+ aPolygon.append(aPoint3);
+ auto aPoint4
+ = convertFromPDFInternalToHMM(aAttachmentPoints[2], aPageSize);
+ aPolygon.append(aPoint4);
+
+ pMarker->maQuads.push_back(aPolygon);
+ }
+ }
+ }
+ }
+ else if (eSubtype == vcl::pdf::PDFAnnotationSubType::Line)
+ {
+ auto const& rLineGeometry = pAnnotation->getLineGeometry();
+ if (!rLineGeometry.empty())
+ {
+ auto pMarker = std::make_shared<vcl::pdf::PDFAnnotationMarkerLine>();
+ rPDFGraphicAnnotation.mpMarker = pMarker;
+
+ auto aPoint1 = convertFromPDFInternalToHMM(rLineGeometry[0], aPageSize);
+ pMarker->maLineStart = aPoint1;
+
+ auto aPoint2 = convertFromPDFInternalToHMM(rLineGeometry[1], aPageSize);
+ pMarker->maLineEnd = aPoint2;
+
+ float fWidth = pAnnotation->getBorderWidth();
+ pMarker->mnWidth = convertPointToMm100(fWidth);
+ }
+ }
+ }
+ }
+ }
+ return aPDFGraphicAnnotations;
+}
+
+} // end anonymous namespace
+
+size_t ImportPDFUnloaded(const OUString& rURL, std::vector<PDFGraphicResult>& rGraphics)
+{
+ std::unique_ptr<SvStream> xStream(
+ ::utl::UcbStreamHelper::CreateStream(rURL, StreamMode::READ | StreamMode::SHARE_DENYNONE));
+
+ // Save the original PDF stream for later use.
+ BinaryDataContainer aDataContainer = vcl::pdf::createBinaryDataContainer(*xStream);
+ if (aDataContainer.isEmpty())
+ return 0;
+
+ // Prepare the link with the PDF stream.
+ auto pGfxLink = std::make_shared<GfxLink>(aDataContainer, GfxLinkType::NativePdf);
+
+ auto pPdfium = vcl::pdf::PDFiumLibrary::get();
+ if (!pPdfium)
+ {
+ return 0;
+ }
+
+ // Load the buffer using pdfium.
+ auto pPdfDocument
+ = pPdfium->openDocument(pGfxLink->GetData(), pGfxLink->GetDataSize(), OString());
+
+ if (!pPdfDocument)
+ return 0;
+
+ const int nPageCount = pPdfDocument->getPageCount();
+ if (nPageCount <= 0)
+ return 0;
+
+ for (int nPageIndex = 0; nPageIndex < nPageCount; ++nPageIndex)
+ {
+ basegfx::B2DSize aPageSize = pPdfDocument->getPageSize(nPageIndex);
+ if (aPageSize.getX() <= 0.0 || aPageSize.getY() <= 0.0)
+ continue;
+
+ // Returned unit is points, convert that to twip
+ // 1 pt = 20 twips
+ constexpr double pointToTwipconversionRatio = 20;
+
+ tools::Long nPageWidth = convertTwipToMm100(aPageSize.getX() * pointToTwipconversionRatio);
+ tools::Long nPageHeight = convertTwipToMm100(aPageSize.getY() * pointToTwipconversionRatio);
+
+ // Create the Graphic with the VectorGraphicDataPtr and link the original PDF stream.
+ // We swap out this Graphic as soon as possible, and a later swap in
+ // actually renders the correct Bitmap on demand.
+ Graphic aGraphic(pGfxLink, nPageIndex);
+
+ auto pPage = pPdfDocument->openPage(nPageIndex);
+
+ std::vector<PDFGraphicAnnotation> aPDFGraphicAnnotations
+ = findAnnotations(pPage, aPageSize);
+
+ rGraphics.emplace_back(std::move(aGraphic), Size(nPageWidth, nPageHeight),
+ aPDFGraphicAnnotations);
+ }
+
+ return rGraphics.size();
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */