1 //========================================================================
2 //
3 // HtmlOutputDev.cc
4 //
5 // Copyright 1997-2002 Glyph & Cog, LLC
6 //
7 // Changed 1999-2000 by G.Ovtcharov
8 //
9 // Changed 2002 by Mikhail Kruk
10 //
11 //========================================================================
12 
13 //========================================================================
14 //
15 // Modified under the Poppler project - http://poppler.freedesktop.org
16 //
17 // All changes made under the Poppler project to this file are licensed
18 // under GPL version 2 or later
19 //
20 // Copyright (C) 2005-2010 Albert Astals Cid <aacid@kde.org>
21 // Copyright (C) 2008 Kjartan Maraas <kmaraas@gnome.org>
22 // Copyright (C) 2008 Boris Toloknov <tlknv@yandex.ru>
23 // Copyright (C) 2008 Haruyuki Kawabe <Haruyuki.Kawabe@unisys.co.jp>
24 // Copyright (C) 2008 Tomas Are Haavet <tomasare@gmail.com>
25 // Copyright (C) 2009 Warren Toomey <wkt@tuhs.org>
26 // Copyright (C) 2009 Carlos Garcia Campos <carlosgc@gnome.org>
27 // Copyright (C) 2009 Reece Dunn <msclrhd@gmail.com>
28 // Copyright (C) 2010 Adrian Johnson <ajohnson@redneon.com>
29 // Copyright (C) 2010 Hib Eris <hib@hiberis.nl>
30 // Copyright (C) 2010 OSSD CDAC Mumbai by Leena Chourey (leenac@cdacmumbai.in) and Onkar Potdar (onkar@cdacmumbai.in)
31 //
32 // To see a description of the changes please see the Changelog file that
33 // came with your tarball or type make ChangeLog if you are building from git
34 //
35 //========================================================================
36 
37 #ifdef __GNUC__
38 #pragma implementation
39 #endif
40 
41 #include "config.h"
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <stdarg.h>
45 #include <stddef.h>
46 #include <ctype.h>
47 #include <math.h>
48 #include "goo/GooString.h"
49 #include "goo/GooList.h"
50 #include "UnicodeMap.h"
51 #include "goo/gmem.h"
52 #include "Error.h"
53 #include "GfxState.h"
54 #include "Page.h"
55 #include "PNGWriter.h"
56 #ifdef ENABLE_LIBJPEG
57 #include "DCTStream.h"
58 #endif
59 #include "GlobalParams.h"
60 #include "HtmlOutputDev.h"
61 #include "HtmlFonts.h"
62 
63 int HtmlPage::pgNum=0;
64 int HtmlOutputDev::imgNum=1;
65 GooList *HtmlOutputDev::imgList=new GooList();
66 
67 extern GBool complexMode;
68 extern GBool singleHtml;
69 extern GBool ignore;
70 extern GBool printCommands;
71 extern GBool printHtml;
72 extern GBool noframes;
73 extern GBool stout;
74 extern GBool xml;
75 extern GBool showHidden;
76 extern GBool noMerge;
77 
basename(GooString * str)78 static GooString* basename(GooString* str){
79 
80   char *p=str->getCString();
81   int len=str->getLength();
82   for (int i=len-1;i>=0;i--)
83     if (*(p+i)==SLASH)
84       return new GooString((p+i+1),len-i-1);
85   return new GooString(str);
86 }
87 
88 #if 0
89 static GooString* Dirname(GooString* str){
90 
91   char *p=str->getCString();
92   int len=str->getLength();
93   for (int i=len-1;i>=0;i--)
94     if (*(p+i)==SLASH)
95       return new GooString(p,i+1);
96   return new GooString();
97 }
98 #endif
99 
100 //------------------------------------------------------------------------
101 // HtmlString
102 //------------------------------------------------------------------------
103 
HtmlString(GfxState * state,double fontSize,HtmlFontAccu * fonts)104 HtmlString::HtmlString(GfxState *state, double fontSize, HtmlFontAccu* fonts) {
105   GfxFont *font;
106   double x, y;
107 
108   state->transform(state->getCurX(), state->getCurY(), &x, &y);
109   if ((font = state->getFont())) {
110     double ascent = font->getAscent();
111     double descent = font->getDescent();
112     if( ascent > 1.05 ){
113         //printf( "ascent=%.15g is too high, descent=%.15g\n", ascent, descent );
114         ascent = 1.05;
115     }
116     if( descent < -0.4 ){
117         //printf( "descent %.15g is too low, ascent=%.15g\n", descent, ascent );
118         descent = -0.4;
119     }
120     yMin = y - ascent * fontSize;
121     yMax = y - descent * fontSize;
122     GfxRGB rgb;
123     state->getFillRGB(&rgb);
124     GooString *name = state->getFont()->getName();
125     if (!name) name = HtmlFont::getDefaultFont(); //new GooString("default");
126     HtmlFont hfont=HtmlFont(name, static_cast<int>(fontSize-1), rgb);
127     fontpos = fonts->AddFont(hfont);
128   } else {
129     // this means that the PDF file draws text without a current font,
130     // which should never happen
131     yMin = y - 0.95 * fontSize;
132     yMax = y + 0.35 * fontSize;
133     fontpos=0;
134   }
135   if (yMin == yMax) {
136     // this is a sanity check for a case that shouldn't happen -- but
137     // if it does happen, we want to avoid dividing by zero later
138     yMin = y;
139     yMax = y + 1;
140   }
141   col = 0;
142   text = NULL;
143   xRight = NULL;
144   link = NULL;
145   len = size = 0;
146   yxNext = NULL;
147   xyNext = NULL;
148   htext=new GooString();
149   dir = textDirUnknown;
150 }
151 
152 
~HtmlString()153 HtmlString::~HtmlString() {
154   gfree(text);
155   delete htext;
156   gfree(xRight);
157 }
158 
addChar(GfxState * state,double x,double y,double dx,double dy,Unicode u)159 void HtmlString::addChar(GfxState *state, double x, double y,
160 			 double dx, double dy, Unicode u) {
161   if (dir == textDirUnknown) {
162     //dir = UnicodeMap::getDirection(u);
163     dir = textDirLeftRight;
164   }
165 
166   if (len == size) {
167     size += 16;
168     text = (Unicode *)grealloc(text, size * sizeof(Unicode));
169     xRight = (double *)grealloc(xRight, size * sizeof(double));
170   }
171   text[len] = u;
172   if (len == 0) {
173     xMin = x;
174   }
175   xMax = xRight[len] = x + dx;
176 //printf("added char: %f %f xright = %f\n", x, dx, x+dx);
177   ++len;
178 }
179 
endString()180 void HtmlString::endString()
181 {
182   if( dir == textDirRightLeft && len > 1 )
183   {
184     //printf("will reverse!\n");
185     for (int i = 0; i < len / 2; i++)
186     {
187       Unicode ch = text[i];
188       text[i] = text[len - i - 1];
189       text[len - i - 1] = ch;
190     }
191   }
192 }
193 
194 //------------------------------------------------------------------------
195 // HtmlPage
196 //------------------------------------------------------------------------
197 
HtmlPage(GBool rawOrder,char * imgExtVal)198 HtmlPage::HtmlPage(GBool rawOrder, char *imgExtVal) {
199   this->rawOrder = rawOrder;
200   curStr = NULL;
201   yxStrings = NULL;
202   xyStrings = NULL;
203   yxCur1 = yxCur2 = NULL;
204   fonts=new HtmlFontAccu();
205   links=new HtmlLinks();
206   pageWidth=0;
207   pageHeight=0;
208   fontsPageMarker = 0;
209   DocName=NULL;
210   firstPage = -1;
211   imgExt = new GooString(imgExtVal);
212 }
213 
~HtmlPage()214 HtmlPage::~HtmlPage() {
215   clear();
216   if (DocName) delete DocName;
217   if (fonts) delete fonts;
218   if (links) delete links;
219   if (imgExt) delete imgExt;
220 }
221 
updateFont(GfxState * state)222 void HtmlPage::updateFont(GfxState *state) {
223   GfxFont *font;
224   double *fm;
225   char *name;
226   int code;
227   double w;
228 
229   // adjust the font size
230   fontSize = state->getTransformedFontSize();
231   if ((font = state->getFont()) && font->getType() == fontType3) {
232     // This is a hack which makes it possible to deal with some Type 3
233     // fonts.  The problem is that it's impossible to know what the
234     // base coordinate system used in the font is without actually
235     // rendering the font.  This code tries to guess by looking at the
236     // width of the character 'm' (which breaks if the font is a
237     // subset that doesn't contain 'm').
238     for (code = 0; code < 256; ++code) {
239       if ((name = ((Gfx8BitFont *)font)->getCharName(code)) &&
240 	  name[0] == 'm' && name[1] == '\0') {
241 	break;
242       }
243     }
244     if (code < 256) {
245       w = ((Gfx8BitFont *)font)->getWidth(code);
246       if (w != 0) {
247 	// 600 is a generic average 'm' width -- yes, this is a hack
248 	fontSize *= w / 0.6;
249       }
250     }
251     fm = font->getFontMatrix();
252     if (fm[0] != 0) {
253       fontSize *= fabs(fm[3] / fm[0]);
254     }
255   }
256 }
257 
beginString(GfxState * state,GooString * s)258 void HtmlPage::beginString(GfxState *state, GooString *s) {
259   curStr = new HtmlString(state, fontSize, fonts);
260 }
261 
262 
conv()263 void HtmlPage::conv(){
264   HtmlString *tmp;
265 
266   int linkIndex = 0;
267   HtmlFont* h;
268   for(tmp=yxStrings;tmp;tmp=tmp->yxNext){
269      int pos=tmp->fontpos;
270      //  printf("%d\n",pos);
271      h=fonts->Get(pos);
272 
273      if (tmp->htext) delete tmp->htext;
274      tmp->htext=HtmlFont::simple(h,tmp->text,tmp->len);
275 
276      if (links->inLink(tmp->xMin,tmp->yMin,tmp->xMax,tmp->yMax, linkIndex)){
277        tmp->link = links->getLink(linkIndex);
278        /*GooString *t=tmp->htext;
279        tmp->htext=links->getLink(k)->Link(tmp->htext);
280        delete t;*/
281      }
282   }
283 
284 }
285 
286 
addChar(GfxState * state,double x,double y,double dx,double dy,double ox,double oy,Unicode * u,int uLen)287 void HtmlPage::addChar(GfxState *state, double x, double y,
288 		       double dx, double dy,
289 			double ox, double oy, Unicode *u, int uLen) {
290   double x1, y1, w1, h1, dx2, dy2;
291   int n, i;
292   state->transform(x, y, &x1, &y1);
293   n = curStr->len;
294 
295   // check that new character is in the same direction as current string
296   // and is not too far away from it before adding
297   //if ((UnicodeMap::getDirection(u[0]) != curStr->dir) ||
298   // XXX
299   if (
300      (n > 0 &&
301       fabs(x1 - curStr->xRight[n-1]) > 0.1 * (curStr->yMax - curStr->yMin))) {
302     endString();
303     beginString(state, NULL);
304   }
305   state->textTransformDelta(state->getCharSpace() * state->getHorizScaling(),
306 			    0, &dx2, &dy2);
307   dx -= dx2;
308   dy -= dy2;
309   state->transformDelta(dx, dy, &w1, &h1);
310   if (uLen != 0) {
311     w1 /= uLen;
312     h1 /= uLen;
313   }
314   for (i = 0; i < uLen; ++i) {
315     curStr->addChar(state, x1 + i*w1, y1 + i*h1, w1, h1, u[i]);
316   }
317 }
318 
endString()319 void HtmlPage::endString() {
320   HtmlString *p1, *p2;
321   double h, y1, y2;
322 
323   // throw away zero-length strings -- they don't have valid xMin/xMax
324   // values, and they're useless anyway
325   if (curStr->len == 0) {
326     delete curStr;
327     curStr = NULL;
328     return;
329   }
330 
331   curStr->endString();
332 
333 #if 0 //~tmp
334   if (curStr->yMax - curStr->yMin > 20) {
335     delete curStr;
336     curStr = NULL;
337     return;
338   }
339 #endif
340 
341   // insert string in y-major list
342   h = curStr->yMax - curStr->yMin;
343   y1 = curStr->yMin + 0.5 * h;
344   y2 = curStr->yMin + 0.8 * h;
345   if (rawOrder) {
346     p1 = yxCur1;
347     p2 = NULL;
348   } else if ((!yxCur1 ||
349               (y1 >= yxCur1->yMin &&
350                (y2 >= yxCur1->yMax || curStr->xMax >= yxCur1->xMin))) &&
351              (!yxCur2 ||
352               (y1 < yxCur2->yMin ||
353                (y2 < yxCur2->yMax && curStr->xMax < yxCur2->xMin)))) {
354     p1 = yxCur1;
355     p2 = yxCur2;
356   } else {
357     for (p1 = NULL, p2 = yxStrings; p2; p1 = p2, p2 = p2->yxNext) {
358       if (y1 < p2->yMin || (y2 < p2->yMax && curStr->xMax < p2->xMin))
359         break;
360     }
361     yxCur2 = p2;
362   }
363   yxCur1 = curStr;
364   if (p1)
365     p1->yxNext = curStr;
366   else
367     yxStrings = curStr;
368   curStr->yxNext = p2;
369   curStr = NULL;
370 }
371 
strrstr(const char * s,const char * ss)372 static const char *strrstr( const char *s, const char *ss )
373 {
374   const char *p = strstr( s, ss );
375   for( const char *pp = p; pp != NULL; pp = strstr( p+1, ss ) ){
376     p = pp;
377   }
378   return p;
379 }
380 
CloseTags(GooString * htext,GBool & finish_a,GBool & finish_italic,GBool & finish_bold)381 static void CloseTags( GooString *htext, GBool &finish_a, GBool &finish_italic, GBool &finish_bold )
382 {
383   const char *last_italic = finish_italic && ( finish_bold   || finish_a    ) ? strrstr( htext->getCString(), "<i>" ) : NULL;
384   const char *last_bold   = finish_bold   && ( finish_italic || finish_a    ) ? strrstr( htext->getCString(), "<b>" ) : NULL;
385   const char *last_a      = finish_a      && ( finish_italic || finish_bold ) ? strrstr( htext->getCString(), "<a " ) : NULL;
386   if( finish_a && ( finish_italic || finish_bold ) && last_a > ( last_italic > last_bold ? last_italic : last_bold ) ){
387     htext->append("</a>", 4);
388     finish_a = false;
389   }
390   if( finish_italic && finish_bold && last_italic > last_bold ){
391     htext->append("</i>", 4);
392     finish_italic = false;
393   }
394   if( finish_bold )
395     htext->append("</b>", 4);
396   if( finish_italic )
397     htext->append("</i>", 4);
398   if( finish_a )
399     htext->append("</a>");
400 }
401 
coalesce()402 void HtmlPage::coalesce() {
403   HtmlString *str1, *str2;
404   HtmlFont *hfont1, *hfont2;
405   double space, horSpace, vertSpace, vertOverlap;
406   GBool addSpace, addLineBreak;
407   int n, i;
408   double curX, curY;
409 
410 #if 0 //~ for debugging
411   for (str1 = yxStrings; str1; str1 = str1->yxNext) {
412     printf("x=%f..%f  y=%f..%f  size=%2d '",
413 	   str1->xMin, str1->xMax, str1->yMin, str1->yMax,
414 	   (int)(str1->yMax - str1->yMin));
415     for (i = 0; i < str1->len; ++i) {
416       fputc(str1->text[i] & 0xff, stdout);
417     }
418     printf("'\n");
419   }
420   printf("\n------------------------------------------------------------\n\n");
421 #endif
422   str1 = yxStrings;
423 
424   if( !str1 ) return;
425 
426   //----- discard duplicated text (fake boldface, drop shadows)
427   if( !complexMode )
428   {	/* if not in complex mode get rid of duplicate strings */
429 	HtmlString *str3;
430 	GBool found;
431   	while (str1)
432 	{
433 		double size = str1->yMax - str1->yMin;
434 		double xLimit = str1->xMin + size * 0.2;
435 		found = gFalse;
436 		for (str2 = str1, str3 = str1->yxNext;
437 			str3 && str3->xMin < xLimit;
438 			str2 = str3, str3 = str2->yxNext)
439 		{
440 			if (str3->len == str1->len &&
441 				!memcmp(str3->text, str1->text, str1->len * sizeof(Unicode)) &&
442 				fabs(str3->yMin - str1->yMin) < size * 0.2 &&
443 				fabs(str3->yMax - str1->yMax) < size * 0.2 &&
444 				fabs(str3->xMax - str1->xMax) < size * 0.2)
445 			{
446 				found = gTrue;
447 				//printf("found duplicate!\n");
448 				break;
449 			}
450 		}
451 		if (found)
452 		{
453 			str2->xyNext = str3->xyNext;
454 			str2->yxNext = str3->yxNext;
455 			delete str3;
456 		}
457 		else
458 		{
459 			str1 = str1->yxNext;
460 		}
461 	}
462   }	/*- !complexMode */
463 
464   str1 = yxStrings;
465 
466   hfont1 = getFont(str1);
467   if( hfont1->isBold() )
468     str1->htext->insert(0,"<b>",3);
469   if( hfont1->isItalic() )
470     str1->htext->insert(0,"<i>",3);
471   if( str1->getLink() != NULL ) {
472     GooString *ls = str1->getLink()->getLinkStart();
473     str1->htext->insert(0, ls);
474     delete ls;
475   }
476   curX = str1->xMin; curY = str1->yMin;
477 
478   while (str1 && (str2 = str1->yxNext)) {
479     hfont2 = getFont(str2);
480     space = str1->yMax - str1->yMin;
481     horSpace = str2->xMin - str1->xMax;
482     addLineBreak = !noMerge && (fabs(str1->xMin - str2->xMin) < 0.4);
483     vertSpace = str2->yMin - str1->yMax;
484 
485 //printf("coalesce %d %d %f? ", str1->dir, str2->dir, d);
486 
487     if (str2->yMin >= str1->yMin && str2->yMin <= str1->yMax)
488     {
489 	vertOverlap = str1->yMax - str2->yMin;
490     } else
491     if (str2->yMax >= str1->yMin && str2->yMax <= str1->yMax)
492     {
493 	vertOverlap = str2->yMax - str1->yMin;
494     } else
495     {
496     	vertOverlap = 0;
497     }
498 
499     if (
500 	(
501 	 (
502 	  (
503 	   (rawOrder && vertOverlap > 0.5 * space)
504 	   ||
505 	   (!rawOrder && str2->yMin < str1->yMax)
506 	  ) &&
507 	  (horSpace > -0.5 * space && horSpace < space)
508 	 ) ||
509        	 (vertSpace >= 0 && vertSpace < 0.5 * space && addLineBreak)
510 	) &&
511 	(!complexMode || (hfont1->isEqualIgnoreBold(*hfont2))) && // in complex mode fonts must be the same, in other modes fonts do not metter
512 	str1->dir == str2->dir // text direction the same
513        )
514     {
515 //      printf("yes\n");
516       n = str1->len + str2->len;
517       if ((addSpace = horSpace > 0.1 * space)) {
518         ++n;
519       }
520       if (addLineBreak) {
521         ++n;
522       }
523 
524       str1->size = (n + 15) & ~15;
525       str1->text = (Unicode *)grealloc(str1->text,
526 				       str1->size * sizeof(Unicode));
527       str1->xRight = (double *)grealloc(str1->xRight,
528 					str1->size * sizeof(double));
529       if (addSpace) {
530 		  str1->text[str1->len] = 0x20;
531 		  str1->htext->append(xml?" ":"&nbsp;");
532 		  str1->xRight[str1->len] = str2->xMin;
533 		  ++str1->len;
534       }
535       if (addLineBreak) {
536 	  str1->text[str1->len] = '\n';
537 	  str1->htext->append("<br>");
538 	  str1->xRight[str1->len] = str2->xMin;
539 	  ++str1->len;
540 	  str1->yMin = str2->yMin;
541 	  str1->yMax = str2->yMax;
542 	  str1->xMax = str2->xMax;
543 	  int fontLineSize = hfont1->getLineSize();
544 	  int curLineSize = (int)(vertSpace + space);
545 	  if( curLineSize != fontLineSize )
546 	  {
547 	      HtmlFont *newfnt = new HtmlFont(*hfont1);
548 	      newfnt->setLineSize(curLineSize);
549 	      str1->fontpos = fonts->AddFont(*newfnt);
550 	      delete newfnt;
551 	      hfont1 = getFont(str1);
552 	      // we have to reget hfont2 because it's location could have
553 	      // changed on resize
554 	      hfont2 = getFont(str2);
555 	  }
556       }
557       for (i = 0; i < str2->len; ++i) {
558 	str1->text[str1->len] = str2->text[i];
559 	str1->xRight[str1->len] = str2->xRight[i];
560 	++str1->len;
561       }
562 
563       /* fix <i>, <b> if str1 and str2 differ and handle switch of links */
564       HtmlLink *hlink1 = str1->getLink();
565       HtmlLink *hlink2 = str2->getLink();
566       bool switch_links = !hlink1 || !hlink2 || !hlink1->isEqualDest(*hlink2);
567       GBool finish_a = switch_links && hlink1 != NULL;
568       GBool finish_italic = hfont1->isItalic() && ( !hfont2->isItalic() || finish_a );
569       GBool finish_bold   = hfont1->isBold()   && ( !hfont2->isBold()   || finish_a || finish_italic );
570       CloseTags( str1->htext, finish_a, finish_italic, finish_bold );
571       if( switch_links && hlink2 != NULL ) {
572         GooString *ls = hlink2->getLinkStart();
573         str1->htext->append(ls);
574         delete ls;
575       }
576       if( ( !hfont1->isItalic() || finish_italic ) && hfont2->isItalic() )
577 	    str1->htext->append("<i>", 3);
578       if( ( !hfont1->isBold() || finish_bold ) && hfont2->isBold() )
579 	    str1->htext->append("<b>", 3);
580 
581 
582       str1->htext->append(str2->htext);
583       // str1 now contains href for link of str2 (if it is defined)
584       str1->link = str2->link;
585       hfont1 = hfont2;
586       if (str2->xMax > str1->xMax) {
587 	str1->xMax = str2->xMax;
588       }
589       if (str2->yMax > str1->yMax) {
590 	str1->yMax = str2->yMax;
591       }
592       str1->yxNext = str2->yxNext;
593       delete str2;
594     } else { // keep strings separate
595 //      printf("no\n");
596       GBool finish_a = str1->getLink() != NULL;
597       GBool finish_bold   = hfont1->isBold();
598       GBool finish_italic = hfont1->isItalic();
599       CloseTags( str1->htext, finish_a, finish_italic, finish_bold );
600 
601       str1->xMin = curX; str1->yMin = curY;
602       str1 = str2;
603       curX = str1->xMin; curY = str1->yMin;
604       hfont1 = hfont2;
605       if( hfont1->isBold() )
606 	str1->htext->insert(0,"<b>",3);
607       if( hfont1->isItalic() )
608 	str1->htext->insert(0,"<i>",3);
609       if( str1->getLink() != NULL ) {
610 	GooString *ls = str1->getLink()->getLinkStart();
611 	str1->htext->insert(0, ls);
612 	delete ls;
613       }
614     }
615   }
616   str1->xMin = curX; str1->yMin = curY;
617 
618   GBool finish_bold   = hfont1->isBold();
619   GBool finish_italic = hfont1->isItalic();
620   GBool finish_a = str1->getLink() != NULL;
621   CloseTags( str1->htext, finish_a, finish_italic, finish_bold );
622 
623 #if 0 //~ for debugging
624   for (str1 = yxStrings; str1; str1 = str1->yxNext) {
625     printf("x=%3d..%3d  y=%3d..%3d  size=%2d ",
626 	   (int)str1->xMin, (int)str1->xMax, (int)str1->yMin, (int)str1->yMax,
627 	   (int)(str1->yMax - str1->yMin));
628     printf("'%s'\n", str1->htext->getCString());
629   }
630   printf("\n------------------------------------------------------------\n\n");
631 #endif
632 
633 }
634 
dumpAsXML(FILE * f,int page)635 void HtmlPage::dumpAsXML(FILE* f,int page){
636   fprintf(f, "<page number=\"%d\" position=\"absolute\"", page);
637   fprintf(f," top=\"0\" left=\"0\" height=\"%d\" width=\"%d\">\n", pageHeight,pageWidth);
638 
639   for(int i=fontsPageMarker;i < fonts->size();i++) {
640     GooString *fontCSStyle = fonts->CSStyle(i);
641     fprintf(f,"\t%s\n",fontCSStyle->getCString());
642     delete fontCSStyle;
643   }
644 
645   GooString *str, *str1 = NULL;
646   for(HtmlString *tmp=yxStrings;tmp;tmp=tmp->yxNext){
647     if (tmp->htext){
648       str=new GooString(tmp->htext);
649       fprintf(f,"<text top=\"%d\" left=\"%d\" ",xoutRound(tmp->yMin),xoutRound(tmp->xMin));
650       fprintf(f,"width=\"%d\" height=\"%d\" ",xoutRound(tmp->xMax-tmp->xMin),xoutRound(tmp->yMax-tmp->yMin));
651       fprintf(f,"font=\"%d\">", tmp->fontpos);
652       str1=fonts->getCSStyle(tmp->fontpos, str);
653       fputs(str1->getCString(),f);
654       delete str;
655       delete str1;
656       fputs("</text>\n",f);
657     }
658   }
659   fputs("</page>\n",f);
660 }
661 
662 
dumpComplex(FILE * file,int page)663 void HtmlPage::dumpComplex(FILE *file, int page){
664   FILE* pageFile;
665   GooString* tmp;
666   char* htmlEncoding;
667 
668   if( firstPage == -1 ) firstPage = page;
669 
670   if( !noframes )
671   {
672       GooString* pgNum=GooString::fromInt(page);
673       tmp = new GooString(DocName);
674       if (!singleHtml){
675             tmp->append('-')->append(pgNum)->append(".html");
676             pageFile = fopen(tmp->getCString(), "w");
677       } else {
678             tmp->append("-html")->append(".html");
679             pageFile = fopen(tmp->getCString(), "a");
680       }
681       delete pgNum;
682       if (!pageFile) {
683 	  error(-1, "Couldn't open html file '%s'", tmp->getCString());
684 	  delete tmp;
685 	  return;
686       }
687 
688       if (!singleHtml)
689           fprintf(pageFile,"%s\n<HTML>\n<HEAD>\n<TITLE>Page %d</TITLE>\n\n", DOCTYPE, page);
690       else
691           fprintf(pageFile,"%s\n<HTML>\n<HEAD>\n<TITLE>%s</TITLE>\n\n", DOCTYPE, tmp->getCString());
692 
693       delete tmp;
694 
695       htmlEncoding = HtmlOutputDev::mapEncodingToHtml
696 	  (globalParams->getTextEncodingName());
697       if (!singleHtml)
698           fprintf(pageFile, "<META http-equiv=\"Content-Type\" content=\"text/html; charset=%s\">\n", htmlEncoding);
699       else
700           fprintf(pageFile, "<META http-equiv=\"Content-Type\" content=\"text/html; charset=%s\">\n <br>\n", htmlEncoding);
701   }
702   else
703   {
704       pageFile = file;
705       fprintf(pageFile,"<!-- Page %d -->\n", page);
706       fprintf(pageFile,"<a name=\"%d\"></a>\n", page);
707   }
708 
709   fprintf(pageFile,"<DIV style=\"position:relative;width:%d;height:%d;\">\n",
710 	pageWidth, pageHeight);
711 
712   tmp=basename(DocName);
713 
714   fputs("<STYLE type=\"text/css\">\n<!--\n",pageFile);
715   for(int i=fontsPageMarker;i!=fonts->size();i++) {
716     GooString *fontCSStyle;
717     if (!singleHtml)
718          fontCSStyle = fonts->CSStyle(i);
719     else
720          fontCSStyle = fonts->CSStyle(i,page);
721     fprintf(pageFile,"\t%s\n",fontCSStyle->getCString());
722     delete fontCSStyle;
723   }
724 
725   fputs("-->\n</STYLE>\n",pageFile);
726 
727   if( !noframes )
728   {
729       fputs("</HEAD>\n<BODY bgcolor=\"#A0A0A0\" vlink=\"blue\" link=\"blue\">\n",pageFile);
730   }
731 
732   if( !ignore )
733   {
734     fprintf(pageFile,
735 	    "<IMG width=\"%d\" height=\"%d\" src=\"%s%03d.%s\" alt=\"background image\">\n",
736 	    pageWidth, pageHeight, tmp->getCString(),
737 		(page-firstPage+1), imgExt->getCString());
738   }
739 
740   delete tmp;
741 
742   GooString *str, *str1 = NULL;
743   for(HtmlString *tmp1=yxStrings;tmp1;tmp1=tmp1->yxNext){
744     if (tmp1->htext){
745       str=new GooString(tmp1->htext);
746       fprintf(pageFile,
747 	      "<DIV style=\"position:absolute;top:%d;left:%d\">",
748 	      xoutRound(tmp1->yMin),
749 	      xoutRound(tmp1->xMin));
750       fputs("<nobr>",pageFile);
751       if (!singleHtml)
752           str1=fonts->getCSStyle(tmp1->fontpos, str);
753       else
754           str1=fonts->getCSStyle(tmp1->fontpos, str, page);
755       fputs(str1->getCString(),pageFile);
756       delete str;
757       delete str1;
758       fputs("</nobr></DIV>\n",pageFile);
759     }
760   }
761 
762   fputs("</DIV>\n", pageFile);
763 
764   if( !noframes )
765   {
766       fputs("</BODY>\n</HTML>\n",pageFile);
767       fclose(pageFile);
768   }
769 }
770 
771 
dump(FILE * f,int pageNum)772 void HtmlPage::dump(FILE *f, int pageNum)
773 {
774   if (complexMode || singleHtml)
775   {
776     if (xml) dumpAsXML(f, pageNum);
777     if (!xml) dumpComplex(f, pageNum);
778   }
779   else
780   {
781     fprintf(f,"<A name=%d></a>",pageNum);
782     // Loop over the list of image names on this page
783     int listlen=HtmlOutputDev::imgList->getLength();
784     for (int i = 0; i < listlen; i++) {
785       GooString *fName= (GooString *)HtmlOutputDev::imgList->del(0);
786       fprintf(f,"<IMG src=\"%s\"><br>\n",fName->getCString());
787       delete fName;
788     }
789     HtmlOutputDev::imgNum=1;
790 
791     GooString* str;
792     for(HtmlString *tmp=yxStrings;tmp;tmp=tmp->yxNext){
793       if (tmp->htext){
794 		str=new GooString(tmp->htext);
795 		fputs(str->getCString(),f);
796 		delete str;
797 		fputs("<br>\n",f);
798       }
799     }
800 	fputs("<hr>\n",f);
801   }
802 }
803 
804 
805 
clear()806 void HtmlPage::clear() {
807   HtmlString *p1, *p2;
808 
809   if (curStr) {
810     delete curStr;
811     curStr = NULL;
812   }
813   for (p1 = yxStrings; p1; p1 = p2) {
814     p2 = p1->yxNext;
815     delete p1;
816   }
817   yxStrings = NULL;
818   xyStrings = NULL;
819   yxCur1 = yxCur2 = NULL;
820 
821   if( !noframes )
822   {
823       delete fonts;
824       fonts=new HtmlFontAccu();
825       fontsPageMarker = 0;
826   }
827   else
828   {
829       fontsPageMarker = fonts->size();
830   }
831 
832   delete links;
833   links=new HtmlLinks();
834 
835 
836 }
837 
setDocName(char * fname)838 void HtmlPage::setDocName(char *fname){
839   DocName=new GooString(fname);
840 }
841 
842 //------------------------------------------------------------------------
843 // HtmlMetaVar
844 //------------------------------------------------------------------------
845 
HtmlMetaVar(char * _name,char * _content)846 HtmlMetaVar::HtmlMetaVar(char *_name, char *_content)
847 {
848     name = new GooString(_name);
849     content = new GooString(_content);
850 }
851 
~HtmlMetaVar()852 HtmlMetaVar::~HtmlMetaVar()
853 {
854    delete name;
855    delete content;
856 }
857 
toString()858 GooString* HtmlMetaVar::toString()
859 {
860     GooString *result = new GooString("<META name=\"");
861     result->append(name);
862     result->append("\" content=\"");
863     result->append(content);
864     result->append("\">");
865     return result;
866 }
867 
868 //------------------------------------------------------------------------
869 // HtmlOutputDev
870 //------------------------------------------------------------------------
871 
872 static char* HtmlEncodings[][2] = {
873     {"Latin1", "ISO-8859-1"},
874     {NULL, NULL}
875 };
876 
877 
mapEncodingToHtml(GooString * encoding)878 char* HtmlOutputDev::mapEncodingToHtml(GooString* encoding)
879 {
880     char* enc = encoding->getCString();
881     for(int i = 0; HtmlEncodings[i][0] != NULL; i++)
882     {
883 	if( strcmp(enc, HtmlEncodings[i][0]) == 0 )
884 	{
885 	    return HtmlEncodings[i][1];
886 	}
887     }
888     return enc;
889 }
890 
doFrame(int firstPage)891 void HtmlOutputDev::doFrame(int firstPage){
892   GooString* fName=new GooString(Docname);
893   char* htmlEncoding;
894   fName->append(".html");
895 
896   if (!(fContentsFrame = fopen(fName->getCString(), "w"))){
897     error(-1, "Couldn't open html file '%s'", fName->getCString());
898     delete fName;
899     return;
900   }
901 
902   delete fName;
903 
904   fName=basename(Docname);
905   fputs(DOCTYPE_FRAMES, fContentsFrame);
906   fputs("\n<HTML>",fContentsFrame);
907   fputs("\n<HEAD>",fContentsFrame);
908   fprintf(fContentsFrame,"\n<TITLE>%s</TITLE>",docTitle->getCString());
909   htmlEncoding = mapEncodingToHtml(globalParams->getTextEncodingName());
910   fprintf(fContentsFrame, "\n<META http-equiv=\"Content-Type\" content=\"text/html; charset=%s\">\n", htmlEncoding);
911   dumpMetaVars(fContentsFrame);
912   fprintf(fContentsFrame, "</HEAD>\n");
913   fputs("<FRAMESET cols=\"100,*\">\n",fContentsFrame);
914   fprintf(fContentsFrame,"<FRAME name=\"links\" src=\"%s_ind.html\">\n",fName->getCString());
915   fputs("<FRAME name=\"contents\" src=",fContentsFrame);
916   if (complexMode)
917       fprintf(fContentsFrame,"\"%s-%d.html\"",fName->getCString(), firstPage);
918   else
919       fprintf(fContentsFrame,"\"%ss.html\"",fName->getCString());
920 
921   fputs(">\n</FRAMESET>\n</HTML>\n",fContentsFrame);
922 
923   delete fName;
924   fclose(fContentsFrame);
925 }
926 
HtmlOutputDev(char * fileName,char * title,char * author,char * keywords,char * subject,char * date,char * extension,GBool rawOrder,int firstPage,GBool outline)927 HtmlOutputDev::HtmlOutputDev(char *fileName, char *title,
928 	char *author, char *keywords, char *subject, char *date,
929 	char *extension,
930 	GBool rawOrder, int firstPage, GBool outline)
931 {
932   char *htmlEncoding;
933 
934   fContentsFrame = NULL;
935   docTitle = new GooString(title);
936   pages = NULL;
937   dumpJPEG=gTrue;
938   //write = gTrue;
939   this->rawOrder = rawOrder;
940   this->doOutline = outline;
941   ok = gFalse;
942   imgNum=1;
943   //this->firstPage = firstPage;
944   //pageNum=firstPage;
945   // open file
946   needClose = gFalse;
947   pages = new HtmlPage(rawOrder, extension);
948 
949   glMetaVars = new GooList();
950   glMetaVars->append(new HtmlMetaVar("generator", "pdftohtml 0.36"));
951   if( author ) glMetaVars->append(new HtmlMetaVar("author", author));
952   if( keywords ) glMetaVars->append(new HtmlMetaVar("keywords", keywords));
953   if( date ) glMetaVars->append(new HtmlMetaVar("date", date));
954   if( subject ) glMetaVars->append(new HtmlMetaVar("subject", subject));
955 
956   maxPageWidth = 0;
957   maxPageHeight = 0;
958 
959   pages->setDocName(fileName);
960   Docname=new GooString (fileName);
961 
962   // for non-xml output (complex or simple) with frames generate the left frame
963   if(!xml && !noframes)
964   {
965      if (!singleHtml)
966      {
967          GooString* left=new GooString(fileName);
968          left->append("_ind.html");
969 
970          doFrame(firstPage);
971 
972          if (!(fContentsFrame = fopen(left->getCString(), "w")))
973          {
974              error(-1, "Couldn't open html file '%s'", left->getCString());
975              delete left;
976              return;
977          }
978          delete left;
979          fputs(DOCTYPE, fContentsFrame);
980          fputs("<HTML>\n<HEAD>\n<TITLE></TITLE>\n</HEAD>\n<BODY>\n",fContentsFrame);
981 
982          if (doOutline)
983          {
984              GooString *str = basename(Docname);
985              fprintf(fContentsFrame, "<A href=\"%s%s\" target=\"contents\">Outline</a><br>", str->getCString(), complexMode ? "-outline.html" : "s.html#outline");
986              delete str;
987          }
988      }
989 	if (!complexMode)
990 	{	/* not in complex mode */
991 
992        GooString* right=new GooString(fileName);
993        right->append("s.html");
994 
995        if (!(page=fopen(right->getCString(),"w"))){
996         error(-1, "Couldn't open html file '%s'", right->getCString());
997         delete right;
998 		return;
999        }
1000        delete right;
1001        fputs(DOCTYPE, page);
1002        fputs("<HTML>\n<HEAD>\n<TITLE></TITLE>\n</HEAD>\n<BODY>\n",page);
1003      }
1004   }
1005 
1006   if (noframes) {
1007     if (stout) page=stdout;
1008     else {
1009       GooString* right=new GooString(fileName);
1010       if (!xml) right->append(".html");
1011       if (xml) right->append(".xml");
1012       if (!(page=fopen(right->getCString(),"w"))){
1013 	error(-1, "Couldn't open html file '%s'", right->getCString());
1014 	delete right;
1015 	return;
1016       }
1017       delete right;
1018     }
1019 
1020     htmlEncoding = mapEncodingToHtml(globalParams->getTextEncodingName());
1021     if (xml)
1022     {
1023       fprintf(page, "<?xml version=\"1.0\" encoding=\"%s\"?>\n", htmlEncoding);
1024       fputs("<!DOCTYPE pdf2xml SYSTEM \"pdf2xml.dtd\">\n\n", page);
1025       fputs("<pdf2xml>\n",page);
1026     }
1027     else
1028     {
1029       fprintf(page,"%s\n<HTML>\n<HEAD>\n<TITLE>%s</TITLE>\n",
1030 	      DOCTYPE, docTitle->getCString());
1031 
1032       fprintf(page, "<META http-equiv=\"Content-Type\" content=\"text/html; charset=%s\">\n", htmlEncoding);
1033 
1034       dumpMetaVars(page);
1035       fprintf(page,"</HEAD>\n");
1036       fprintf(page,"<BODY bgcolor=\"#A0A0A0\" vlink=\"blue\" link=\"blue\">\n");
1037     }
1038   }
1039   ok = gTrue;
1040 }
1041 
~HtmlOutputDev()1042 HtmlOutputDev::~HtmlOutputDev() {
1043     HtmlFont::clear();
1044 
1045     delete Docname;
1046     delete docTitle;
1047 
1048     deleteGooList(glMetaVars, HtmlMetaVar);
1049 
1050     if (fContentsFrame){
1051       fputs("</BODY>\n</HTML>\n",fContentsFrame);
1052       fclose(fContentsFrame);
1053     }
1054     if (xml) {
1055       fputs("</pdf2xml>\n",page);
1056       fclose(page);
1057     } else
1058     if ( !complexMode || xml || noframes )
1059     {
1060       fputs("</BODY>\n</HTML>\n",page);
1061       fclose(page);
1062     }
1063     if (pages)
1064       delete pages;
1065 }
1066 
startPage(int pageNum,GfxState * state)1067 void HtmlOutputDev::startPage(int pageNum, GfxState *state) {
1068 #if 0
1069   if (mode&&!xml){
1070     if (write){
1071       write=gFalse;
1072       GooString* fname=Dirname(Docname);
1073       fname->append("image.log");
1074       if((tin=fopen(getFileNameFromPath(fname->getCString(),fname->getLength()),"w"))==NULL){
1075 	printf("Error : can not open %s",fname);
1076 	exit(1);
1077       }
1078       delete fname;
1079     // if(state->getRotation()!=0)
1080     //  fprintf(tin,"ROTATE=%d rotate %d neg %d neg translate\n",state->getRotation(),state->getX1(),-state->getY1());
1081     // else
1082       fprintf(tin,"ROTATE=%d neg %d neg translate\n",state->getX1(),state->getY1());
1083     }
1084   }
1085 #endif
1086 
1087   this->pageNum = pageNum;
1088   GooString *str=basename(Docname);
1089   pages->clear();
1090   if(!noframes)
1091   {
1092     if (fContentsFrame)
1093 	{
1094       if (complexMode)
1095 		fprintf(fContentsFrame,"<A href=\"%s-%d.html\"",str->getCString(),pageNum);
1096       else
1097 		fprintf(fContentsFrame,"<A href=\"%ss.html#%d\"",str->getCString(),pageNum);
1098       fprintf(fContentsFrame," target=\"contents\" >Page %d</a><br>\n",pageNum);
1099     }
1100   }
1101 
1102   pages->pageWidth=static_cast<int>(state->getPageWidth());
1103   pages->pageHeight=static_cast<int>(state->getPageHeight());
1104 
1105   delete str;
1106 }
1107 
1108 
endPage()1109 void HtmlOutputDev::endPage() {
1110   Links *linksList = docPage->getLinks(catalog);
1111   for (int i = 0; i < linksList->getNumLinks(); ++i)
1112   {
1113       doProcessLink(linksList->getLink(i));
1114   }
1115   delete linksList;
1116 
1117   pages->conv();
1118   pages->coalesce();
1119   pages->dump(page, pageNum);
1120 
1121   // I don't yet know what to do in the case when there are pages of different
1122   // sizes and we want complex output: running ghostscript many times
1123   // seems very inefficient. So for now I'll just use last page's size
1124   maxPageWidth = pages->pageWidth;
1125   maxPageHeight = pages->pageHeight;
1126 
1127   //if(!noframes&&!xml) fputs("<br>\n", fContentsFrame);
1128   if(!stout && !globalParams->getErrQuiet()) printf("Page-%d\n",(pageNum));
1129 }
1130 
updateFont(GfxState * state)1131 void HtmlOutputDev::updateFont(GfxState *state) {
1132   pages->updateFont(state);
1133 }
1134 
beginString(GfxState * state,GooString * s)1135 void HtmlOutputDev::beginString(GfxState *state, GooString *s) {
1136   pages->beginString(state, s);
1137 }
1138 
endString(GfxState * state)1139 void HtmlOutputDev::endString(GfxState *state) {
1140   pages->endString();
1141 }
1142 
drawChar(GfxState * state,double x,double y,double dx,double dy,double originX,double originY,CharCode code,int,Unicode * u,int uLen)1143 void HtmlOutputDev::drawChar(GfxState *state, double x, double y,
1144 	      double dx, double dy,
1145 	      double originX, double originY,
1146 	      CharCode code, int /*nBytes*/, Unicode *u, int uLen)
1147 {
1148   if ( !showHidden && (state->getRender() & 3) == 3) {
1149     return;
1150   }
1151   pages->addChar(state, x, y, dx, dy, originX, originY, u, uLen);
1152 }
1153 
drawImageMask(GfxState * state,Object * ref,Stream * str,int width,int height,GBool invert,GBool interpolate,GBool inlineImg)1154 void HtmlOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
1155 				  int width, int height, GBool invert,
1156 				  GBool interpolate, GBool inlineImg) {
1157 
1158   if (ignore||complexMode) {
1159     OutputDev::drawImageMask(state, ref, str, width, height, invert, interpolate, inlineImg);
1160     return;
1161   }
1162 
1163   FILE *f1;
1164   int c;
1165 
1166   int x0, y0;			// top left corner of image
1167   int w0, h0, w1, h1;		// size of image
1168   double xt, yt, wt, ht;
1169   GBool rotate, xFlip, yFlip;
1170 
1171   // get image position and size
1172   state->transform(0, 0, &xt, &yt);
1173   state->transformDelta(1, 1, &wt, &ht);
1174   if (wt > 0) {
1175     x0 = xoutRound(xt);
1176     w0 = xoutRound(wt);
1177   } else {
1178     x0 = xoutRound(xt + wt);
1179     w0 = xoutRound(-wt);
1180   }
1181   if (ht > 0) {
1182     y0 = xoutRound(yt);
1183     h0 = xoutRound(ht);
1184   } else {
1185     y0 = xoutRound(yt + ht);
1186     h0 = xoutRound(-ht);
1187   }
1188   state->transformDelta(1, 0, &xt, &yt);
1189   rotate = fabs(xt) < fabs(yt);
1190   if (rotate) {
1191     w1 = h0;
1192     h1 = w0;
1193     xFlip = ht < 0;
1194     yFlip = wt > 0;
1195   } else {
1196     w1 = w0;
1197     h1 = h0;
1198     xFlip = wt < 0;
1199     yFlip = ht > 0;
1200   }
1201 
1202   // dump JPEG file
1203   if (dumpJPEG  && str->getKind() == strDCT) {
1204     GooString *fName=new GooString(Docname);
1205     fName->append("-");
1206     GooString *pgNum=GooString::fromInt(pageNum);
1207     GooString *imgnum=GooString::fromInt(imgNum);
1208     // open the image file
1209     fName->append(pgNum)->append("_")->append(imgnum)->append(".jpg");
1210     delete pgNum;
1211     delete imgnum;
1212 
1213     ++imgNum;
1214     if (!(f1 = fopen(fName->getCString(), "wb"))) {
1215       error(-1, "Couldn't open image file '%s'", fName->getCString());
1216       delete fName;
1217       return;
1218     }
1219 
1220     // initialize stream
1221     str = ((DCTStream *)str)->getRawStream();
1222     str->reset();
1223 
1224     // copy the stream
1225     while ((c = str->getChar()) != EOF)
1226       fputc(c, f1);
1227 
1228     fclose(f1);
1229 
1230   if (fName) imgList->append(fName);
1231   }
1232   else {
1233     OutputDev::drawImageMask(state, ref, str, width, height, invert, interpolate, inlineImg);
1234   }
1235 }
1236 
drawImage(GfxState * state,Object * ref,Stream * str,int width,int height,GfxImageColorMap * colorMap,GBool interpolate,int * maskColors,GBool inlineImg)1237 void HtmlOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
1238 			      int width, int height, GfxImageColorMap *colorMap,
1239 			      GBool interpolate, int *maskColors, GBool inlineImg) {
1240 
1241   if (ignore||complexMode) {
1242     OutputDev::drawImage(state, ref, str, width, height, colorMap, interpolate,
1243 			 maskColors, inlineImg);
1244     return;
1245   }
1246 
1247   FILE *f1;
1248   int c;
1249 
1250   int x0, y0;			// top left corner of image
1251   int w0, h0, w1, h1;		// size of image
1252   double xt, yt, wt, ht;
1253   GBool rotate, xFlip, yFlip;
1254 
1255   // get image position and size
1256   state->transform(0, 0, &xt, &yt);
1257   state->transformDelta(1, 1, &wt, &ht);
1258   if (wt > 0) {
1259     x0 = xoutRound(xt);
1260     w0 = xoutRound(wt);
1261   } else {
1262     x0 = xoutRound(xt + wt);
1263     w0 = xoutRound(-wt);
1264   }
1265   if (ht > 0) {
1266     y0 = xoutRound(yt);
1267     h0 = xoutRound(ht);
1268   } else {
1269     y0 = xoutRound(yt + ht);
1270     h0 = xoutRound(-ht);
1271   }
1272   state->transformDelta(1, 0, &xt, &yt);
1273   rotate = fabs(xt) < fabs(yt);
1274   if (rotate) {
1275     w1 = h0;
1276     h1 = w0;
1277     xFlip = ht < 0;
1278     yFlip = wt > 0;
1279   } else {
1280     w1 = w0;
1281     h1 = h0;
1282     xFlip = wt < 0;
1283     yFlip = ht > 0;
1284   }
1285 
1286 
1287   /*if( !globalParams->getErrQuiet() )
1288     printf("image stream of kind %d\n", str->getKind());*/
1289   // dump JPEG file
1290   if (dumpJPEG && str->getKind() == strDCT) {
1291     GooString *fName=new GooString(Docname);
1292     fName->append("-");
1293     GooString *pgNum= GooString::fromInt(pageNum);
1294     GooString *imgnum= GooString::fromInt(imgNum);
1295 
1296     // open the image file
1297     fName->append(pgNum)->append("_")->append(imgnum)->append(".jpg");
1298     delete pgNum;
1299     delete imgnum;
1300 
1301     ++imgNum;
1302 
1303     if (!(f1 = fopen(fName->getCString(), "wb"))) {
1304       error(-1, "Couldn't open image file '%s'", fName->getCString());
1305       delete fName;
1306       return;
1307     }
1308 
1309     // initialize stream
1310     str = ((DCTStream *)str)->getRawStream();
1311     str->reset();
1312 
1313     // copy the stream
1314     while ((c = str->getChar()) != EOF)
1315       fputc(c, f1);
1316 
1317     fclose(f1);
1318 
1319     if (fName) imgList->append(fName);
1320   }
1321   else {
1322 #ifdef ENABLE_LIBPNG
1323     // Dump the image as a PNG file. Much of the PNG code
1324     // comes from an example by Guillaume Cottenceau.
1325     Guchar *p;
1326     GfxRGB rgb;
1327     png_byte *row = (png_byte *) malloc(3 * width);   // 3 bytes/pixel: RGB
1328     png_bytep *row_pointer= &row;
1329 
1330     // Create the image filename
1331     GooString *fName=new GooString(Docname);
1332     fName->append("-");
1333     GooString *pgNum= GooString::fromInt(pageNum);
1334     GooString *imgnum= GooString::fromInt(imgNum);
1335     fName->append(pgNum)->append("_")->append(imgnum)->append(".png");
1336     delete pgNum;
1337     delete imgnum;
1338 
1339     // Open the image file
1340     if (!(f1 = fopen(fName->getCString(), "wb"))) {
1341       error(-1, "Couldn't open image file '%s'", fName->getCString());
1342       delete fName;
1343       return;
1344     }
1345 
1346     PNGWriter *writer = new PNGWriter();
1347     // TODO can we calculate the resolution of the image?
1348     if (!writer->init(f1, width, height, 72, 72)) {
1349         delete writer;
1350         fclose(f1);
1351         return;
1352     }
1353 
1354     // Initialize the image stream
1355     ImageStream *imgStr = new ImageStream(str, width,
1356                         colorMap->getNumPixelComps(), colorMap->getBits());
1357     imgStr->reset();
1358 
1359     // For each line...
1360     for (int y = 0; y < height; y++) {
1361 
1362       // Convert into a PNG row
1363       p = imgStr->getLine();
1364       for (int x = 0; x < width; x++) {
1365         colorMap->getRGB(p, &rgb);
1366 	// Write the RGB pixels into the row
1367 	row[3*x]= colToByte(rgb.r);
1368 	row[3*x+1]= colToByte(rgb.g);
1369 	row[3*x+2]= colToByte(rgb.b);
1370          p += colorMap->getNumPixelComps();
1371       }
1372 
1373       if (!writer->writeRow(row_pointer)) {
1374         delete writer;
1375         fclose(f1);
1376         return;
1377       }
1378     }
1379 
1380     writer->close();
1381     delete writer;
1382     fclose(f1);
1383 
1384     free(row);
1385     imgList->append(fName);
1386     ++imgNum;
1387     imgStr->close();
1388     delete imgStr;
1389 #else
1390     OutputDev::drawImage(state, ref, str, width, height, colorMap, interpolate,
1391 			 maskColors, inlineImg);
1392 #endif
1393   }
1394 }
1395 
1396 
1397 
doProcessLink(Link * link)1398 void HtmlOutputDev::doProcessLink(Link* link){
1399   double _x1,_y1,_x2,_y2;
1400   int x1,y1,x2,y2;
1401 
1402   link->getRect(&_x1,&_y1,&_x2,&_y2);
1403   cvtUserToDev(_x1,_y1,&x1,&y1);
1404 
1405   cvtUserToDev(_x2,_y2,&x2,&y2);
1406 
1407 
1408   GooString* _dest=getLinkDest(link,catalog);
1409   HtmlLink t((double) x1,(double) y2,(double) x2,(double) y1,_dest);
1410   pages->AddLink(t);
1411   delete _dest;
1412 }
1413 
getLinkDest(Link * link,Catalog * catalog)1414 GooString* HtmlOutputDev::getLinkDest(Link *link,Catalog* catalog){
1415   char *p;
1416   switch(link->getAction()->getKind())
1417   {
1418       case actionGoTo:
1419 	  {
1420 	  GooString* file=basename(Docname);
1421 	  int page=1;
1422 	  LinkGoTo *ha=(LinkGoTo *)link->getAction();
1423 	  LinkDest *dest=NULL;
1424 	  if (ha->getDest()!=NULL)
1425 	      dest=ha->getDest()->copy();
1426 	  else if (ha->getNamedDest()!=NULL)
1427 	      dest=catalog->findDest(ha->getNamedDest());
1428 
1429 	  if (dest){
1430 	      if (dest->isPageRef()){
1431 		  Ref pageref=dest->getPageRef();
1432 		  page=catalog->findPage(pageref.num,pageref.gen);
1433 	      }
1434 	      else {
1435 		  page=dest->getPageNum();
1436 	      }
1437 
1438 	      delete dest;
1439 
1440 	      GooString *str=GooString::fromInt(page);
1441 	      /* 		complex 	simple
1442 	       	frames		file-4.html	files.html#4
1443 		noframes	file.html#4	file.html#4
1444 	       */
1445 	      if (noframes)
1446 	      {
1447 		  file->append(".html#");
1448 		  file->append(str);
1449 	      }
1450 	      else
1451 	      {
1452 	      	if( complexMode )
1453 		{
1454 		    file->append("-");
1455 		    file->append(str);
1456 		    file->append(".html");
1457 		}
1458 		else
1459 		{
1460 		    file->append("s.html#");
1461 		    file->append(str);
1462 		}
1463 	      }
1464 
1465 	      if (printCommands) printf(" link to page %d ",page);
1466 	      delete str;
1467 	      return file;
1468 	  }
1469 	  else
1470 	  {
1471 	      return new GooString();
1472 	  }
1473 	  }
1474       case actionGoToR:
1475 	  {
1476 	  LinkGoToR *ha=(LinkGoToR *) link->getAction();
1477 	  LinkDest *dest=NULL;
1478 	  int page=1;
1479 	  GooString *file=new GooString();
1480 	  if (ha->getFileName()){
1481 	      delete file;
1482 	      file=new GooString(ha->getFileName()->getCString());
1483 	  }
1484 	  if (ha->getDest()!=NULL)  dest=ha->getDest()->copy();
1485 	  if (dest&&file){
1486 	      if (!(dest->isPageRef()))  page=dest->getPageNum();
1487 	      delete dest;
1488 
1489 	      if (printCommands) printf(" link to page %d ",page);
1490 	      if (printHtml){
1491 		  p=file->getCString()+file->getLength()-4;
1492 		  if (!strcmp(p, ".pdf") || !strcmp(p, ".PDF")){
1493 		      file->del(file->getLength()-4,4);
1494 		      file->append(".html");
1495 		  }
1496 		  file->append('#');
1497 		  file->append(GooString::fromInt(page));
1498 	      }
1499 	  }
1500 	  if (printCommands && file) printf("filename %s\n",file->getCString());
1501 	  return file;
1502 	  }
1503       case actionURI:
1504 	  {
1505 	  LinkURI *ha=(LinkURI *) link->getAction();
1506 	  GooString* file=new GooString(ha->getURI()->getCString());
1507 	  // printf("uri : %s\n",file->getCString());
1508 	  return file;
1509 	  }
1510       case actionLaunch:
1511 	  {
1512 	  LinkLaunch *ha=(LinkLaunch *) link->getAction();
1513 	  GooString* file=new GooString(ha->getFileName()->getCString());
1514 	  if (printHtml) {
1515 	      p=file->getCString()+file->getLength()-4;
1516 	      if (!strcmp(p, ".pdf") || !strcmp(p, ".PDF")){
1517 		  file->del(file->getLength()-4,4);
1518 		  file->append(".html");
1519 	      }
1520 	      if (printCommands) printf("filename %s",file->getCString());
1521 
1522 	      return file;
1523 
1524 	  }
1525 	  }
1526       default:
1527 	  return new GooString();
1528   }
1529 }
1530 
dumpMetaVars(FILE * file)1531 void HtmlOutputDev::dumpMetaVars(FILE *file)
1532 {
1533   GooString *var;
1534 
1535   for(int i = 0; i < glMetaVars->getLength(); i++)
1536   {
1537      HtmlMetaVar *t = (HtmlMetaVar*)glMetaVars->get(i);
1538      var = t->toString();
1539      fprintf(file, "%s\n", var->getCString());
1540      delete var;
1541   }
1542 }
1543 
dumpDocOutline(Catalog * catalog)1544 GBool HtmlOutputDev::dumpDocOutline(Catalog* catalog)
1545 {
1546 	FILE * output = NULL;
1547 	GBool bClose = gFalse;
1548 
1549 	if (!ok || xml)
1550     	return gFalse;
1551 
1552 	Object *outlines = catalog->getOutline();
1553   	if (!outlines->isDict())
1554     	return gFalse;
1555 
1556 	if (!complexMode && !xml)
1557   	{
1558 		output = page;
1559   	}
1560   	else if (complexMode && !xml)
1561 	{
1562 		if (noframes)
1563 		{
1564 			output = page;
1565 			fputs("<hr>\n", output);
1566 		}
1567 		else
1568 		{
1569 			GooString *str = Docname->copy();
1570 			str->append("-outline.html");
1571 			output = fopen(str->getCString(), "w");
1572 			if (output == NULL)
1573 				return gFalse;
1574 			delete str;
1575 			bClose = gTrue;
1576      		fputs("<HTML>\n<HEAD>\n<TITLE>Document Outline</TITLE>\n</HEAD>\n<BODY>\n", output);
1577 		}
1578 	}
1579 
1580   	GBool done = newOutlineLevel(output, outlines, catalog);
1581   	if (done && !complexMode)
1582     	fputs("<hr>\n", output);
1583 
1584 	if (bClose)
1585 	{
1586 		fputs("</BODY>\n</HTML>\n", output);
1587 		fclose(output);
1588 	}
1589   	return done;
1590 }
1591 
newOutlineLevel(FILE * output,Object * node,Catalog * catalog,int level)1592 GBool HtmlOutputDev::newOutlineLevel(FILE *output, Object *node, Catalog* catalog, int level)
1593 {
1594   Object curr, next;
1595   GBool atLeastOne = gFalse;
1596 
1597   if (node->dictLookup("First", &curr)->isDict()) {
1598     if (level == 1)
1599 	{
1600 		fputs("<A name=\"outline\"></a>", output);
1601 		fputs("<h1>Document Outline</h1>\n", output);
1602 	}
1603     fputs("<ul>",output);
1604     do {
1605       // get title, give up if not found
1606       Object title;
1607       if (curr.dictLookup("Title", &title)->isNull()) {
1608 		title.free();
1609 		break;
1610       }
1611       GooString *titleStr = new GooString(title.getString());
1612       title.free();
1613 
1614       // get corresponding link
1615       // Note: some code duplicated from HtmlOutputDev::getLinkDest().
1616       GooString *linkName = NULL;;
1617       Object dest;
1618       if (!curr.dictLookup("Dest", &dest)->isNull()) {
1619 		LinkGoTo *link = new LinkGoTo(&dest);
1620 		LinkDest *linkdest=NULL;
1621 		if (link->getDest()!=NULL)
1622 			linkdest=link->getDest()->copy();
1623 		else if (link->getNamedDest()!=NULL)
1624 			linkdest=catalog->findDest(link->getNamedDest());
1625 
1626 		delete link;
1627 		if (linkdest) {
1628 	  		int page;
1629 	  		if (linkdest->isPageRef()) {
1630 	    		Ref pageref=linkdest->getPageRef();
1631 	    		page=catalog->findPage(pageref.num,pageref.gen);
1632 	  		} else {
1633 	    		page=linkdest->getPageNum();
1634 	  		}
1635 	  		delete linkdest;
1636 
1637 			/* 			complex 	simple
1638 			frames		file-4.html	files.html#4
1639 			noframes	file.html#4	file.html#4
1640 	   		*/
1641 	  		linkName=basename(Docname);
1642 	  		GooString *str=GooString::fromInt(page);
1643 	  		if (noframes) {
1644 	    		linkName->append(".html#");
1645 				linkName->append(str);
1646 	  		} else {
1647     			if( complexMode ) {
1648 	   		   		linkName->append("-");
1649 	      			linkName->append(str);
1650 	      			linkName->append(".html");
1651 	    		} else {
1652 	      			linkName->append("s.html#");
1653 	      			linkName->append(str);
1654 	    		}
1655 	  		}
1656 			delete str;
1657 		}
1658       }
1659       dest.free();
1660 
1661       fputs("<li>",output);
1662       if (linkName)
1663 		fprintf(output,"<A href=\"%s\">", linkName->getCString());
1664       fputs(titleStr->getCString(),output);
1665       if (linkName) {
1666 		fputs("</A>",output);
1667 		delete linkName;
1668       }
1669       fputs("\n",output);
1670       delete titleStr;
1671       atLeastOne = gTrue;
1672 
1673       newOutlineLevel(output, &curr, catalog, level+1);
1674       curr.dictLookup("Next", &next);
1675       curr.free();
1676       curr = next;
1677     } while(curr.isDict());
1678     fputs("</ul>",output);
1679   }
1680   curr.free();
1681 
1682   return atLeastOne;
1683 }
1684