tdf#108748 generate PDF preview on SwapIn
When including a PDF as an image, it's represented internally as
a Bitmap with additional PDF data. On SwapIn, LibreOffice just
imported the PDF data missing the PDF preview. The Graphic also
gad the wrong image type, which results in a busy loop on master,
with a strange / unhelpful STR_COMCORE_READERROR generated by
SwNoTextFrame::PaintPicture.
This is a workaround to generate the Bitmap on SwapIn, which
will really slow down LibreOffice when importing many PDFs.
I guess the job of generating the PDF previews should probably
be deferred to a thread or a low priority Scheduler task, just
like the general image loading is handled.
Change-Id: I8084e4533995ecddc5b03ef19cb0c6a2dbf60ebd
Reviewed-on: https://gerrit.libreoffice.org/43906
Tested-by: Jenkins <ci@libreoffice.org>
Reviewed-by: Jan-Marek Glogowski <glogow@fbihome.de>
diff --git a/include/tools/stream.hxx b/include/tools/stream.hxx
index 00aa872..64823f4 100644
--- a/include/tools/stream.hxx
+++ b/include/tools/stream.hxx
@@ -252,6 +252,7 @@ public:
SvStream& WriteOString(const OString& rStr)
{ return WriteCharPtr(rStr.getStr()); }
SvStream& WriteStream( SvStream& rStream );
sal_uInt64 WriteStream( SvStream& rStream, sal_uInt64 nSize );
SvStream& WriteBool( bool b )
{ return WriteUChar(static_cast<unsigned char>(b)); }
diff --git a/tools/source/stream/stream.cxx b/tools/source/stream/stream.cxx
index 4f95afd..4c363dc 100644
--- a/tools/source/stream/stream.cxx
+++ b/tools/source/stream/stream.cxx
@@ -1179,6 +1179,27 @@ SvStream& SvStream::WriteStream( SvStream& rStream )
return *this;
}
sal_uInt64 SvStream::WriteStream( SvStream& rStream, sal_uInt64 nSize )
{
const sal_uInt32 cBufLen = 0x8000;
std::unique_ptr<char[]> pBuf( new char[ cBufLen ] );
sal_uInt32 nCurBufLen = cBufLen;
sal_uInt32 nCount;
sal_uInt64 nWriteSize = nSize;
do {
if ( nSize >= nCurBufLen )
nWriteSize -= nCurBufLen;
else
nCurBufLen = nWriteSize;
nCount = rStream.ReadBytes( pBuf.get(), nCurBufLen );
WriteBytes( pBuf.get(), nCount );
}
while( nWriteSize && nCount == nCurBufLen );
return nSize - nWriteSize;
}
OUString SvStream::ReadUniOrByteString( rtl_TextEncoding eSrcCharSet )
{
// read UTF-16 string directly from stream ?
diff --git a/vcl/source/filter/ipdf/pdfread.hxx b/vcl/inc/pdfread.hxx
similarity index 73%
rename from vcl/source/filter/ipdf/pdfread.hxx
rename to vcl/inc/pdfread.hxx
index 2cb3abd..e873c49 100644
--- a/vcl/source/filter/ipdf/pdfread.hxx
+++ b/vcl/inc/pdfread.hxx
@@ -17,6 +17,10 @@ namespace vcl
{
/// Imports a PDF stream into rGraphic as a GDIMetaFile.
VCL_DLLPUBLIC bool ImportPDF(SvStream& rStream, Bitmap &rBitmap,
css::uno::Sequence<sal_Int8> &rPdfFata,
sal_uInt64 nPos = STREAM_SEEK_TO_BEGIN,
sal_uInt64 nSize = STREAM_SEEK_TO_END);
VCL_DLLPUBLIC bool ImportPDF(SvStream& rStream, Graphic& rGraphic);
}
diff --git a/vcl/source/filter/graphicfilter.cxx b/vcl/source/filter/graphicfilter.cxx
index 776ef71..062f5ee 100644
--- a/vcl/source/filter/graphicfilter.cxx
+++ b/vcl/source/filter/graphicfilter.cxx
@@ -44,7 +44,7 @@
#include <vcl/wmf.hxx>
#include <vcl/settings.hxx>
#include "igif/gifread.hxx"
#include "ipdf/pdfread.hxx"
#include <pdfread.hxx>
#include "jpeg/jpeg.hxx"
#include "ixbm/xbmread.hxx"
#include "ixpm/xpmread.hxx"
diff --git a/vcl/source/filter/ipdf/pdfread.cxx b/vcl/source/filter/ipdf/pdfread.cxx
index 254e2adf..59a7b1f 100644
--- a/vcl/source/filter/ipdf/pdfread.cxx
+++ b/vcl/source/filter/ipdf/pdfread.cxx
@@ -7,7 +7,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#include "pdfread.hxx"
#include <pdfread.hxx>
#include <config_features.h>
@@ -56,7 +56,8 @@ double pointToPixel(double fPoint)
}
/// Does PDF to bitmap conversion using pdfium.
bool generatePreview(SvStream& rStream, Graphic& rGraphic)
bool generatePreview(SvStream& rStream, Bitmap& rBitmap,
sal_uInt64 nPos, sal_uInt64 nSize)
{
FPDF_LIBRARY_CONFIG aConfig;
aConfig.version = 2;
@@ -67,7 +68,8 @@ bool generatePreview(SvStream& rStream, Graphic& rGraphic)
// Read input into a buffer.
SvMemoryStream aInBuffer;
aInBuffer.WriteStream(rStream);
rStream.Seek(nPos);
aInBuffer.WriteStream(rStream, nSize);
// Load the buffer using pdfium.
FPDF_DOCUMENT pPdfDocument = FPDF_LoadMemDocument(aInBuffer.GetData(), aInBuffer.GetSize(), /*password=*/nullptr);
@@ -103,7 +105,7 @@ bool generatePreview(SvStream& rStream, Graphic& rGraphic)
pWriteAccess->CopyScanline(nRow, pPdfLine, ScanlineFormat::N32BitTcBgra, nStride);
}
}
rGraphic = aBitmap;
rBitmap = aBitmap;
FPDFBitmap_Destroy(pPdfBitmap);
FPDF_ClosePage(pPdfPage);
@@ -114,11 +116,14 @@ bool generatePreview(SvStream& rStream, Graphic& rGraphic)
}
/// Decide if PDF data is old enough to be compatible.
bool isCompatible(SvStream& rInStream)
bool isCompatible(SvStream& rInStream, sal_uInt64 nPos, sal_uInt64 nSize)
{
if (nSize < 8)
return false;
// %PDF-x.y
sal_uInt8 aFirstBytes[8];
rInStream.Seek(STREAM_SEEK_TO_BEGIN);
rInStream.Seek(nPos);
sal_uLong nRead = rInStream.ReadBytes(aFirstBytes, 8);
if (nRead < 8)
return false;
@@ -133,13 +138,14 @@ bool isCompatible(SvStream& rInStream)
/// Takes care of transparently downgrading the version of the PDF stream in
/// case it's too new for our PDF export.
bool getCompatibleStream(SvStream& rInStream, SvStream& rOutStream)
bool getCompatibleStream(SvStream& rInStream, SvStream& rOutStream,
sal_uInt64 nPos, sal_uInt64 nSize)
{
bool bCompatible = isCompatible(rInStream);
rInStream.Seek(STREAM_SEEK_TO_BEGIN);
bool bCompatible = isCompatible(rInStream, nPos, nSize);
rInStream.Seek( nPos );
if (bCompatible)
// Not converting.
rOutStream.WriteStream(rInStream);
rOutStream.WriteStream(rInStream, nSize);
else
{
// Downconvert to PDF-1.4.
@@ -152,7 +158,7 @@ bool getCompatibleStream(SvStream& rInStream, SvStream& rOutStream)
// Read input into a buffer.
SvMemoryStream aInBuffer;
aInBuffer.WriteStream(rInStream);
aInBuffer.WriteStream(rInStream, nSize);
// Load the buffer using pdfium.
FPDF_DOCUMENT pPdfDocument = FPDF_LoadMemDocument(aInBuffer.GetData(), aInBuffer.GetSize(), /*password=*/nullptr);
@@ -174,18 +180,22 @@ bool getCompatibleStream(SvStream& rInStream, SvStream& rOutStream)
return rOutStream.good();
}
#else
bool generatePreview(SvStream& rStream, Graphic& rGraphic)
bool generatePreview(SvStream& rStream, Bitmap& rBitmap
sal_uInt64 nPos, sal_uInt64 nSize)
{
(void)rStream;
(void)rGraphic;
(void)rBitmap;
(void)nPos;
(void)nSize;
return true;
}
bool getCompatibleStream(SvStream& rInStream, SvStream& rOutStream)
bool getCompatibleStream(SvStream& rInStream, SvStream& rOutStream,
sal_uInt64 nPos, sal_uInt64 nSize)
{
rInStream.Seek(STREAM_SEEK_TO_BEGIN);
rOutStream.WriteStream(rInStream);
rInStream.Seek(nPos);
rOutStream.WriteStream(rInStream, nSize);
return rOutStream.good();
}
#endif // HAVE_FEATURE_PDFIUM
@@ -195,26 +205,38 @@ bool getCompatibleStream(SvStream& rInStream, SvStream& rOutStream)
namespace vcl
{
bool ImportPDF(SvStream& rStream, Graphic& rGraphic)
bool ImportPDF(SvStream& rStream, Bitmap &rBitmap,
css::uno::Sequence<sal_Int8> &rPdfData,
sal_uInt64 nPos, sal_uInt64 nSize)
{
// Get the preview of the first page.
if (!generatePreview(rStream, rGraphic))
if (!generatePreview(rStream, rBitmap, nPos, nSize))
return false;
// Save the original PDF stream for later use.
SvMemoryStream aMemoryStream;
if (!getCompatibleStream(rStream, aMemoryStream))
if (!getCompatibleStream(rStream, aMemoryStream, nPos, nSize))
return false;
aMemoryStream.Seek(STREAM_SEEK_TO_END);
uno::Sequence<sal_Int8> aPdfData(aMemoryStream.Tell());
rPdfData = css::uno::Sequence<sal_Int8>(aMemoryStream.Tell());
aMemoryStream.Seek(STREAM_SEEK_TO_BEGIN);
aMemoryStream.ReadBytes(aPdfData.getArray(), aPdfData.getLength());
rGraphic.setPdfData(aPdfData);
aMemoryStream.ReadBytes(rPdfData.getArray(), rPdfData.getLength());
return true;
}
bool ImportPDF(SvStream& rStream, Graphic& rGraphic)
{
uno::Sequence<sal_Int8> aPdfData;
Bitmap aBitmap;
bool bRet = ImportPDF(rStream, aBitmap, aPdfData);
rGraphic = aBitmap;
rGraphic.setPdfData(aPdfData);
return bRet;
}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/gdi/impgraph.cxx b/vcl/source/gdi/impgraph.cxx
index b0b472c..983eb46 100644
--- a/vcl/source/gdi/impgraph.cxx
+++ b/vcl/source/gdi/impgraph.cxx
@@ -41,6 +41,8 @@
#include <o3tl/make_unique.hxx>
#include <vcl/gdimetafiletools.hxx>
#include <pdfread.hxx>
#define GRAPHIC_MTFTOBMP_MAXEXT 2048
#define GRAPHIC_STREAMBUFSIZE 8192UL
@@ -1574,13 +1576,14 @@ void ReadImpGraphic( SvStream& rIStm, ImpGraphic& rImpGraphic )
// Stream in PDF data.
sal_uInt32 nPdfDataLength = 0;
rIStm.ReadUInt32(nPdfDataLength);
Bitmap aBitmap;
if (nPdfDataLength)
if (nPdfDataLength && !rIStm.GetError() &&
vcl::ImportPDF(rIStm, aBitmap, rImpGraphic.maPdfData,
rIStm.Tell(), nPdfDataLength))
{
uno::Sequence<sal_Int8> aPdfData(nPdfDataLength);
rIStm.ReadBytes(aPdfData.getArray(), nPdfDataLength);
if (!rIStm.GetError())
rImpGraphic.maPdfData = aPdfData;
rImpGraphic.maEx = aBitmap;
rImpGraphic.meType = GraphicType::Bitmap;
}
}
else