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 "pdfread.hxx"
12 #include <config_features.h>
14 #if HAVE_FEATURE_PDFIUM
19 #include <fpdf_edit.h>
20 #include <fpdf_save.h>
26 #include <vcl/bitmapaccess.hxx>
28 using namespace com::sun::star
;
33 #if HAVE_FEATURE_PDFIUM
35 /// Callback class to be used with FPDF_SaveWithVersion().
36 struct CompatibleWriter
: public FPDF_FILEWRITE
40 static int WriteBlockCallback(FPDF_FILEWRITE
* pFileWrite
, const void* pData
, unsigned long nSize
);
42 SvMemoryStream m_aStream
;
45 CompatibleWriter::CompatibleWriter()
47 FPDF_FILEWRITE::version
= 1;
48 FPDF_FILEWRITE::WriteBlock
= CompatibleWriter::WriteBlockCallback
;
51 int CompatibleWriter::WriteBlockCallback(FPDF_FILEWRITE
* pFileWrite
, const void* pData
, unsigned long nSize
)
53 auto pImpl
= static_cast<CompatibleWriter
*>(pFileWrite
);
54 pImpl
->m_aStream
.WriteBytes(pData
, nSize
);
58 /// Convert to inch, then assume 96 DPI.
59 double pointToPixel(double fPoint
)
61 return fPoint
/ 72 * 96;
64 /// Does PDF to bitmap conversion using pdfium.
65 bool generatePreview(SvStream
& rStream
, Graphic
& rGraphic
)
67 FPDF_LIBRARY_CONFIG aConfig
;
69 aConfig
.m_pUserFontPaths
= nullptr;
70 aConfig
.m_pIsolate
= nullptr;
71 aConfig
.m_v8EmbedderSlot
= 0;
72 FPDF_InitLibraryWithConfig(&aConfig
);
74 // Read input into a buffer.
75 SvMemoryStream aInBuffer
;
76 aInBuffer
.WriteStream(rStream
);
78 // Load the buffer using pdfium.
79 FPDF_DOCUMENT pPdfDocument
= FPDF_LoadMemDocument(aInBuffer
.GetData(), aInBuffer
.GetSize(), /*password=*/nullptr);
83 // Render the first page.
84 FPDF_PAGE pPdfPage
= FPDF_LoadPage(pPdfDocument
, /*page_index=*/0);
88 // Returned unit is points, convert that to pixel.
89 size_t nPageWidth
= pointToPixel(FPDF_GetPageWidth(pPdfPage
));
90 size_t nPageHeight
= pointToPixel(FPDF_GetPageHeight(pPdfPage
));
91 FPDF_BITMAP pPdfBitmap
= FPDFBitmap_Create(nPageWidth
, nPageHeight
, /*alpha=*/1);
95 FPDF_DWORD nColor
= FPDFPage_HasTransparency(pPdfPage
) ? 0x00000000 : 0xFFFFFFFF;
96 FPDFBitmap_FillRect(pPdfBitmap
, 0, 0, nPageWidth
, nPageHeight
, nColor
);
97 FPDF_RenderPageBitmap(pPdfBitmap
, pPdfPage
, /*start_x=*/0, /*start_y=*/0, nPageWidth
, nPageHeight
, /*rotate=*/0, /*flags=*/0);
99 // Save the buffer as a bitmap.
100 Bitmap
aBitmap(Size(nPageWidth
, nPageHeight
), 24);
102 Bitmap::ScopedWriteAccess
pWriteAccess(aBitmap
);
103 auto pPdfBuffer
= static_cast<ConstScanline
>(FPDFBitmap_GetBuffer(pPdfBitmap
));
104 for (size_t nRow
= 0; nRow
< nPageHeight
; ++nRow
)
106 int nStride
= FPDFBitmap_GetStride(pPdfBitmap
);
107 ConstScanline pPdfLine
= pPdfBuffer
+ (nStride
* nRow
);
108 // pdfium byte order is BGRA.
109 pWriteAccess
->CopyScanline(nRow
, pPdfLine
, ScanlineFormat::N32BitTcBgra
, nStride
);
114 FPDFBitmap_Destroy(pPdfBitmap
);
115 FPDF_ClosePage(pPdfPage
);
116 FPDF_CloseDocument(pPdfDocument
);
117 FPDF_DestroyLibrary();
122 /// Decide if PDF data is old enough to be compatible.
123 bool isCompatible(SvStream
& rInStream
)
126 sal_uInt8 aFirstBytes
[8];
127 rInStream
.Seek(STREAM_SEEK_TO_BEGIN
);
128 sal_uLong nRead
= rInStream
.ReadBytes(aFirstBytes
, 8);
132 if ((aFirstBytes
[0] != '%' || aFirstBytes
[1] != 'P' || aFirstBytes
[2] != 'D' || aFirstBytes
[3] != 'F' || aFirstBytes
[4] != '-'))
135 sal_Int32 nMajor
= OString(aFirstBytes
[5]).toInt32();
136 sal_Int32 nMinor
= OString(aFirstBytes
[7]).toInt32();
137 if (nMajor
> 1 || (nMajor
== 1 && nMinor
> 4))
143 /// Takes care of transparently downgrading the version of the PDF stream in
144 /// case it's too new for our PDF export.
145 bool getCompatibleStream(SvStream
& rInStream
, SvStream
& rOutStream
)
147 bool bCompatible
= isCompatible(rInStream
);
148 rInStream
.Seek(STREAM_SEEK_TO_BEGIN
);
151 rOutStream
.WriteStream(rInStream
);
154 // Downconvert to PDF-1.4.
155 FPDF_LIBRARY_CONFIG aConfig
;
157 aConfig
.m_pUserFontPaths
= nullptr;
158 aConfig
.m_pIsolate
= nullptr;
159 aConfig
.m_v8EmbedderSlot
= 0;
160 FPDF_InitLibraryWithConfig(&aConfig
);
162 // Read input into a buffer.
163 SvMemoryStream aInBuffer
;
164 aInBuffer
.WriteStream(rInStream
);
166 // Load the buffer using pdfium.
167 FPDF_DOCUMENT pPdfDocument
= FPDF_LoadMemDocument(aInBuffer
.GetData(), aInBuffer
.GetSize(), /*password=*/nullptr);
171 CompatibleWriter aWriter
;
173 if (!FPDF_SaveWithVersion(pPdfDocument
, &aWriter
, 0, 14))
176 FPDF_CloseDocument(pPdfDocument
);
177 FPDF_DestroyLibrary();
179 aWriter
.m_aStream
.Seek(STREAM_SEEK_TO_BEGIN
);
180 rOutStream
.WriteStream(aWriter
.m_aStream
);
183 return rOutStream
.good();
186 bool generatePreview(SvStream
& rStream
, Graphic
& rGraphic
)
194 bool getCompatibleStream(SvStream
& rInStream
, SvStream
& rOutStream
)
196 rInStream
.Seek(STREAM_SEEK_TO_BEGIN
);
197 rOutStream
.WriteStream(rInStream
);
198 return rOutStream
.good();
200 #endif // HAVE_FEATURE_PDFIUM
207 bool ImportPDF(SvStream
& rStream
, Graphic
& rGraphic
)
209 // Get the preview of the first page.
210 if (!generatePreview(rStream
, rGraphic
))
213 // Save the original PDF stream for later use.
214 SvMemoryStream aMemoryStream
;
215 if (!getCompatibleStream(rStream
, aMemoryStream
))
218 aMemoryStream
.Seek(STREAM_SEEK_TO_END
);
219 uno::Sequence
<sal_Int8
> aPdfData(aMemoryStream
.Tell());
220 aMemoryStream
.Seek(STREAM_SEEK_TO_BEGIN
);
221 aMemoryStream
.ReadBytes(aPdfData
.getArray(), aPdfData
.getLength());
222 rGraphic
.setPdfData(aPdfData
);
229 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */