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