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