tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / sdext / source / pdfimport / xpdfwrapper / pdfioutdev_gpl.cxx
blob4d915ce73be16f80d99a7e164ad016e07752b480
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/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "pdfioutdev_gpl.hxx"
21 #include "pnghelper.hxx"
23 #if defined __GNUC__ || defined __clang__
24 # pragma GCC diagnostic push
25 # pragma GCC diagnostic ignored "-Wunused-parameter"
26 #elif defined _MSC_VER
27 #pragma warning(push)
28 #pragma warning(disable : 4100) // unreferenced formal parameter
29 #pragma warning(disable : 4121) // alignment of a member was sensitive to packing in Gfx.h/Operator
30 #endif
32 #include <Gfx.h>
33 #include <splash/SplashBitmap.h>
34 #include <SplashOutputDev.h>
35 #if defined __GNUC__ || defined __clang__
36 # pragma GCC diagnostic pop
37 #elif defined _MSC_VER
38 #pragma warning(pop)
39 #endif
42 #include <stdlib.h>
43 #include <stdio.h>
44 #include <assert.h>
45 #include <math.h>
47 #include <memory>
48 #include <vector>
50 // sigh, UTF8.h was removed in poppler-0.21.0 and put back in 0.21.1, then renamed to UnicodeMapFuncs.h in 0.62.0
51 // FIXME: we can't use #if POPPLER_CHECK_VERSION(0, 21, 0) && !POPPLER_CHECK_VERSION(0, 21, 1)
52 // because the internal poppler does not provide poppler-version.h and the macro always returns 0
53 #if POPPLER_CHECK_VERSION(0, 62, 0)
54 #include <UnicodeMapFuncs.h>
55 #elif POPPLER_CHECK_VERSION(0, 21, 1)
56 #include <UTF8.h>
57 #elif POPPLER_CHECK_VERSION(0, 21, 0)
58 #include "UTF.h"
59 #else
60 #include "UTF8.h"
61 #endif
63 #ifdef _WIN32
64 # define snprintf _snprintf
66 #if defined __GNUC__
67 #pragma GCC diagnostic warning "-Wformat"
68 #pragma GCC diagnostic warning "-Wformat-extra-args"
69 #endif
70 #endif
72 /* SYNC STREAMS
73 ============
75 We stream human-readable tokens to stdout, and binary data (fonts,
76 bitmaps) to g_binary_out. Another process reads from those pipes, and
77 there lies the rub: things can deadlock, if the two involved
78 processes access the pipes in different order. At any point in
79 time, both processes must access the same pipe. To ensure this,
80 data must be flushed to the OS before writing to a different pipe,
81 otherwise not-yet-written data will leave the reading process
82 waiting on the wrong pipe.
85 namespace pdfi
88 /// cut off very small numbers & clamp value to zero
89 static double normalize( double val )
91 return fabs(val) < 0.0000001 ? 0.0 : val;
94 namespace
97 /** Escapes line-ending characters (\n and \r) in input string.
99 std::vector<char> lcl_escapeLineFeeds(const char* const i_pStr)
101 size_t nLength(strlen(i_pStr));
102 std::vector<char> aBuffer;
103 aBuffer.reserve(2*nLength+1);
105 const char* pRead = i_pStr;
106 while( nLength-- )
108 if( *pRead == '\r' )
110 aBuffer.push_back('\\');
111 aBuffer.push_back('r');
113 else if( *pRead == '\n' )
115 aBuffer.push_back('\\');
116 aBuffer.push_back('n');
118 else if( *pRead == '\\' )
120 aBuffer.push_back('\\');
121 aBuffer.push_back('\\');
123 else
124 aBuffer.push_back(*pRead);
125 pRead++;
127 aBuffer.push_back(0);
129 return aBuffer;
134 /// for the temp char buffer the header gets snprintfed in
135 #define WRITE_BUFFER_SIZE 1024
137 /// for the initial std::vector capacity when copying stream from xpdf
138 #define WRITE_BUFFER_INITIAL_CAPACITY (1024*100)
140 static void initBuf(OutputBuffer& io_rBuffer)
142 io_rBuffer.reserve(WRITE_BUFFER_INITIAL_CAPACITY);
145 static void writeBinaryBuffer( const OutputBuffer& rBuffer )
147 // ---sync point--- see SYNC STREAMS above
148 fflush(stdout);
150 // put buffer to stderr
151 if( !rBuffer.empty() )
152 if( fwrite(rBuffer.data(), sizeof(char),
153 rBuffer.size(), g_binary_out) != static_cast<size_t>(rBuffer.size()) )
154 exit(1); // error
156 // ---sync point--- see SYNC STREAMS above
157 fflush(g_binary_out);
160 static bool ExtractJpegData(Stream* str, OutputBuffer& outBuf)
162 int bytesToMarker = 0;
163 int bytesToLen = -1;
164 bool collectBytes = false;
165 int startOfScan = 0;
166 int b1 = -1;
167 for (; ; )
169 const int b2 = b1;
170 b1 = str->getChar();
172 if (b1 == -1)
173 return false;
175 if (collectBytes)
177 outBuf.push_back(static_cast<Output_t>(b1));
179 bytesToMarker--;
180 bytesToLen--;
183 if (bytesToMarker == 0)
185 if (startOfScan == 1)
187 bytesToMarker = -1;
188 startOfScan = 2;
190 else if (b2 == 0xFF)
192 if (b1 == 0xD8)
194 collectBytes = true;
195 bytesToMarker = 2;
197 outBuf.push_back(Output_t(0xFF));
198 outBuf.push_back(Output_t(0xD8));
200 else
202 bytesToLen = 2;
204 if (b1 == 0xDA)
206 startOfScan = 1;
209 else if (collectBytes)
211 return false;
215 if (bytesToLen == 0)
217 bytesToMarker = b2 * 256 + b1;
220 if (startOfScan == 2)
221 if ((b2 == 0xFF) && (b1 == 0xD9))
222 return true;
226 static void writeJpeg_( OutputBuffer& o_rOutputBuf, Stream* str )
228 // dump JPEG file as-is
229 #if POPPLER_CHECK_VERSION(0, 17, 3)
230 str = str->getNextStream();
231 #else
232 str = ((DCTStream *)str)->getRawStream();
233 #endif
234 str->reset();
236 o_rOutputBuf.clear();
237 ExtractJpegData(str, o_rOutputBuf);
239 printf( " JPEG %d", static_cast<int>(o_rOutputBuf.size()) );
240 printf("\n");
242 str->close();
245 static void writePbm_(OutputBuffer& o_rOutputBuf, Stream* str, int width, int height, bool bInvert )
247 // write as PBM (char by char, to avoid stdlib lineend messing)
248 o_rOutputBuf.clear();
249 o_rOutputBuf.resize(WRITE_BUFFER_SIZE);
250 o_rOutputBuf[0] = 'P';
251 o_rOutputBuf[1] = '4';
252 o_rOutputBuf[2] = 0x0A;
253 char *pAsCharPtr = reinterpret_cast<char *>(&o_rOutputBuf[3]);
254 int nOutLen = snprintf(pAsCharPtr, WRITE_BUFFER_SIZE-10, "%d %d", width, height);
255 if( nOutLen < 0 )
256 nOutLen = WRITE_BUFFER_SIZE-10;
257 o_rOutputBuf[3+nOutLen] =0x0A;
258 o_rOutputBuf[3+nOutLen+1]=0;
260 const int header_size = 3+nOutLen+1;
261 const int size = height * ((width + 7) / 8);
263 printf( " PBM %d", size + header_size );
264 printf("\n");
266 // trim buffer to exact header length
267 o_rOutputBuf.resize(header_size);
269 // initialize stream
270 str->reset();
272 // copy the raw stream
273 if( bInvert )
275 for( int i=0; i<size; ++i)
276 o_rOutputBuf.push_back(static_cast<char>(str->getChar() ^ 0xff));
278 else
280 for( int i=0; i<size; ++i)
281 o_rOutputBuf.push_back(static_cast<char>(str->getChar()));
284 str->close();
287 static void writePpm_( OutputBuffer& o_rOutputBuf,
288 Stream* str,
289 int width,
290 int height,
291 GfxImageColorMap* colorMap )
293 // write as PPM (char by char, to avoid stdlib lineend messing)
294 o_rOutputBuf.clear();
295 o_rOutputBuf.resize(WRITE_BUFFER_SIZE);
296 o_rOutputBuf[0] = 'P';
297 o_rOutputBuf[1] = '6';
298 o_rOutputBuf[2] = '\n';
299 char *pAsCharPtr = reinterpret_cast<char *>(&o_rOutputBuf[3]);
300 int nOutLen = snprintf(pAsCharPtr, WRITE_BUFFER_SIZE-10, "%d %d", width, height);
301 if( nOutLen < 0 )
302 nOutLen = WRITE_BUFFER_SIZE-10;
303 o_rOutputBuf[3+nOutLen] ='\n';
304 o_rOutputBuf[3+nOutLen+1]='2';
305 o_rOutputBuf[3+nOutLen+2]='5';
306 o_rOutputBuf[3+nOutLen+3]='5';
307 o_rOutputBuf[3+nOutLen+4]='\n';
308 o_rOutputBuf[3+nOutLen+5]=0;
310 const int header_size = 3+nOutLen+5;
311 const int size = width*height*3 + header_size;
313 printf( " PPM %d", size );
314 printf("\n");
316 // trim buffer to exact header size
317 o_rOutputBuf.resize(header_size);
319 // initialize stream
320 unsigned char *p;
321 GfxRGB rgb;
322 std::unique_ptr<ImageStream> imgStr(
323 new ImageStream(str,
324 width,
325 colorMap->getNumPixelComps(),
326 colorMap->getBits()));
327 imgStr->reset();
329 for( int y=0; y<height; ++y)
331 p = imgStr->getLine();
332 for( int x=0; x<width; ++x)
334 colorMap->getRGB(p, &rgb);
335 o_rOutputBuf.push_back(colToByte(rgb.r));
336 o_rOutputBuf.push_back(colToByte(rgb.g));
337 o_rOutputBuf.push_back(colToByte(rgb.b));
339 p +=colorMap->getNumPixelComps();
344 // call this only for 1 bit image streams !
345 static void writePng_( OutputBuffer& o_rOutputBuf,
346 Stream* str,
347 int width,
348 int height,
349 GfxRGB const & zeroColor,
350 GfxRGB const & oneColor,
351 bool bIsMask )
353 o_rOutputBuf.clear();
355 // get png image
356 PngHelper::createPng( o_rOutputBuf, str, width, height, zeroColor, oneColor, bIsMask );
358 printf( " PNG %d", static_cast<int>(o_rOutputBuf.size()) );
359 printf("\n");
362 static void writePng_( OutputBuffer& o_rOutputBuf,
363 Stream* str,
364 int width, int height, GfxImageColorMap* colorMap,
365 Stream* maskStr,
366 int maskWidth, int maskHeight, GfxImageColorMap* maskColorMap )
368 o_rOutputBuf.clear();
370 // get png image
371 PngHelper::createPng( o_rOutputBuf, str, width, height, colorMap, maskStr, maskWidth, maskHeight, maskColorMap );
373 printf( " PNG %d", static_cast<int>(o_rOutputBuf.size()) );
374 printf("\n");
377 static void writePng_( OutputBuffer& o_rOutputBuf,
378 Stream* str,
379 int width, int height, GfxImageColorMap* colorMap,
380 Stream* maskStr,
381 int maskWidth, int maskHeight, bool maskInvert )
383 o_rOutputBuf.clear();
385 // get png image
386 PngHelper::createPng( o_rOutputBuf, str, width, height, colorMap, maskStr, maskWidth, maskHeight, maskInvert );
388 printf( " PNG %d", static_cast<int>(o_rOutputBuf.size()) );
389 printf("\n");
392 // stolen from ImageOutputDev.cc
393 static void writeMask_( OutputBuffer& o_rOutputBuf, Stream* str, int width, int height, bool bInvert )
395 if( str->getKind() == strDCT )
396 writeJpeg_(o_rOutputBuf, str);
397 else
398 writePbm_(o_rOutputBuf, str, width, height, bInvert );
401 static void writeImage_( OutputBuffer& o_rOutputBuf,
402 Stream* str,
403 int width,
404 int height,
405 GfxImageColorMap* colorMap )
407 // dump JPEG file
408 if( str->getKind() == strDCT &&
409 (colorMap->getNumPixelComps() == 1 ||
410 colorMap->getNumPixelComps() == 3) )
412 writeJpeg_(o_rOutputBuf, str);
414 else if (colorMap->getNumPixelComps() == 1 &&
415 colorMap->getBits() == 1)
417 // this is a two color bitmap, write a png
418 // provide default colors
419 GfxRGB zeroColor = { 0, 0, 0 },
420 oneColor = { byteToCol( 0xff ), byteToCol( 0xff ), byteToCol( 0xff ) };
421 if( colorMap->getColorSpace()->getMode() == csIndexed || colorMap->getColorSpace()->getMode() == csDeviceGray )
423 unsigned char nIndex = 0;
424 colorMap->getRGB( &nIndex, &zeroColor );
425 nIndex = 1;
426 colorMap->getRGB( &nIndex, &oneColor );
428 writePng_( o_rOutputBuf, str, width, height, zeroColor, oneColor, false);
430 else
431 writePpm_( o_rOutputBuf, str, width, height, colorMap );
434 // forwarders
435 static void writeImageLF( OutputBuffer& o_rOutputBuf,
436 Stream* str,
437 int width,
438 int height,
439 GfxImageColorMap* colorMap ) { writeImage_(o_rOutputBuf,str,width,height,colorMap); }
440 static void writeMaskLF( OutputBuffer& o_rOutputBuf,
441 Stream* str,
442 int width,
443 int height,
444 bool bInvert ) { writeMask_(o_rOutputBuf,str,width,height,bInvert); }
446 // Vertically flip the bitmap
447 static void flipSplashBitmap(SplashBitmap *pBitmap)
449 if (pBitmap->getRowSize() <= 0)
450 return;
452 auto nBitmapHeight = static_cast<size_t>(pBitmap->getHeight());
453 auto nRowSize = static_cast<size_t>(pBitmap->getRowSize());
454 auto nAlphaRowSize = static_cast<size_t>(pBitmap->getAlphaRowSize());
456 std::unique_ptr<unsigned char[]> aTmpRow(new unsigned char[nRowSize]);
457 std::unique_ptr<unsigned char[]> aTmpAlphaRow(new unsigned char[nAlphaRowSize]);
459 auto pBitmapData = pBitmap->getDataPtr();
460 auto pAlphaData = pBitmap->getAlphaPtr();
462 // Set up pairs of pointers working from each end of the bitmap
463 auto pCurRowA = pBitmapData;
464 auto pCurAlphaA = pAlphaData;
465 auto pCurRowB = pBitmapData+nRowSize*(nBitmapHeight-1);
466 auto pCurAlphaB = pAlphaData+nAlphaRowSize*(nBitmapHeight-1);
468 for (size_t nCur = 0;
469 nCur < nBitmapHeight/2;
470 nCur++, pCurRowA+=nRowSize, pCurRowB-=nRowSize,
471 pCurAlphaA+=nAlphaRowSize, pCurAlphaB-=nAlphaRowSize)
473 memcpy(aTmpRow.get(), pCurRowA, nRowSize);
474 memcpy(pCurRowA, pCurRowB, nRowSize);
475 memcpy(pCurRowB, aTmpRow.get(), nRowSize);
477 memcpy(aTmpAlphaRow.get(), pCurAlphaA, nAlphaRowSize);
478 memcpy(pCurAlphaA, pCurAlphaB, nAlphaRowSize);
479 memcpy(pCurAlphaB, aTmpAlphaRow.get(), nAlphaRowSize);
483 int PDFOutDev::parseFont( long long nNewId, GfxFont* gfxFont, const GfxState* state ) const
485 FontAttributes aNewFont;
486 int nSize = 0;
488 #if POPPLER_CHECK_VERSION(20, 12, 0)
489 std::string familyName = gfxFont->getNameWithoutSubsetTag();
490 #else
491 #if POPPLER_CHECK_VERSION(0, 71, 0) // GooString::toStr()
492 std::string familyName = gfxFont->getName()->toStr();
493 #else
494 const GooString* gooString = gfxFont->getName();
495 std::string familyName = std::string(gooString->getCString(), gooString->getLength());
496 #endif
497 if (familyName.length() > 7 && familyName.at(6) == '+')
499 familyName = familyName.substr(7);
501 #endif
502 if( familyName != "" )
504 aNewFont.familyName.clear();
505 #if POPPLER_CHECK_VERSION(0, 83, 0) // GooString::append(const std::string&)
506 aNewFont.familyName.append( familyName );
507 #else
508 aNewFont.familyName.append( familyName.c_str() );
509 #endif
511 else
513 aNewFont.familyName.clear();
514 aNewFont.familyName.append( "Arial" );
517 aNewFont.maFontWeight = gfxFont->getWeight();
518 aNewFont.isItalic = gfxFont->isItalic();
519 #if POPPLER_CHECK_VERSION(0, 83, 0) // const added to getTransformedFontSize
520 aNewFont.size = state->getTransformedFontSize();
521 #else
522 aNewFont.size = const_cast<GfxState*>(state)->getTransformedFontSize();
523 #endif
524 aNewFont.isUnderline = false;
526 if( gfxFont->getType() == fontTrueType || gfxFont->getType() == fontType1 )
528 // TODO(P3): Unfortunately, need to read stream twice, since
529 // we must write byte count to stdout before
530 #if POPPLER_CHECK_VERSION(22, 6, 0)
531 std::optional<std::vector<unsigned char>> pBuf = gfxFont->readEmbFontFile( m_pDoc->getXRef() );
532 if ( pBuf )
534 aNewFont.isEmbedded = true;
535 nSize = pBuf->size();
537 #else
538 char* pBuf = gfxFont->readEmbFontFile( m_pDoc->getXRef(), &nSize );
539 if( pBuf )
541 aNewFont.isEmbedded = true;
542 gfree(pBuf);
544 #endif
547 m_aFontMap[ nNewId ] = aNewFont;
548 return nSize;
551 void PDFOutDev::writeFontFile( GfxFont* gfxFont ) const
553 if( gfxFont->getType() != fontTrueType && gfxFont->getType() != fontType1 )
554 return;
556 int nSize = 0;
557 #if POPPLER_CHECK_VERSION(22, 6, 0)
558 std::optional<std::vector<unsigned char>> pBuf = gfxFont->readEmbFontFile( m_pDoc->getXRef() );
559 if ( pBuf )
560 nSize = pBuf->size();
561 if ( nSize == 0 )
562 return;
563 #else
564 char* pBuf = gfxFont->readEmbFontFile( m_pDoc->getXRef(), &nSize );
565 if( !pBuf )
566 return;
567 #endif
569 // ---sync point--- see SYNC STREAMS above
570 fflush(stdout);
572 #if POPPLER_CHECK_VERSION(22, 6, 0)
573 if( fwrite(pBuf->data(), sizeof(*pBuf->data()), nSize, g_binary_out) != static_cast<size_t>(nSize) )
575 exit(1); // error
577 // ---sync point--- see SYNC STREAMS above
578 fflush(g_binary_out);
579 #else
580 if( fwrite(pBuf, sizeof(char), nSize, g_binary_out) != static_cast<size_t>(nSize) )
582 gfree(pBuf);
583 exit(1); // error
585 // ---sync point--- see SYNC STREAMS above
586 fflush(g_binary_out);
587 gfree(pBuf);
588 #endif
591 #if POPPLER_CHECK_VERSION(0, 83, 0)
592 void PDFOutDev::printPath( const GfxPath* pPath )
593 #else
594 void PDFOutDev::printPath( GfxPath* pPath )
595 #endif
597 int nSubPaths = pPath ? pPath->getNumSubpaths() : 0;
598 for( int i=0; i<nSubPaths; i++ )
600 #if POPPLER_CHECK_VERSION(0, 83, 0)
601 const
602 #endif
603 GfxSubpath* pSub = pPath->getSubpath( i );
604 const int nPoints = pSub->getNumPoints();
606 printf( " subpath %d", pSub->isClosed() );
608 for( int n=0; n<nPoints; ++n )
610 printf( " %f %f %d",
611 normalize(pSub->getX(n)),
612 normalize(pSub->getY(n)),
613 pSub->getCurve(n) );
618 PDFOutDev::PDFOutDev( PDFDoc* pDoc ) :
619 m_pDoc( pDoc ),
620 m_pUtf8Map( new UnicodeMap("UTF-8", true, &mapUTF8) ),
621 m_bSkipImages(false)
624 PDFOutDev::~PDFOutDev()
628 void PDFOutDev::startPage(int /*pageNum*/, GfxState* state
629 #if POPPLER_CHECK_VERSION(0, 23, 0)
630 , XRef* /*xref*/
631 #endif
634 assert(state);
635 printf("startPage %f %f\n",
636 normalize(state->getPageWidth()),
637 normalize(state->getPageHeight()));
640 void PDFOutDev::endPage()
642 printf("endPage\n");
645 #if POPPLER_CHECK_VERSION(0, 19, 0)
646 void PDFOutDev::processLink(AnnotLink *link)
647 #elif POPPLER_CHECK_VERSION(0, 17, 0)
648 void PDFOutDev::processLink(AnnotLink *link, Catalog *)
649 #else
650 void PDFOutDev::processLink(Link* link, Catalog*)
651 #endif
653 assert(link);
655 double x1,x2,y1,y2;
656 link->getRect( &x1, &y1, &x2, &y2 );
658 LinkAction* pAction = link->getAction();
659 if (!(pAction && pAction->getKind() == actionURI))
660 return;
662 #if POPPLER_CHECK_VERSION(0, 86, 0)
663 const char* pURI = static_cast<LinkURI*>(pAction)->getURI().c_str();
664 #elif POPPLER_CHECK_VERSION(0, 72, 0)
665 const char* pURI = static_cast<LinkURI*>(pAction)->getURI()->c_str();
666 #else
667 const char* pURI = static_cast<LinkURI*>(pAction)->getURI()->getCString();
668 #endif
670 std::vector<char> aEsc( lcl_escapeLineFeeds(pURI) );
672 printf( "drawLink %f %f %f %f %s\n",
673 normalize(x1),
674 normalize(y1),
675 normalize(x2),
676 normalize(y2),
677 aEsc.data() );
680 void PDFOutDev::saveState(GfxState*)
682 printf( "saveState\n" );
685 void PDFOutDev::restoreState(GfxState*)
687 printf( "restoreState\n" );
690 #if POPPLER_CHECK_VERSION(0, 71, 0)
691 void PDFOutDev::setDefaultCTM(const double *pMat)
692 #else
693 void PDFOutDev::setDefaultCTM(double *pMat)
694 #endif
696 assert(pMat);
698 OutputDev::setDefaultCTM(pMat);
700 printf( "updateCtm %f %f %f %f %f %f\n",
701 normalize(pMat[0]),
702 normalize(pMat[1]),
703 normalize(pMat[2]),
704 normalize(pMat[3]),
705 normalize(pMat[4]),
706 normalize(pMat[5]) );
709 void PDFOutDev::updateCTM(GfxState* state,
710 double, double,
711 double, double,
712 double, double)
714 assert(state);
716 const double* const pMat = state->getCTM();
717 assert(pMat);
719 printf( "updateCtm %f %f %f %f %f %f\n",
720 normalize(pMat[0]),
721 normalize(pMat[1]),
722 normalize(pMat[2]),
723 normalize(pMat[3]),
724 normalize(pMat[4]),
725 normalize(pMat[5]) );
728 void PDFOutDev::updateLineDash(GfxState *state)
730 if (m_bSkipImages)
731 return;
732 assert(state);
734 int arrayLen; double startOffset;
735 #if POPPLER_CHECK_VERSION(22, 9, 0)
736 const std::vector<double> &dash = state->getLineDash(&startOffset);
737 const double* dashArray = dash.data();
738 arrayLen = dash.size();
739 #else
740 double* dashArray;
741 state->getLineDash(&dashArray, &arrayLen, &startOffset);
742 #endif
744 printf( "updateLineDash" );
745 if( arrayLen && dashArray )
747 printf( " %f %d", normalize(startOffset), arrayLen );
748 for( int i=0; i<arrayLen; ++i )
749 printf( " %f", normalize(*dashArray++) );
751 printf( "\n" );
754 void PDFOutDev::updateFlatness(GfxState *state)
756 if (m_bSkipImages)
757 return;
758 assert(state);
759 printf( "updateFlatness %d\n", state->getFlatness() );
762 void PDFOutDev::updateLineJoin(GfxState *state)
764 if (m_bSkipImages)
765 return;
766 assert(state);
767 printf( "updateLineJoin %d\n", state->getLineJoin() );
770 void PDFOutDev::updateLineCap(GfxState *state)
772 if (m_bSkipImages)
773 return;
774 assert(state);
775 printf( "updateLineCap %d\n", state->getLineCap() );
778 void PDFOutDev::updateMiterLimit(GfxState *state)
780 if (m_bSkipImages)
781 return;
782 assert(state);
783 printf( "updateMiterLimit %f\n", normalize(state->getMiterLimit()) );
786 void PDFOutDev::updateLineWidth(GfxState *state)
788 if (m_bSkipImages)
789 return;
790 assert(state);
791 printf( "updateLineWidth %f\n", normalize(state->getLineWidth()) );
794 void PDFOutDev::updateFillColor(GfxState *state)
796 if (m_bSkipImages)
797 return;
798 assert(state);
800 GfxRGB aRGB;
801 state->getFillRGB( &aRGB );
803 printf( "updateFillColor %f %f %f %f\n",
804 normalize(colToDbl(aRGB.r)),
805 normalize(colToDbl(aRGB.g)),
806 normalize(colToDbl(aRGB.b)),
807 normalize(state->getFillOpacity()) );
810 void PDFOutDev::updateStrokeColor(GfxState *state)
812 if (m_bSkipImages)
813 return;
814 assert(state);
816 GfxRGB aRGB;
817 state->getStrokeRGB( &aRGB );
819 printf( "updateStrokeColor %f %f %f %f\n",
820 normalize(colToDbl(aRGB.r)),
821 normalize(colToDbl(aRGB.g)),
822 normalize(colToDbl(aRGB.b)),
823 normalize(state->getFillOpacity()) );
826 void PDFOutDev::updateFillOpacity(GfxState *state)
828 if (m_bSkipImages)
829 return;
830 updateFillColor(state);
833 void PDFOutDev::updateStrokeOpacity(GfxState *state)
835 if (m_bSkipImages)
836 return;
837 updateStrokeColor(state);
840 void PDFOutDev::updateBlendMode(GfxState*)
844 void PDFOutDev::updateFont(GfxState *state)
846 assert(state);
848 #if POPPLER_CHECK_VERSION(22, 6, 0)
849 GfxFont *gfxFont = state->getFont().get();
850 #else
851 GfxFont *gfxFont = state->getFont();
852 #endif
853 if( !gfxFont )
854 return;
856 FontAttributes aFont;
857 int nEmbedSize=0;
859 #if POPPLER_CHECK_VERSION(0, 64, 0)
860 const
861 #endif
862 Ref* pID = gfxFont->getID();
863 // TODO(Q3): Portability problem
864 long long fontID = static_cast<long long>(pID->gen) << 32 | static_cast<long long>(pID->num);
865 std::unordered_map< long long, FontAttributes >::const_iterator it =
866 m_aFontMap.find( fontID );
867 if( it == m_aFontMap.end() )
869 nEmbedSize = parseFont( fontID, gfxFont, state );
870 it = m_aFontMap.find( fontID );
873 printf( "updateFont" );
874 if( it != m_aFontMap.end() )
876 // conflating this with printf below crashes under Windoze
877 printf( " %lld", fontID );
879 aFont = it->second;
881 #if POPPLER_CHECK_VERSION(0, 72, 0)
882 std::vector<char> aEsc( lcl_escapeLineFeeds(aFont.familyName.c_str()) );
883 #else
884 std::vector<char> aEsc( lcl_escapeLineFeeds(aFont.familyName.getCString()) );
885 #endif
886 printf( " %d %d %d %d %f %d %s",
887 aFont.isEmbedded,
888 aFont.maFontWeight,
889 aFont.isItalic,
890 aFont.isUnderline,
891 normalize(state->getTransformedFontSize()),
892 nEmbedSize,
893 aEsc.data() );
895 printf( "\n" );
897 if (nEmbedSize)
899 writeFontFile(gfxFont);
903 void PDFOutDev::updateRender(GfxState *state)
905 assert(state);
907 printf( "setTextRenderMode %d\n", state->getRender() );
910 void PDFOutDev::stroke(GfxState *state)
912 if (m_bSkipImages)
913 return;
914 assert(state);
916 printf( "strokePath" );
917 printPath( state->getPath() );
918 printf( "\n" );
921 void PDFOutDev::fill(GfxState *state)
923 if (m_bSkipImages)
924 return;
925 assert(state);
927 printf( "fillPath" );
928 printPath( state->getPath() );
929 printf( "\n" );
932 void PDFOutDev::eoFill(GfxState *state)
934 if (m_bSkipImages)
935 return;
936 assert(state);
938 printf( "eoFillPath" );
939 printPath( state->getPath() );
940 printf( "\n" );
943 void PDFOutDev::clip(GfxState *state)
945 if (m_bSkipImages)
946 return;
947 assert(state);
949 printf( "clipPath" );
950 printPath( state->getPath() );
951 printf( "\n" );
954 void PDFOutDev::eoClip(GfxState *state)
956 if (m_bSkipImages)
957 return;
958 assert(state);
960 printf( "eoClipPath" );
961 printPath( state->getPath() );
962 printf( "\n" );
965 void PDFOutDev::clipToStrokePath(GfxState *state)
967 if (m_bSkipImages)
968 return;
969 assert(state);
971 printf( "clipToStrokePath" );
972 printPath( state->getPath() );
973 printf( "\n" );
976 /** Output one glyph
979 @param dx
980 horizontal skip for character (already scaled with font size) +
981 inter-char space: cursor is shifted by this amount for next char
983 @param dy
984 vertical skip for character (zero for horizontal writing mode):
985 cursor is shifted by this amount for next char
987 @param originX
988 local offset of character (zero for horizontal writing mode). not
989 taken into account for output pos updates. Used for vertical writing.
991 @param originY
992 local offset of character (zero for horizontal writing mode). not
993 taken into account for output pos updates. Used for vertical writing.
996 #if POPPLER_CHECK_VERSION(0, 82, 0)
997 void PDFOutDev::drawChar(GfxState *state, double x, double y,
998 double dx, double dy,
999 double originX, double originY,
1000 CharCode, int /*nBytes*/, const Unicode *u, int uLen)
1002 #else
1003 void PDFOutDev::drawChar(GfxState *state, double x, double y,
1004 double dx, double dy,
1005 double originX, double originY,
1006 CharCode, int /*nBytes*/, Unicode *u, int uLen)
1008 #endif
1009 assert(state);
1011 if( u == nullptr )
1012 return;
1014 // Fix for tdf#96080
1015 if (uLen == 4 && u[0] == '\t' && u[1] == '\r' && u[2] == ' ' && u[3] == 0xA0)
1017 u += 2;
1018 uLen = 1;
1021 double csdx = 0.0;
1022 double csdy = 0.0;
1023 if (!state->getFont() || !state->getFont()->getWMode())
1025 csdx = state->getCharSpace();
1026 if (*u == ' ')
1027 csdx += state->getWordSpace();
1028 csdx *= state->getHorizScaling();
1030 else
1032 csdy = state->getCharSpace();
1033 if (*u == ' ')
1034 csdy += state->getWordSpace();
1037 double cstdx = 0.0;
1038 double cstdy = 0.0;
1039 state->textTransformDelta(csdx, csdy, &cstdx, &cstdy);
1041 const double fontSize = state->getFontSize();
1043 const double aPositionX(x-originX);
1044 const double aPositionY(y-originY);
1046 const double* pTextMat=state->getTextMat();
1047 printf( "drawChar %f %f %f %f %f %f %f %f %f ",
1048 normalize(aPositionX),
1049 normalize(aPositionY),
1050 normalize(aPositionX + dx - cstdx),
1051 normalize(aPositionY + dy - cstdy),
1052 normalize(pTextMat[0]),
1053 normalize(pTextMat[2]),
1054 normalize(pTextMat[1]),
1055 normalize(pTextMat[3]),
1056 normalize(fontSize)
1059 // silence spurious warning
1060 #if POPPLER_CHECK_VERSION(0, 62, 0)
1061 (void)&mapUTF16;
1062 #else
1063 (void)&mapUCS2;
1064 #endif
1066 char buf[9];
1067 for( int i=0; i<uLen; ++i )
1069 buf[ m_pUtf8Map->mapUnicode(u[i], buf, sizeof(buf)-1) ] = 0;
1070 std::vector<char> aEsc( lcl_escapeLineFeeds(buf) );
1071 printf( "%s", aEsc.data() );
1074 printf( "\n" );
1077 #if POPPLER_CHECK_VERSION(0, 64, 0)
1078 void PDFOutDev::drawString(GfxState*, const GooString* /*s*/)
1079 #else
1080 void PDFOutDev::drawString(GfxState*, GooString* /*s*/)
1081 #endif
1083 // TODO(F3): NYI
1086 void PDFOutDev::endTextObject(GfxState*)
1088 printf( "endTextObject\n" );
1091 void PDFOutDev::drawImageMask(GfxState* pState, Object*, Stream* str,
1092 int width, int height, poppler_bool invert,
1093 poppler_bool /*interpolate*/,
1094 poppler_bool /*inlineImg*/ )
1096 if (m_bSkipImages)
1097 return;
1098 OutputBuffer aBuf; initBuf(aBuf);
1100 printf( "drawMask %d %d %d", width, height, invert );
1102 int bitsPerComponent = 1;
1103 StreamColorSpaceMode csMode = streamCSNone;
1104 str->getImageParams( &bitsPerComponent, &csMode );
1105 if( bitsPerComponent == 1 && (csMode == streamCSNone || csMode == streamCSDeviceGray) )
1107 GfxRGB oneColor = { dblToCol( 1.0 ), dblToCol( 1.0 ), dblToCol( 1.0 ) };
1108 GfxRGB zeroColor = { dblToCol( 0.0 ), dblToCol( 0.0 ), dblToCol( 0.0 ) };
1109 pState->getFillColorSpace()->getRGB( pState->getFillColor(), &zeroColor );
1110 if( invert )
1111 writePng_( aBuf, str, width, height, oneColor, zeroColor, true );
1112 else
1113 writePng_( aBuf, str, width, height, zeroColor, oneColor, true );
1115 else
1116 writeMaskLF(aBuf, str, width, height, invert);
1117 writeBinaryBuffer(aBuf);
1120 #if POPPLER_CHECK_VERSION(0, 82, 0)
1121 void PDFOutDev::drawImage(GfxState*, Object*, Stream* str,
1122 int width, int height, GfxImageColorMap* colorMap,
1123 poppler_bool /*interpolate*/,
1124 const int* maskColors, poppler_bool /*inlineImg*/ )
1126 #else
1127 void PDFOutDev::drawImage(GfxState*, Object*, Stream* str,
1128 int width, int height, GfxImageColorMap* colorMap,
1129 poppler_bool /*interpolate*/,
1130 int* maskColors, poppler_bool /*inlineImg*/ )
1132 #endif
1133 if (m_bSkipImages)
1134 return;
1135 OutputBuffer aBuf; initBuf(aBuf);
1136 OutputBuffer aMaskBuf;
1138 printf( "drawImage %d %d", width, height );
1140 if( maskColors )
1142 // write mask colors. nBytes must be even - first half is
1143 // lower bound values, second half upper bound values
1144 if( colorMap->getColorSpace()->getMode() == csIndexed )
1146 aMaskBuf.push_back( static_cast<char>(maskColors[0]) );
1147 aMaskBuf.push_back( static_cast<char>(maskColors[gfxColorMaxComps]) );
1149 else
1151 GfxRGB aMinRGB;
1152 colorMap->getColorSpace()->getRGB(
1153 #if POPPLER_CHECK_VERSION(0, 82, 0)
1154 reinterpret_cast<const GfxColor*>(maskColors),
1155 #else
1156 reinterpret_cast<GfxColor*>(maskColors),
1157 #endif
1158 &aMinRGB );
1160 GfxRGB aMaxRGB;
1161 colorMap->getColorSpace()->getRGB(
1162 #if POPPLER_CHECK_VERSION(0, 82, 0)
1163 reinterpret_cast<const GfxColor*>(maskColors)+gfxColorMaxComps,
1164 #else
1165 reinterpret_cast<GfxColor*>(maskColors)+gfxColorMaxComps,
1166 #endif
1167 &aMaxRGB );
1169 aMaskBuf.push_back( colToByte(aMinRGB.r) );
1170 aMaskBuf.push_back( colToByte(aMinRGB.g) );
1171 aMaskBuf.push_back( colToByte(aMinRGB.b) );
1172 aMaskBuf.push_back( colToByte(aMaxRGB.r) );
1173 aMaskBuf.push_back( colToByte(aMaxRGB.g) );
1174 aMaskBuf.push_back( colToByte(aMaxRGB.b) );
1178 printf( " %d", static_cast<int>(aMaskBuf.size()) );
1179 writeImageLF( aBuf, str, width, height, colorMap );
1180 writeBinaryBuffer(aBuf);
1181 writeBinaryBuffer(aMaskBuf);
1184 void PDFOutDev::drawMaskedImage(GfxState*, Object*, Stream* str,
1185 int width, int height,
1186 GfxImageColorMap* colorMap,
1187 poppler_bool /*interpolate*/,
1188 Stream* maskStr,
1189 int maskWidth, int maskHeight,
1190 poppler_bool maskInvert, poppler_bool /*maskInterpolate*/
1193 if (m_bSkipImages)
1194 return;
1195 OutputBuffer aBuf; initBuf(aBuf);
1196 printf( "drawImage %d %d 0", width, height );
1197 writePng_( aBuf, str, width, height, colorMap, maskStr, maskWidth, maskHeight, maskInvert );
1198 writeBinaryBuffer( aBuf );
1201 void PDFOutDev::drawSoftMaskedImage(GfxState*, Object*, Stream* str,
1202 int width, int height,
1203 GfxImageColorMap* colorMap,
1204 poppler_bool /*interpolate*/,
1205 Stream* maskStr,
1206 int maskWidth, int maskHeight,
1207 GfxImageColorMap* maskColorMap
1208 , poppler_bool /*maskInterpolate*/
1211 if (m_bSkipImages)
1212 return;
1213 OutputBuffer aBuf; initBuf(aBuf);
1214 printf( "drawImage %d %d 0", width, height );
1215 writePng_( aBuf, str, width, height, colorMap, maskStr, maskWidth, maskHeight, maskColorMap );
1216 writeBinaryBuffer( aBuf );
1219 void PDFOutDev::setPageNum( int nNumPages )
1221 // TODO(F3): printf might format int locale-dependent!
1222 printf("setPageNum %d\n", nNumPages);
1225 void PDFOutDev::setSkipImages( bool bSkipImages )
1227 m_bSkipImages = bSkipImages;
1230 #if POPPLER_CHECK_VERSION(21, 3, 0)
1231 poppler_bool PDFOutDev::tilingPatternFill(GfxState *state, Gfx *, Catalog *,
1232 GfxTilingPattern *tPat, const double *mat,
1233 int x0, int y0, int x1, int y1,
1234 double xStep, double yStep)
1236 const double *pBbox = tPat->getBBox();
1237 const int nPaintType = tPat->getPaintType();
1238 Dict *pResDict = tPat->getResDict();
1239 Object *aStr = tPat->getContentStream();
1240 double nWidth = pBbox[2] - pBbox[0];
1241 double nHeight = pBbox[3] - pBbox[1];
1243 // If our wrapper is skipping images then we don't need to do anything
1244 // but return 'true' so that Poppler doesn't do the slow method
1245 if (m_bSkipImages)
1246 return true;
1248 // Copied from the Cairo output dev; I think this is patterns
1249 // with gaps, let poppler do the slow method for now.
1250 if (xStep != nWidth || yStep != nHeight)
1251 return false;
1253 printf( "tilingPatternFill %d %d %d %d %f %f "
1254 "%d "
1255 "%f %f %f %f %f %f", // No ending space!
1257 x0, y0, x1, y1, normalize(xStep), normalize(yStep),
1259 nPaintType,
1261 normalize(mat[0]), normalize(mat[1]),
1262 normalize(mat[2]), normalize(mat[3]),
1263 normalize(mat[4]), normalize(mat[5])
1266 PDFRectangle aBox;
1267 aBox.x1 = pBbox[0];
1268 aBox.y1 = pBbox[1];
1269 aBox.x2 = pBbox[2];
1270 aBox.y2 = pBbox[3];
1272 const int nDPI = 72; // GfxState seems to have 72.0 as magic for some reason
1273 auto pSplashGfxState = new GfxState(nDPI, nDPI, &aBox, 0, false);
1274 auto pSplashOut = new SplashOutputDev(splashModeRGB8, 1, false, nullptr);
1275 pSplashOut->setEnableFreeType(false);
1276 pSplashOut->startDoc(m_pDoc);
1277 pSplashOut->startPage(0 /* pageNum */, pSplashGfxState, nullptr /* xref */);
1279 auto pSplashGfx = new Gfx(m_pDoc, pSplashOut, pResDict, &aBox, nullptr);
1280 pSplashGfx->display(aStr);
1281 std::unique_ptr<SplashBitmap> pSplashBitmap(pSplashOut->takeBitmap());
1282 // Poppler tells us to free the splash device immediately after taking the
1283 // bitmap
1284 delete pSplashGfxState;
1285 delete pSplashGfx;
1286 delete pSplashOut;
1288 // Add a vertical flip, we can't do this in LO for an image filled poly
1289 flipSplashBitmap(pSplashBitmap.get());
1291 auto nBitmapWidth = static_cast<size_t>(pSplashBitmap->getWidth());
1292 auto nBitmapHeight = static_cast<size_t>(pSplashBitmap->getHeight());
1294 char *pBitmapData = reinterpret_cast<char *>(pSplashBitmap->getDataPtr());
1295 if (nPaintType == 2)
1297 // My understanding is Type 2 fills are just bitmaps of *what* to fill
1298 // in the current fill colour.
1299 // sending it to LO as a flat colour image with the alpha map is easiest
1300 GfxRGB aCurFill;
1301 unsigned char r,g,b;
1302 state->getFillColorSpace()->getRGB(state->getFillColor(), &aCurFill);
1303 r = colToByte(aCurFill.r);
1304 g = colToByte(aCurFill.g);
1305 b = colToByte(aCurFill.b);
1307 for(size_t i=0; i < (nBitmapWidth * nBitmapHeight * 3); i+=3)
1309 pBitmapData[i ] = r;
1310 pBitmapData[i+1] = g;
1311 pBitmapData[i+2] = b;
1315 std::unique_ptr<MemStream> pRgbStr(new MemStream(pBitmapData, 0,
1316 nBitmapWidth * nBitmapHeight * 3, Object(objNull)));
1317 std::unique_ptr<MemStream> pAlphaStr(new MemStream(reinterpret_cast<char *>(pSplashBitmap->getAlphaPtr()),
1318 0, nBitmapWidth * nBitmapHeight, Object(objNull)));
1319 auto aDecode = Object(objNull);
1320 std::unique_ptr<GfxImageColorMap> pRgbIdentityColorMap(new GfxImageColorMap(8, &aDecode,
1321 new GfxDeviceRGBColorSpace()));
1322 std::unique_ptr<GfxImageColorMap> pGrayIdentityColorMap(new GfxImageColorMap(8, &aDecode,
1323 new GfxDeviceGrayColorSpace()));
1325 OutputBuffer aBuf; initBuf(aBuf);
1326 writePng_(aBuf, pRgbStr.get(), nBitmapWidth, nBitmapHeight, pRgbIdentityColorMap.get(),
1327 pAlphaStr.get(), nBitmapWidth, nBitmapHeight, pGrayIdentityColorMap.get());
1328 writeBinaryBuffer(aBuf);
1330 // If we return false here we can fall back to the slow path
1331 return true;
1334 // This could be implemented for earlier versions, but the interface keeps
1335 // changing a little; not having it is only a problem for inputs with
1336 // large patterns.
1337 #endif
1340 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */