1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 #include "pdfioutdev_gpl.hxx"
23 #include "pnghelper.hxx"
24 
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <assert.h>
28 #include <math.h>
29 #include <vector>
30 
31 #include <boost/shared_array.hpp>
32 
33 #if defined __SUNPRO_CC
34 #pragma disable_warn
35 #elif defined _MSC_VER
36 #pragma warning(push, 1)
37 #endif
38 
39 #include "UTF8.h"
40 
41 #if defined __SUNPRO_CC
42 #pragma enable_warn
43 #elif defined _MSC_VER
44 #pragma warning(pop)
45 #endif
46 
47 #ifdef WNT
48 # define snprintf _snprintf
49 #endif
50 
51 
52 /* SYNC STREAMS
53    ============
54 
55    We stream human-readble tokens to stdout, and binary data (fonts,
56    bitmaps) to g_binary_out. Another process reads from those pipes, and
57    there lies the rub: things can deadlock, if the two involved
58    processes access the pipes in different order. At any point in
59    time, both processes must access the same pipe. To ensure this,
60    data must be flushed to the OS before writing to a different pipe,
61    otherwise not-yet-written data will leave the reading process
62    waiting on the wrong pipe.
63  */
64 
65 namespace pdfi
66 {
67 
68 /// cut off very small numbers & clamp value to zero
69 inline double normalize( double val )
70 {
71     return fabs(val) < 0.0000001 ? 0.0 : val;
72 }
73 
74 namespace
75 {
76 
77 /** Escapes line-ending characters (\n and \r) in input string.
78   */
79 boost::shared_array<char> lcl_escapeLineFeeds(const char* const i_pStr)
80 {
81     size_t nLength(strlen(i_pStr));
82     char* pBuffer = new char[2*nLength+1];
83 
84     const char* pRead = i_pStr;
85     char* pWrite = pBuffer;
86     while( nLength-- )
87     {
88         if( *pRead == '\r' )
89         {
90             *pWrite++ = '\\';
91             *pWrite++ = 'r';
92         }
93         else if( *pRead == '\n' )
94         {
95             *pWrite++ = '\\';
96             *pWrite++ = 'n';
97         }
98         else if( *pRead == '\\' )
99         {
100             *pWrite++ = '\\';
101             *pWrite++ = '\\';
102         }
103         else
104             *pWrite++ = *pRead;
105         pRead++;
106     }
107     *pWrite++ = 0;
108 
109     return boost::shared_array<char>(pBuffer);
110 }
111 
112 }
113 
114 /// for the temp char buffer the header gets snprintfed in
115 #define WRITE_BUFFER_SIZE 1024
116 
117 /// for the initial std::vector capacity when copying stream from xpdf
118 #define WRITE_BUFFER_INITIAL_CAPACITY (1024*100)
119 
120 void initBuf(OutputBuffer& io_rBuffer)
121 {
122     io_rBuffer.reserve(WRITE_BUFFER_INITIAL_CAPACITY);
123 }
124 
125 void writeBinaryBuffer( const OutputBuffer& rBuffer )
126 {
127     // ---sync point--- see SYNC STREAMS above
128     fflush(stdout);
129 
130     // put buffer to stderr
131     if( !rBuffer.empty() )
132         if( fwrite(&rBuffer[0], sizeof(char),
133                    rBuffer.size(), g_binary_out) != (size_t)rBuffer.size() )
134             exit(1); // error
135 
136     // ---sync point--- see SYNC STREAMS above
137     fflush(g_binary_out);
138 }
139 
140 void writeJpeg_( OutputBuffer& o_rOutputBuf, Stream* str, bool bWithLinefeed )
141 {
142     // dump JPEG file as-is
143     str = ((DCTStream *)str)->getRawStream();
144     str->reset();
145 
146     int c;
147     o_rOutputBuf.clear();
148     while((c=str->getChar()) != EOF)
149         o_rOutputBuf.push_back(static_cast<char>(c));
150 
151     printf( " JPEG %d", (int)o_rOutputBuf.size() );
152     if( bWithLinefeed )
153         printf("\n");
154 
155     str->close();
156 }
157 
158 void writePbm_(OutputBuffer& o_rOutputBuf, Stream* str, int width, int height, bool bWithLinefeed, bool bInvert )
159 {
160     // write as PBM (char by char, to avoid stdlib lineend messing)
161     o_rOutputBuf.clear();
162     o_rOutputBuf.resize(WRITE_BUFFER_SIZE);
163     o_rOutputBuf[0] = 'P';
164     o_rOutputBuf[1] = '4';
165     o_rOutputBuf[2] = 0x0A;
166     int nOutLen = snprintf(&o_rOutputBuf[3], WRITE_BUFFER_SIZE-10, "%d %d", width, height);
167     if( nOutLen < 0 )
168         nOutLen = WRITE_BUFFER_SIZE-10;
169     o_rOutputBuf[3+nOutLen]  =0x0A;
170     o_rOutputBuf[3+nOutLen+1]=0;
171 
172     const int header_size = 3+nOutLen+1;
173     const int size = height * ((width + 7) / 8);
174 
175     printf( " PBM %d", size + header_size );
176     if( bWithLinefeed )
177         printf("\n");
178 
179     // trim buffer to exact header length
180     o_rOutputBuf.resize(header_size);
181 
182     // initialize stream
183     str->reset();
184 
185     // copy the raw stream
186     if( bInvert )
187     {
188         for( int i=0; i<size; ++i)
189             o_rOutputBuf.push_back(static_cast<char>(str->getChar() ^ 0xff));
190     }
191     else
192     {
193         for( int i=0; i<size; ++i)
194             o_rOutputBuf.push_back(static_cast<char>(str->getChar()));
195     }
196 
197     str->close();
198 }
199 
200 void writePpm_( OutputBuffer&     o_rOutputBuf,
201                 Stream*           str,
202                 int               width,
203                 int               height,
204                 GfxImageColorMap* colorMap,
205                 bool              bWithLinefeed )
206 {
207     // write as PPM (char by char, to avoid stdlib lineend messing)
208     o_rOutputBuf.clear();
209     o_rOutputBuf.resize(WRITE_BUFFER_SIZE);
210     o_rOutputBuf[0] = 'P';
211     o_rOutputBuf[1] = '6';
212     o_rOutputBuf[2] = '\n';
213     int nOutLen = snprintf(&o_rOutputBuf[3], WRITE_BUFFER_SIZE-10, "%d %d", width, height);
214     if( nOutLen < 0 )
215         nOutLen = WRITE_BUFFER_SIZE-10;
216     o_rOutputBuf[3+nOutLen]  ='\n';
217     o_rOutputBuf[3+nOutLen+1]='2';
218     o_rOutputBuf[3+nOutLen+2]='5';
219     o_rOutputBuf[3+nOutLen+3]='5';
220     o_rOutputBuf[3+nOutLen+4]='\n';
221     o_rOutputBuf[3+nOutLen+5]=0;
222 
223     const int header_size = 3+nOutLen+5;
224     const int size = width*height*3 + header_size;
225 
226     printf( " PPM %d", size );
227     if( bWithLinefeed )
228         printf("\n");
229 
230     // trim buffer to exact header size
231     o_rOutputBuf.resize(header_size);
232 
233     // initialize stream
234     Guchar *p;
235     GfxRGB rgb;
236     ImageStream* imgStr =
237         new ImageStream(str,
238                         width,
239                         colorMap->getNumPixelComps(),
240                         colorMap->getBits());
241     imgStr->reset();
242 
243     for( int y=0; y<height; ++y)
244     {
245         p = imgStr->getLine();
246         for( int x=0; x<width; ++x)
247         {
248             colorMap->getRGB(p, &rgb);
249             o_rOutputBuf.push_back(colToByte(rgb.r));
250             o_rOutputBuf.push_back(colToByte(rgb.g));
251             o_rOutputBuf.push_back(colToByte(rgb.b));
252 
253             p +=colorMap->getNumPixelComps();
254         }
255     }
256 
257     delete imgStr;
258 
259 }
260 
261 // call this only for 1 bit image streams !
262 void writePng_( OutputBuffer&     o_rOutputBuf,
263                 Stream*           str,
264                 int               width,
265                 int               height,
266                 GfxRGB&           zeroColor,
267                 GfxRGB&           oneColor,
268                 bool              bIsMask,
269                 bool              bWithLinefeed )
270 {
271     o_rOutputBuf.clear();
272 
273     // get png image
274     PngHelper::createPng( o_rOutputBuf, str, width, height, zeroColor, oneColor, bIsMask );
275 
276     printf( " PNG %d", (int)o_rOutputBuf.size() );
277     if( bWithLinefeed )
278         printf("\n");
279 }
280 
281 void writePng_( OutputBuffer& o_rOutputBuf,
282                 Stream* str,
283                 int width, int height, GfxImageColorMap* colorMap,
284                 Stream* maskStr,
285                 int maskWidth, int maskHeight, GfxImageColorMap* maskColorMap,
286                 bool bWithLinefeed )
287 {
288     o_rOutputBuf.clear();
289 
290     // get png image
291     PngHelper::createPng( o_rOutputBuf, str, width, height, colorMap, maskStr, maskWidth, maskHeight, maskColorMap );
292 
293     printf( " PNG %d", (int)o_rOutputBuf.size() );
294     if( bWithLinefeed )
295         printf("\n");
296 }
297 
298 void writePng_( OutputBuffer& o_rOutputBuf,
299                 Stream* str,
300                 int width, int height, GfxImageColorMap* colorMap,
301                 Stream* maskStr,
302                 int maskWidth, int maskHeight, bool maskInvert,
303                 bool bWithLinefeed )
304 {
305     o_rOutputBuf.clear();
306 
307     // get png image
308     PngHelper::createPng( o_rOutputBuf, str, width, height, colorMap, maskStr, maskWidth, maskHeight, maskInvert );
309 
310     printf( " PNG %d", (int)o_rOutputBuf.size() );
311     if( bWithLinefeed )
312         printf("\n");
313 }
314 
315 // stolen from ImageOutputDev.cc
316 void writeMask_( OutputBuffer& o_rOutputBuf, Stream* str, int width, int height, bool bWithLinefeed, bool bInvert )
317 {
318     if( str->getKind() == strDCT )
319         writeJpeg_(o_rOutputBuf, str, bWithLinefeed);
320     else
321         writePbm_(o_rOutputBuf, str, width, height, bWithLinefeed, bInvert );
322 }
323 
324 void writeImage_( OutputBuffer&     o_rOutputBuf,
325                   Stream*           str,
326                   int               width,
327                   int               height,
328                   GfxImageColorMap* colorMap,
329                   bool              bWithLinefeed )
330 {
331     // dump JPEG file
332     if( str->getKind() == strDCT &&
333         (colorMap->getNumPixelComps() == 1 ||
334          colorMap->getNumPixelComps() == 3) )
335     {
336         writeJpeg_(o_rOutputBuf, str, bWithLinefeed);
337     }
338     else if (colorMap->getNumPixelComps() == 1 &&
339              colorMap->getBits() == 1)
340     {
341         // this is a two color bitmap, write a png
342         // provide default colors
343         GfxRGB zeroColor = { 0, 0, 0 },
344                 oneColor = { byteToCol( 0xff ), byteToCol( 0xff ), byteToCol( 0xff ) };
345         if( colorMap->getColorSpace()->getMode() == csIndexed || colorMap->getColorSpace()->getMode() == csDeviceGray )
346         {
347             Guchar nIndex = 0;
348             colorMap->getRGB( &nIndex, &zeroColor );
349             nIndex = 1;
350             colorMap->getRGB( &nIndex, &oneColor );
351         }
352         writePng_( o_rOutputBuf, str, width, height, zeroColor, oneColor, false, bWithLinefeed );
353     }
354     else
355         writePpm_( o_rOutputBuf, str, width, height, colorMap, bWithLinefeed );
356 }
357 
358 // forwarders
359 // ------------------------------------------------------------------
360 
361 inline void writeImage( OutputBuffer&     o_rOutputBuf,
362                         Stream*           str,
363                         int               width,
364                         int               height,
365                         GfxImageColorMap* colorMap ) { writeImage_(o_rOutputBuf,str,width,height,colorMap,false); }
366 inline void writeImageLF( OutputBuffer&     o_rOutputBuf,
367                           Stream*           str,
368                           int               width,
369                           int               height,
370                           GfxImageColorMap* colorMap ) { writeImage_(o_rOutputBuf,str,width,height,colorMap,true); }
371 inline void writeMask( OutputBuffer&     o_rOutputBuf,
372                        Stream*           str,
373                        int               width,
374                        int               height,
375                        bool              bInvert ) { writeMask_(o_rOutputBuf,str,width,height,false,bInvert); }
376 inline void writeMaskLF( OutputBuffer&     o_rOutputBuf,
377                          Stream*           str,
378                          int               width,
379                          int               height,
380                          bool              bInvert ) { writeMask_(o_rOutputBuf,str,width,height,true,bInvert); }
381 
382 // ------------------------------------------------------------------
383 
384 
385 int PDFOutDev::parseFont( long long nNewId, GfxFont* gfxFont, GfxState* state ) const
386 {
387     FontAttributes aNewFont;
388     int nSize = 0;
389 
390     GooString* pFamily = gfxFont->getName();
391     if( ! pFamily )
392         pFamily = gfxFont->getOrigName();
393     if( pFamily )
394     {
395         aNewFont.familyName.clear();
396         aNewFont.familyName.append( gfxFont->getName() );
397     }
398     else
399     {
400         aNewFont.familyName.clear();
401         aNewFont.familyName.append( "Arial" );
402     }
403 
404     aNewFont.isBold        = gfxFont->isBold();
405     aNewFont.isItalic      = gfxFont->isItalic();
406     aNewFont.size          = state->getTransformedFontSize();
407     aNewFont.isUnderline   = false;
408 
409     if( gfxFont->getType() == fontTrueType || gfxFont->getType() == fontType1 )
410     {
411         // TODO(P3): Unfortunately, need to read stream twice, since
412         // we must write byte count to stdout before
413         char* pBuf = gfxFont->readEmbFontFile( m_pDoc->getXRef(), &nSize );
414         if( pBuf )
415             aNewFont.isEmbedded = true;
416     }
417 
418     m_aFontMap[ nNewId ] = aNewFont;
419     return nSize;
420 }
421 
422 void PDFOutDev::writeFontFile( GfxFont* gfxFont ) const
423 {
424     if( gfxFont->getType() != fontTrueType && gfxFont->getType() != fontType1 )
425         return;
426 
427     int nSize = 0;
428     char* pBuf = gfxFont->readEmbFontFile( m_pDoc->getXRef(), &nSize );
429     if( !pBuf )
430         return;
431 
432     // ---sync point--- see SYNC STREAMS above
433     fflush(stdout);
434 
435     if( fwrite(pBuf, sizeof(char), nSize, g_binary_out) != (size_t)nSize )
436         exit(1); // error
437 
438     // ---sync point--- see SYNC STREAMS above
439     fflush(g_binary_out);
440 }
441 
442 void PDFOutDev::printPath( GfxPath* pPath ) const
443 {
444     int nSubPaths = pPath ? pPath->getNumSubpaths() : 0;
445     for( int i=0; i<nSubPaths; i++ )
446     {
447         GfxSubpath* pSub  = pPath->getSubpath( i );
448         const int nPoints = pSub->getNumPoints();
449 
450         printf( " subpath %d", pSub->isClosed() );
451 
452         for( int n=0; n<nPoints; ++n )
453         {
454             printf( " %f %f %d",
455                     normalize(pSub->getX(n)),
456                     normalize(pSub->getY(n)),
457                     pSub->getCurve(n) );
458         }
459     }
460 }
461 
462 PDFOutDev::PDFOutDev( PDFDoc* pDoc ) :
463     m_pDoc( pDoc ),
464     m_aFontMap(),
465     m_pUtf8Map( new UnicodeMap((char*)"UTF-8", gTrue, &mapUTF8) )
466 {
467 }
468 
469 void PDFOutDev::startPage(int /*pageNum*/, GfxState* state)
470 {
471     assert(state);
472     printf("startPage %f %f\n",
473            normalize(state->getPageWidth()),
474            normalize(state->getPageHeight()));
475 }
476 
477 void PDFOutDev::endPage()
478 {
479     printf("endPage\n");
480 }
481 
482 void PDFOutDev::processLink(Link* link, Catalog*)
483 {
484     assert(link);
485 
486     double x1,x2,y1,y2;
487     link->getRect( &x1, &y1, &x2, &y2 );
488 
489     LinkAction* pAction = link->getAction();
490     if( pAction->getKind() == actionURI )
491     {
492         const char* pURI = static_cast<LinkURI*>(pAction)->getURI()->getCString();
493 
494         boost::shared_array<char> pEsc( lcl_escapeLineFeeds(pURI) );
495 
496         printf( "drawLink %f %f %f %f %s\n",
497                 normalize(x1),
498                 normalize(y1),
499                 normalize(x2),
500                 normalize(y2),
501                 pEsc.get() );
502     }
503 }
504 
505 void PDFOutDev::saveState(GfxState*)
506 {
507     printf( "saveState\n" );
508 }
509 
510 void PDFOutDev::restoreState(GfxState*)
511 {
512     printf( "restoreState\n" );
513 }
514 
515 void PDFOutDev::setDefaultCTM(double *pMat)
516 {
517     assert(pMat);
518 
519     OutputDev::setDefaultCTM(pMat);
520 
521     printf( "updateCtm %f %f %f %f %f %f\n",
522             normalize(pMat[0]),
523             normalize(pMat[2]),
524             normalize(pMat[1]),
525             normalize(pMat[3]),
526             normalize(pMat[4]),
527             normalize(pMat[5]) );
528 }
529 
530 void PDFOutDev::updateCTM(GfxState* state,
531                           double, double,
532                           double, double,
533                           double, double)
534 {
535     assert(state);
536 
537     const double* const pMat = state->getCTM();
538     assert(pMat);
539 
540     printf( "updateCtm %f %f %f %f %f %f\n",
541             normalize(pMat[0]),
542             normalize(pMat[2]),
543             normalize(pMat[1]),
544             normalize(pMat[3]),
545             normalize(pMat[4]),
546             normalize(pMat[5]) );
547 }
548 
549 void PDFOutDev::updateLineDash(GfxState *state)
550 {
551     assert(state);
552 
553     double* dashArray; int arrayLen; double startOffset;
554     state->getLineDash(&dashArray, &arrayLen, &startOffset);
555 
556     printf( "updateLineDash" );
557     if( arrayLen && dashArray )
558     {
559         printf( " %f %d", normalize(startOffset), arrayLen );
560         for( int i=0; i<arrayLen; ++i )
561             printf( " %f", normalize(*dashArray++) );
562     }
563     printf( "\n" );
564 }
565 
566 void PDFOutDev::updateFlatness(GfxState *state)
567 {
568     assert(state);
569     printf( "updateFlatness %d\n", state->getFlatness() );
570 }
571 
572 void PDFOutDev::updateLineJoin(GfxState *state)
573 {
574     assert(state);
575     printf( "updateLineJoin %d\n", state->getLineJoin() );
576 }
577 
578 void PDFOutDev::updateLineCap(GfxState *state)
579 {
580     assert(state);
581     printf( "updateLineCap %d\n", state->getLineCap() );
582 }
583 
584 void PDFOutDev::updateMiterLimit(GfxState *state)
585 {
586     assert(state);
587     printf( "updateMiterLimit %f\n", normalize(state->getMiterLimit()) );
588 }
589 
590 void PDFOutDev::updateLineWidth(GfxState *state)
591 {
592     assert(state);
593     printf( "updateLineWidth %f\n", normalize(state->getLineWidth()) );
594 }
595 
596 void PDFOutDev::updateFillColor(GfxState *state)
597 {
598     assert(state);
599 
600     GfxRGB aRGB;
601     state->getFillRGB( &aRGB );
602 
603     printf( "updateFillColor %f %f %f %f\n",
604             normalize(colToDbl(aRGB.r)),
605             normalize(colToDbl(aRGB.g)),
606             normalize(colToDbl(aRGB.b)),
607             normalize(state->getFillOpacity()) );
608 }
609 
610 void PDFOutDev::updateStrokeColor(GfxState *state)
611 {
612     assert(state);
613 
614     GfxRGB aRGB;
615     state->getStrokeRGB( &aRGB );
616 
617     printf( "updateStrokeColor %f %f %f %f\n",
618             normalize(colToDbl(aRGB.r)),
619             normalize(colToDbl(aRGB.g)),
620             normalize(colToDbl(aRGB.b)),
621             normalize(state->getFillOpacity()) );
622 }
623 
624 void PDFOutDev::updateFillOpacity(GfxState *state)
625 {
626     updateFillColor(state);
627 }
628 
629 void PDFOutDev::updateStrokeOpacity(GfxState *state)
630 {
631     updateStrokeColor(state);
632 }
633 
634 void PDFOutDev::updateBlendMode(GfxState*)
635 {
636 }
637 
638 void PDFOutDev::updateFont(GfxState *state)
639 {
640     assert(state);
641 
642     GfxFont *gfxFont = state->getFont();
643     if( gfxFont )
644     {
645         FontAttributes aFont;
646         int nEmbedSize=0;
647 
648         Ref* pID = gfxFont->getID();
649         // TODO(Q3): Portability problem
650         long long fontID = (long long)pID->gen << 32 | (long long)pID->num;
651         std::hash_map< long long, FontAttributes >::const_iterator it =
652             m_aFontMap.find( fontID );
653         if( it == m_aFontMap.end() )
654         {
655             nEmbedSize = parseFont( fontID, gfxFont, state );
656             it = m_aFontMap.find( fontID );
657         }
658 
659         printf( "updateFont" );
660         if( it != m_aFontMap.end() )
661         {
662             // conflating this with printf below crashes under Windoze
663             printf( " %lld", fontID );
664 
665             aFont = it->second;
666 
667             boost::shared_array<char> pEsc( lcl_escapeLineFeeds(aFont.familyName.getCString()) );
668             printf( " %d %d %d %d %f %d %s",
669                     aFont.isEmbedded,
670                     aFont.isBold,
671                     aFont.isItalic,
672                     aFont.isUnderline,
673                     normalize(state->getTransformedFontSize()),
674                     nEmbedSize,
675                     pEsc.get() );
676         }
677         printf( "\n" );
678 
679         if( nEmbedSize )
680             writeFontFile(gfxFont);
681     }
682 }
683 
684 void PDFOutDev::updateRender(GfxState *state)
685 {
686     assert(state);
687 
688     printf( "setTextRenderMode %d\n", state->getRender() );
689 }
690 
691 void PDFOutDev::stroke(GfxState *state)
692 {
693     assert(state);
694 
695     printf( "strokePath" );
696     printPath( state->getPath() );
697     printf( "\n" );
698 }
699 
700 void PDFOutDev::fill(GfxState *state)
701 {
702     assert(state);
703 
704     printf( "fillPath" );
705     printPath( state->getPath() );
706     printf( "\n" );
707 }
708 
709 void PDFOutDev::eoFill(GfxState *state)
710 {
711     assert(state);
712 
713     printf( "eoFillPath" );
714     printPath( state->getPath() );
715     printf( "\n" );
716 }
717 
718 void PDFOutDev::clip(GfxState *state)
719 {
720     assert(state);
721 
722     printf( "clipPath" );
723     printPath( state->getPath() );
724     printf( "\n" );
725 }
726 
727 void PDFOutDev::eoClip(GfxState *state)
728 {
729     assert(state);
730 
731     printf( "eoClipPath" );
732     printPath( state->getPath() );
733     printf( "\n" );
734 }
735 
736 /** Output one glyph
737 
738 
739     @param dx
740     horizontal skip for character (already scaled with font size) +
741     inter-char space: cursor is shifted by this amount for next char
742 
743     @param dy
744     vertical skip for character (zero for horizontal writing mode):
745     cursor is shifted by this amount for next char
746 
747     @param originX
748     local offset of character (zero for horizontal writing mode). not
749     taken into account for output pos updates. Used for vertical writing.
750 
751     @param originY
752     local offset of character (zero for horizontal writing mode). not
753     taken into account for output pos updates. Used for vertical writing.
754  */
755 void PDFOutDev::drawChar(GfxState *state, double x, double y,
756                          double dx, double dy,
757                          double originX, double originY,
758                          CharCode, int /*nBytes*/, Unicode *u, int uLen)
759 {
760     assert(state);
761 
762     if( u == NULL )
763         return;
764 
765     // normalize coordinates: correct from baseline-relative to upper
766     // left corner of glyphs
767     double x2(0.0), y2(0.0);
768     state->textTransformDelta( 0.0,
769                                state->getFont()->getAscent(),
770                                &x2, &y2 );
771     const double fFontSize(state->getFontSize());
772     x += x2*fFontSize;
773     y += y2*fFontSize;
774 
775     const double aPositionX(x-originX);
776     const double aPositionY(y-originY);
777     // TODO(F2): use leading here, when set
778     const double nWidth(dx != 0.0 ? dx : fFontSize);
779     const double nHeight(dy != 0.0 ? dy : fFontSize);
780 
781     const double* pTextMat=state->getTextMat();
782     printf( "drawChar %f %f %f %f %f %f %f %f ",
783             normalize(aPositionX),
784             normalize(aPositionY),
785             normalize(aPositionX+nWidth),
786             normalize(aPositionY-nHeight),
787             normalize(pTextMat[0]),
788             normalize(pTextMat[2]),
789             normalize(pTextMat[1]),
790             normalize(pTextMat[3]) );
791 
792     // silence spurious warning
793     (void)&mapUCS2;
794 
795     char buf[9];
796     for( int i=0; i<uLen; ++i )
797     {
798         buf[ m_pUtf8Map->mapUnicode(u[i], buf, sizeof(buf)-1) ] = 0;
799         boost::shared_array<char> pEsc( lcl_escapeLineFeeds(buf) );
800         printf( "%s", pEsc.get() );
801     }
802 
803     printf( "\n" );
804 }
805 
806 void PDFOutDev::drawString(GfxState*, GooString* /*s*/)
807 {
808     // TODO(F3): NYI
809 }
810 
811 void PDFOutDev::endTextObject(GfxState*)
812 {
813     printf( "endTextObject\n" );
814 }
815 
816 void PDFOutDev::drawImageMask(GfxState* pState, Object*, Stream* str,
817                               int width, int height, GBool invert,
818                               GBool /*inlineImg*/ )
819 {
820     OutputBuffer aBuf; initBuf(aBuf);
821 
822     printf( "drawMask %d %d %d", width, height, invert );
823 
824     int bitsPerComponent = 1;
825     StreamColorSpaceMode csMode = streamCSNone;
826     str->getImageParams( &bitsPerComponent, &csMode );
827     if( bitsPerComponent == 1 && (csMode == streamCSNone || csMode == streamCSDeviceGray) )
828     {
829         GfxRGB oneColor = { dblToCol( 1.0 ), dblToCol( 1.0 ), dblToCol( 1.0 ) };
830         GfxRGB zeroColor = { dblToCol( 0.0 ), dblToCol( 0.0 ), dblToCol( 0.0 ) };
831         pState->getFillColorSpace()->getRGB( pState->getFillColor(), &zeroColor );
832         if( invert )
833             writePng_( aBuf, str, width, height, oneColor, zeroColor, true, true );
834         else
835             writePng_( aBuf, str, width, height, zeroColor, oneColor, true, true );
836     }
837     else
838         writeMaskLF(aBuf, str, width, height, invert != 0);
839     writeBinaryBuffer(aBuf);
840 }
841 
842 void PDFOutDev::drawImage(GfxState*, Object*, Stream* str,
843                           int width, int height, GfxImageColorMap* colorMap,
844                           int* maskColors, GBool /*inlineImg*/ )
845 {
846     OutputBuffer aBuf; initBuf(aBuf);
847     OutputBuffer aMaskBuf;
848 
849     printf( "drawImage %d %d", width, height );
850 
851     if( maskColors )
852     {
853         // write mask colors. nBytes must be even - first half is
854         // lower bound values, second half upper bound values
855         if( colorMap->getColorSpace()->getMode() == csIndexed )
856         {
857             aMaskBuf.push_back( (char)maskColors[0] );
858             aMaskBuf.push_back( (char)maskColors[gfxColorMaxComps] );
859         }
860         else
861         {
862             GfxRGB aMinRGB;
863             colorMap->getColorSpace()->getRGB(
864                 (GfxColor*)maskColors,
865                 &aMinRGB );
866 
867             GfxRGB aMaxRGB;
868             colorMap->getColorSpace()->getRGB(
869                 (GfxColor*)maskColors+gfxColorMaxComps,
870                 &aMaxRGB );
871 
872             aMaskBuf.push_back( colToByte(aMinRGB.r) );
873             aMaskBuf.push_back( colToByte(aMinRGB.g) );
874             aMaskBuf.push_back( colToByte(aMinRGB.b) );
875             aMaskBuf.push_back( colToByte(aMaxRGB.r) );
876             aMaskBuf.push_back( colToByte(aMaxRGB.g) );
877             aMaskBuf.push_back( colToByte(aMaxRGB.b) );
878         }
879     }
880 
881     printf( " %d", (int)aMaskBuf.size() );
882     writeImageLF( aBuf, str, width, height, colorMap );
883     writeBinaryBuffer(aBuf);
884     writeBinaryBuffer(aMaskBuf);
885 }
886 
887 void PDFOutDev::drawMaskedImage(GfxState*, Object*, Stream* str,
888                                 int width, int height,
889                                 GfxImageColorMap* colorMap,
890                                 Stream* maskStr,
891                                 int maskWidth, int maskHeight,
892                                 GBool maskInvert)
893 {
894     OutputBuffer aBuf;     initBuf(aBuf);
895     printf( "drawImage %d %d 0", width, height );
896     writePng_( aBuf, str, width, height, colorMap, maskStr, maskWidth, maskHeight, maskInvert, true );
897     writeBinaryBuffer( aBuf );
898     #if 0
899     OutputBuffer aBuf;     initBuf(aBuf);
900     OutputBuffer aMaskBuf; initBuf(aMaskBuf);
901 
902     printf( "drawMaskedImage %d %d %d %d %d", width, height, maskWidth, maskHeight, 0 /*maskInvert note: currently we do inversion here*/ );
903     writeImage( aBuf, str, width, height, colorMap );
904     writeMaskLF( aMaskBuf, maskStr, width, height, maskInvert );
905     writeBinaryBuffer(aBuf);
906     writeBinaryBuffer(aMaskBuf);
907     #endif
908 }
909 
910 void PDFOutDev::drawSoftMaskedImage(GfxState*, Object*, Stream* str,
911                                     int width, int height,
912                                     GfxImageColorMap* colorMap,
913                                     Stream* maskStr,
914                                     int maskWidth, int maskHeight,
915                                     GfxImageColorMap* maskColorMap )
916 {
917     OutputBuffer aBuf;     initBuf(aBuf);
918     printf( "drawImage %d %d 0", width, height );
919     writePng_( aBuf, str, width, height, colorMap, maskStr, maskWidth, maskHeight, maskColorMap, true );
920     writeBinaryBuffer( aBuf );
921     #if 0
922     OutputBuffer aBuf;     initBuf(aBuf);
923     OutputBuffer aMaskBuf; initBuf(aMaskBuf);
924 
925     printf( "drawSoftMaskedImage %d %d %d %d", width, height, maskWidth, maskHeight );
926     writeImage( aBuf, str, width, height, colorMap );
927     writeImageLF( aMaskBuf, maskStr, maskWidth, maskHeight, maskColorMap );
928     writeBinaryBuffer(aBuf);
929     writeBinaryBuffer(aMaskBuf);
930     #endif
931 }
932 
933 void PDFOutDev::setPageNum( int nNumPages )
934 {
935     // TODO(F3): printf might format int locale-dependent!
936     printf("setPageNum %d\n", nNumPages);
937 }
938 
939 }
940