build fix: no comphelper/profilezone.hxx in this branch
[LibreOffice.git] / vcl / source / filter / ipdf / pdfread.cxx
blobf8754405d0b729929d1addea0b4738b7bd0d21d8
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 "pdfread.hxx"
12 #include <config_features.h>
14 #if HAVE_FEATURE_PDFIUM
15 #ifdef WNT
16 #include <prewin.h>
17 #endif
18 #include <fpdfview.h>
19 #include <fpdf_edit.h>
20 #include <fpdf_save.h>
21 #ifdef WNT
22 #include <postwin.h>
23 #endif
24 #endif
26 #include <vcl/bitmapaccess.hxx>
28 using namespace com::sun::star;
30 namespace
33 #if HAVE_FEATURE_PDFIUM
35 /// Callback class to be used with FPDF_SaveWithVersion().
36 struct CompatibleWriter : public FPDF_FILEWRITE
38 public:
39 CompatibleWriter();
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);
55 return 1;
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;
68 aConfig.version = 2;
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);
80 if (!pPdfDocument)
81 return false;
83 // Render the first page.
84 FPDF_PAGE pPdfPage = FPDF_LoadPage(pPdfDocument, /*page_index=*/0);
85 if (!pPdfPage)
86 return false;
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);
92 if (!pPdfBitmap)
93 return false;
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);
112 rGraphic = aBitmap;
114 FPDFBitmap_Destroy(pPdfBitmap);
115 FPDF_ClosePage(pPdfPage);
116 FPDF_CloseDocument(pPdfDocument);
117 FPDF_DestroyLibrary();
119 return true;
122 /// Decide if PDF data is old enough to be compatible.
123 bool isCompatible(SvStream& rInStream)
125 // %PDF-x.y
126 sal_uInt8 aFirstBytes[8];
127 rInStream.Seek(STREAM_SEEK_TO_BEGIN);
128 sal_uLong nRead = rInStream.ReadBytes(aFirstBytes, 8);
129 if (nRead < 8)
130 return false;
132 if ((aFirstBytes[0] != '%' || aFirstBytes[1] != 'P' || aFirstBytes[2] != 'D' || aFirstBytes[3] != 'F' || aFirstBytes[4] != '-'))
133 return false;
135 sal_Int32 nMajor = OString(aFirstBytes[5]).toInt32();
136 sal_Int32 nMinor = OString(aFirstBytes[7]).toInt32();
137 if (nMajor > 1 || (nMajor == 1 && nMinor > 4))
138 return false;
140 return true;
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);
149 if (bCompatible)
150 // Not converting.
151 rOutStream.WriteStream(rInStream);
152 else
154 // Downconvert to PDF-1.4.
155 FPDF_LIBRARY_CONFIG aConfig;
156 aConfig.version = 2;
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);
168 if (!pPdfDocument)
169 return false;
171 CompatibleWriter aWriter;
172 // 14 means PDF-1.4.
173 if (!FPDF_SaveWithVersion(pPdfDocument, &aWriter, 0, 14))
174 return false;
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();
185 #else
186 bool generatePreview(SvStream& rStream, Graphic& rGraphic)
188 (void)rStream;
189 (void)rGraphic;
191 return true;
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
204 namespace vcl
207 bool ImportPDF(SvStream& rStream, Graphic& rGraphic)
209 // Get the preview of the first page.
210 if (!generatePreview(rStream, rGraphic))
211 return false;
213 // Save the original PDF stream for later use.
214 SvMemoryStream aMemoryStream;
215 if (!getCompatibleStream(rStream, aMemoryStream))
216 return false;
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);
224 return true;
229 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */