2 implements a pdf output device (OutputDev).
4 This file is part of swftools.
6 Swftools is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 Swftools is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with swftools; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
25 #include "../config.h"
29 #ifdef HAVE_SYS_STAT_H
32 #ifdef HAVE_FONTCONFIG_H
33 #include <fontconfig.h>
49 #include "OutputDev.h"
52 #include "CharCodeToUnicode.h"
53 #include "NameToUnicodeTable.h"
54 #include "GlobalParams.h"
59 #include "FoFiType1C.h"
60 #include "FoFiTrueType.h"
62 #include "SWFOutputDev.h"
64 //swftools header files
65 #include "swfoutput.h"
66 #include "../lib/log.h"
67 #include "../lib/gfxdevice.h"
72 typedef struct _fontfile
79 static fontfile_t fonts[2048];
80 static int fontnum = 0;
82 static int config_use_fontconfig = 1;
85 // TODO: move into pdf_doc_t
87 static int pagebuflen = 0;
88 static int pagepos = 0;
91 static double caplinewidth = 3.0;
92 static int zoom = 72; /* xpdf: 86 */
93 static int forceType0Fonts = 0;
95 static void printInfoString(Dict *infoDict, char *key, char *fmt);
96 static void printInfoDate(Dict *infoDict, char *key, char *fmt);
102 {"Times-Roman", "n021003l"},
103 {"Times-Italic", "n021023l"},
104 {"Times-Bold", "n021004l"},
105 {"Times-BoldItalic", "n021024l"},
106 {"Helvetica", "n019003l"},
107 {"Helvetica-Oblique", "n019023l"},
108 {"Helvetica-Bold", "n019004l"},
109 {"Helvetica-BoldOblique", "n019024l"},
110 {"Courier", "n022003l"},
111 {"Courier-Oblique", "n022023l"},
112 {"Courier-Bold", "n022004l"},
113 {"Courier-BoldOblique", "n022024l"},
114 {"Symbol", "s050000l"},
115 {"ZapfDingbats", "d050000l"}};
117 class SWFOutputState {
123 this->textRender = 0;
127 class SWFOutputDev: public OutputDev {
129 struct swfoutput output;
136 virtual ~SWFOutputDev() ;
138 void setMove(int x,int y);
139 void setClip(int x1,int y1,int x2,int y2);
141 int save(char*filename);
145 void getDimensions(int*x1,int*y1,int*x2,int*y2);
147 //----- get info about output device
149 // Does this device use upside-down coordinates?
150 // (Upside-down means (0,0) is the top left corner of the page.)
151 virtual GBool upsideDown();
153 // Does this device use drawChar() or drawString()?
154 virtual GBool useDrawChar();
156 // Can this device draw gradients?
157 virtual GBool useGradients();
159 virtual GBool interpretType3Chars() {return gTrue;}
161 //----- initialization and control
163 void setXRef(PDFDoc*doc, XRef *xref);
166 virtual void startPage(int pageNum, GfxState *state, double x1, double y1, double x2, double y2) ;
169 virtual void drawLink(Link *link, Catalog *catalog) ;
171 //----- save/restore graphics state
172 virtual void saveState(GfxState *state) ;
173 virtual void restoreState(GfxState *state) ;
175 //----- update graphics state
177 virtual void updateFont(GfxState *state);
178 virtual void updateFillColor(GfxState *state);
179 virtual void updateStrokeColor(GfxState *state);
180 virtual void updateLineWidth(GfxState *state);
181 virtual void updateLineJoin(GfxState *state);
182 virtual void updateLineCap(GfxState *state);
184 virtual void updateAll(GfxState *state)
187 updateFillColor(state);
188 updateStrokeColor(state);
189 updateLineWidth(state);
190 updateLineJoin(state);
191 updateLineCap(state);
194 //----- path painting
195 virtual void stroke(GfxState *state) ;
196 virtual void fill(GfxState *state) ;
197 virtual void eoFill(GfxState *state) ;
199 //----- path clipping
200 virtual void clip(GfxState *state) ;
201 virtual void eoClip(GfxState *state) ;
204 virtual void beginString(GfxState *state, GString *s) ;
205 virtual void endString(GfxState *state) ;
206 virtual void endTextObject(GfxState *state);
207 virtual void drawChar(GfxState *state, double x, double y,
208 double dx, double dy,
209 double originX, double originY,
210 CharCode code, Unicode *u, int uLen);
212 //----- image drawing
213 virtual void drawImageMask(GfxState *state, Object *ref, Stream *str,
214 int width, int height, GBool invert,
216 virtual void drawImage(GfxState *state, Object *ref, Stream *str,
217 int width, int height, GfxImageColorMap *colorMap,
218 int *maskColors, GBool inlineImg);
220 virtual GBool beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode code, Unicode *u, int uLen);
221 virtual void endType3Char(GfxState *state);
223 virtual void type3D0(GfxState *state, double wx, double wy);
224 virtual void type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury);
227 void drawGeneralImage(GfxState *state, Object *ref, Stream *str,
228 int width, int height, GfxImageColorMap*colorMap, GBool invert,
229 GBool inlineImg, int mask, int *maskColors);
230 SWFOutputState states[64];
238 char* searchFont(char*name);
239 char* substituteFont(GfxFont*gfxFont, char*oldname);
240 char* writeEmbeddedFontToFile(XRef*ref, GfxFont*font);
242 int jpeginfo; // did we write "File contains jpegs" yet?
243 int pbminfo; // did we write "File contains jpegs" yet?
244 int linkinfo; // did we write "File contains links" yet?
245 int ttfinfo; // did we write "File contains TrueType Fonts" yet?
246 int gradientinfo; // did we write "File contains Gradients yet?
248 int type3active; // are we between beginType3()/endType3()?
254 char* substitutetarget[256];
255 char* substitutesource[256];
258 int user_movex,user_movey;
259 int user_clipx1,user_clipx2,user_clipy1,user_clipy2;
262 static char*getFontID(GfxFont*font);
264 class InfoOutputDev: public OutputDev
278 virtual ~InfoOutputDev()
281 virtual GBool upsideDown() {return gTrue;}
282 virtual GBool useDrawChar() {return gTrue;}
283 virtual GBool useGradients() {return gTrue;}
284 virtual GBool interpretType3Chars() {return gTrue;}
285 virtual void startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
288 state->transform(crop_x1,crop_y1,&x1,&y1);
289 state->transform(crop_x2,crop_y2,&x2,&y2);
290 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
291 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
297 virtual void drawLink(Link *link, Catalog *catalog)
301 virtual void updateFont(GfxState *state)
303 GfxFont*font = state->getFont();
306 char*id = getFontID(font);
310 virtual void drawImageMask(GfxState *state, Object *ref, Stream *str,
311 int width, int height, GBool invert,
316 virtual void drawImage(GfxState *state, Object *ref, Stream *str,
317 int width, int height, GfxImageColorMap *colorMap,
318 int *maskColors, GBool inlineImg)
324 SWFOutputDev::SWFOutputDev()
342 memset(&output, 0, sizeof(output));
343 // printf("SWFOutputDev::SWFOutputDev() \n");
346 void SWFOutputDev::setMove(int x,int y)
348 this->user_movex = x;
349 this->user_movey = y;
352 void SWFOutputDev::setClip(int x1,int y1,int x2,int y2)
354 if(x2<x1) {int x3=x1;x1=x2;x2=x3;}
355 if(y2<y1) {int y3=y1;y1=y2;y2=y3;}
357 this->user_clipx1 = x1;
358 this->user_clipy1 = y1;
359 this->user_clipx2 = x2;
360 this->user_clipy2 = y2;
362 void SWFOutputDev::getDimensions(int*x1,int*y1,int*x2,int*y2)
364 return swfoutput_getdimensions(&output, x1,y1,x2,y2);
367 static char*getFontID(GfxFont*font)
369 GString*gstr = font->getName();
370 char* fontname = gstr==0?0:gstr->getCString();
374 sprintf(buf, "UFONT%d", r->num);
377 return strdup(fontname);
380 static char*getFontName(GfxFont*font)
382 char*fontid = getFontID(font);
384 char* plus = strchr(fontid, '+');
385 if(plus && plus < &fontid[strlen(fontid)-1]) {
386 fontname = strdup(plus+1);
388 fontname = strdup(fontid);
394 static char mybuf[1024];
395 static char* gfxstate2str(GfxState *state)
399 bufpos+=sprintf(bufpos,"CTM[%.3f/%.3f/%.3f/%.3f/%.3f/%.3f] ",
406 if(state->getX1()!=0.0)
407 bufpos+=sprintf(bufpos,"X1-%.1f ",state->getX1());
408 if(state->getY1()!=0.0)
409 bufpos+=sprintf(bufpos,"Y1-%.1f ",state->getY1());
410 bufpos+=sprintf(bufpos,"X2-%.1f ",state->getX2());
411 bufpos+=sprintf(bufpos,"Y2-%.1f ",state->getY2());
412 bufpos+=sprintf(bufpos,"PW%.1f ",state->getPageWidth());
413 bufpos+=sprintf(bufpos,"PH%.1f ",state->getPageHeight());
414 /*bufpos+=sprintf(bufpos,"FC[%.1f/%.1f] ",
415 state->getFillColor()->c[0], state->getFillColor()->c[1]);
416 bufpos+=sprintf(bufpos,"SC[%.1f/%.1f] ",
417 state->getStrokeColor()->c[0], state->getFillColor()->c[1]);*/
418 /* bufpos+=sprintf(bufpos,"FC[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f]",
419 state->getFillColor()->c[0], state->getFillColor()->c[1],
420 state->getFillColor()->c[2], state->getFillColor()->c[3],
421 state->getFillColor()->c[4], state->getFillColor()->c[5],
422 state->getFillColor()->c[6], state->getFillColor()->c[7]);
423 bufpos+=sprintf(bufpos,"SC[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f]",
424 state->getStrokeColor()->c[0], state->getFillColor()->c[1],
425 state->getStrokeColor()->c[2], state->getFillColor()->c[3],
426 state->getStrokeColor()->c[4], state->getFillColor()->c[5],
427 state->getStrokeColor()->c[6], state->getFillColor()->c[7]);*/
428 state->getFillRGB(&rgb);
429 if(rgb.r || rgb.g || rgb.b)
430 bufpos+=sprintf(bufpos,"FR[%.1f/%.1f/%.1f] ", rgb.r,rgb.g,rgb.b);
431 state->getStrokeRGB(&rgb);
432 if(rgb.r || rgb.g || rgb.b)
433 bufpos+=sprintf(bufpos,"SR[%.1f/%.1f/%.1f] ", rgb.r,rgb.g,rgb.b);
434 if(state->getFillColorSpace()->getNComps()>1)
435 bufpos+=sprintf(bufpos,"CS[[%d]] ",state->getFillColorSpace()->getNComps());
436 if(state->getStrokeColorSpace()->getNComps()>1)
437 bufpos+=sprintf(bufpos,"SS[[%d]] ",state->getStrokeColorSpace()->getNComps());
438 if(state->getFillPattern())
439 bufpos+=sprintf(bufpos,"FP%08x ", state->getFillPattern());
440 if(state->getStrokePattern())
441 bufpos+=sprintf(bufpos,"SP%08x ", state->getStrokePattern());
443 if(state->getFillOpacity()!=1.0)
444 bufpos+=sprintf(bufpos,"FO%.1f ", state->getFillOpacity());
445 if(state->getStrokeOpacity()!=1.0)
446 bufpos+=sprintf(bufpos,"SO%.1f ", state->getStrokeOpacity());
448 bufpos+=sprintf(bufpos,"LW%.1f ", state->getLineWidth());
453 state->getLineDash(&dash, &length, &start);
457 bufpos+=sprintf(bufpos,"DASH%.1f[",start);
458 for(t=0;t<length;t++) {
459 bufpos+=sprintf(bufpos,"D%.1f",dash[t]);
461 bufpos+=sprintf(bufpos,"]");
464 if(state->getFlatness()!=1)
465 bufpos+=sprintf(bufpos,"F%d ", state->getFlatness());
466 if(state->getLineJoin()!=0)
467 bufpos+=sprintf(bufpos,"J%d ", state->getLineJoin());
468 if(state->getLineJoin()!=0)
469 bufpos+=sprintf(bufpos,"C%d ", state->getLineCap());
470 if(state->getLineJoin()!=0)
471 bufpos+=sprintf(bufpos,"ML%d ", state->getMiterLimit());
473 if(state->getFont() && getFontID(state->getFont()))
474 bufpos+=sprintf(bufpos,"F\"%s\" ",getFontID(state->getFont()));
475 bufpos+=sprintf(bufpos,"FS%.1f ", state->getFontSize());
476 bufpos+=sprintf(bufpos,"MAT[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f] ", state->getTextMat()[0],state->getTextMat()[1],state->getTextMat()[2],
477 state->getTextMat()[3],state->getTextMat()[4],state->getTextMat()[5]);
478 if(state->getCharSpace())
479 bufpos+=sprintf(bufpos,"CS%.5f ", state->getCharSpace());
480 if(state->getWordSpace())
481 bufpos+=sprintf(bufpos,"WS%.5f ", state->getWordSpace());
482 if(state->getHorizScaling()!=1.0)
483 bufpos+=sprintf(bufpos,"SC%.1f ", state->getHorizScaling());
484 if(state->getLeading())
485 bufpos+=sprintf(bufpos,"L%.1f ", state->getLeading());
487 bufpos+=sprintf(bufpos,"R%.1f ", state->getRise());
488 if(state->getRender())
489 bufpos+=sprintf(bufpos,"R%d ", state->getRender());
490 bufpos+=sprintf(bufpos,"P%08x ", state->getPath());
491 bufpos+=sprintf(bufpos,"CX%.1f ", state->getCurX());
492 bufpos+=sprintf(bufpos,"CY%.1f ", state->getCurY());
493 if(state->getLineX())
494 bufpos+=sprintf(bufpos,"LX%.1f ", state->getLineX());
495 if(state->getLineY())
496 bufpos+=sprintf(bufpos,"LY%.1f ", state->getLineY());
497 bufpos+=sprintf(bufpos," ");
501 static void dumpFontInfo(char*loglevel, GfxFont*font);
502 static int lastdumps[1024];
503 static int lastdumppos = 0;
508 static void showFontError(GfxFont*font, int nr)
512 for(t=0;t<lastdumppos;t++)
513 if(lastdumps[t] == r->num)
517 if(lastdumppos<sizeof(lastdumps)/sizeof(int))
518 lastdumps[lastdumppos++] = r->num;
520 msg("<warning> The following font caused problems:");
522 msg("<warning> The following font caused problems (substituting):");
524 msg("<warning> The following Type 3 Font will be rendered as bitmap:");
525 dumpFontInfo("<warning>", font);
528 static void dumpFontInfo(char*loglevel, GfxFont*font)
530 char* name = getFontID(font);
531 Ref* r=font->getID();
532 msg("%s=========== %s (ID:%d,%d) ==========\n", loglevel, getFontName(font), r->num,r->gen);
534 GString*gstr = font->getTag();
536 msg("%s| Tag: %s\n", loglevel, name);
538 if(font->isCIDFont()) msg("%s| is CID font\n", loglevel);
540 GfxFontType type=font->getType();
542 case fontUnknownType:
543 msg("%s| Type: unknown\n",loglevel);
546 msg("%s| Type: 1\n",loglevel);
549 msg("%s| Type: 1C\n",loglevel);
552 msg("%s| Type: 3\n",loglevel);
555 msg("%s| Type: TrueType\n",loglevel);
558 msg("%s| Type: CIDType0\n",loglevel);
561 msg("%s| Type: CIDType0C\n",loglevel);
564 msg("%s| Type: CIDType2\n",loglevel);
569 GBool embedded = font->getEmbeddedFontID(&embRef);
570 if(font->getEmbeddedFontName())
571 name = font->getEmbeddedFontName()->getCString();
573 msg("%s| Embedded name: %s id: %d\n",loglevel, FIXNULL(name), embRef.num);
575 gstr = font->getExtFontFile();
577 msg("%s| External Font file: %s\n", loglevel, FIXNULL(gstr->getCString()));
579 // Get font descriptor flags.
580 if(font->isFixedWidth()) msg("%s| is fixed width\n", loglevel);
581 if(font->isSerif()) msg("%s| is serif\n", loglevel);
582 if(font->isSymbolic()) msg("%s| is symbolic\n", loglevel);
583 if(font->isItalic()) msg("%s| is italic\n", loglevel);
584 if(font->isBold()) msg("%s| is bold\n", loglevel);
587 //void SWFOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, GBool invert, GBool inlineImg) {printf("void SWFOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, GBool invert, GBool inlineImg) \n");}
588 //void SWFOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, GBool inlineImg) {printf("void SWFOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, GBool inlineImg) \n");}
591 void dump_outline(gfxline_t*line)
594 if(line->type == gfx_moveTo) {
595 msg("<debug> | moveTo %.2f %.2f", line->x,line->y);
596 } else if(line->type == gfx_lineTo) {
597 msg("<debug> | lineTo %.2f %.2f", line->x,line->y);
598 } else if(line->type == gfx_splineTo) {
599 msg("<debug> | splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y);
605 gfxline_t* gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed)
607 int num = path->getNumSubpaths();
610 double lastx=0,lasty=0,posx=0,posy=0;
613 msg("<warning> empty path");
617 gfxdrawer_target_gfxline(&draw);
619 for(t = 0; t < num; t++) {
620 GfxSubpath *subpath = path->getSubpath(t);
621 int subnum = subpath->getNumPoints();
622 double bx=0,by=0,cx=0,cy=0;
624 for(s=0;s<subnum;s++) {
626 state->transform(subpath->getX(s),subpath->getY(s),&x,&y);
628 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
629 draw.lineTo(&draw, lastx, lasty);
631 draw.moveTo(&draw, x,y);
636 } else if(subpath->getCurve(s) && cpos==0) {
640 } else if(subpath->getCurve(s) && cpos==1) {
648 draw.lineTo(&draw, x,y);
650 gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y);
657 /* fix non-closed lines */
658 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
659 draw.lineTo(&draw, lastx, lasty);
661 gfxline_t*result = (gfxline_t*)draw.result(&draw);
665 /*----------------------------------------------------------------------------
666 * Primitive Graphic routines
667 *----------------------------------------------------------------------------*/
669 void SWFOutputDev::stroke(GfxState *state)
671 GfxPath * path = state->getPath();
672 int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
673 int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
674 double miterLimit = state->getMiterLimit();
675 double width = state->getTransformedLineWidth();
678 double opaq = state->getStrokeOpacity();
680 state->getFillRGB(&rgb);
682 state->getStrokeRGB(&rgb);
684 col.r = (unsigned char)(rgb.r*255);
685 col.g = (unsigned char)(rgb.g*255);
686 col.b = (unsigned char)(rgb.b*255);
687 col.a = (unsigned char)(opaq*255);
689 gfx_capType capType = gfx_capRound;
690 if(lineCap == 0) capType = gfx_capButt;
691 else if(lineCap == 1) capType = gfx_capRound;
692 else if(lineCap == 2) capType = gfx_capSquare;
694 gfx_joinType joinType = gfx_joinRound;
695 if(lineJoin == 0) joinType = gfx_joinMiter;
696 else if(lineJoin == 1) joinType = gfx_joinRound;
697 else if(lineJoin == 2) joinType = gfx_joinBevel;
699 gfxline_t*line= gfxPath_to_gfxline(state, path, 0);
702 double dashphase = 0;
704 state->getLineDash(&ldash, &dashnum, &dashphase);
706 if(dashnum && ldash) {
707 float * dash = (float*)malloc(sizeof(float)*(dashnum+1));
711 msg("<trace> %d dashes", dashnum);
712 msg("<trace> | phase: %f", dashphase);
713 for(t=0;t<dashnum;t++) {
715 msg("<trace> | d%-3d: %f", t, ldash[t]);
718 if(getLogLevel() >= LOGLEVEL_TRACE) {
722 gfxline_t*line2 = gfxtool_dash_line(line, dash, dashphase);
725 msg("<trace> After dashing:");
728 if(getLogLevel() >= LOGLEVEL_TRACE) {
730 state->getStrokeGray(&gray);
731 msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x gray=%f\n",
733 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
734 lineCap==0?"butt": (lineJoin==1?"round":"square"),
736 col.r,col.g,col.b,col.a,
742 swfoutput_drawgfxline(&output, line, width, &col, capType, joinType, miterLimit);
745 void SWFOutputDev::fill(GfxState *state)
747 GfxPath * path = state->getPath();
748 double opaq = state->getFillOpacity();
750 state->getFillRGB(&rgb);
752 col.r = (unsigned char)(rgb.r*255);
753 col.g = (unsigned char)(rgb.g*255);
754 col.b = (unsigned char)(rgb.b*255);
755 col.a = (unsigned char)(opaq*255);
757 gfxline_t*line= gfxPath_to_gfxline(state, path, 1);
759 if(getLogLevel() >= LOGLEVEL_TRACE) {
760 msg("<trace> fill %02x%02x%02x%02x\n", col.r, col.g, col.b, col.a);
764 swfoutput_fillgfxline(&output, line, &col);
767 void SWFOutputDev::eoFill(GfxState *state)
769 GfxPath * path = state->getPath();
770 double opaq = state->getFillOpacity();
772 state->getFillRGB(&rgb);
774 col.r = (unsigned char)(rgb.r*255);
775 col.g = (unsigned char)(rgb.g*255);
776 col.b = (unsigned char)(rgb.b*255);
777 col.a = (unsigned char)(opaq*255);
779 gfxline_t*line= gfxPath_to_gfxline(state, path, 1);
781 if(getLogLevel() >= LOGLEVEL_TRACE) {
782 msg("<trace> eofill\n");
786 swfoutput_fillgfxline(&output, line, &col);
789 void SWFOutputDev::clip(GfxState *state)
791 GfxPath * path = state->getPath();
792 gfxline_t*line = gfxPath_to_gfxline(state, path, 1);
794 if(getLogLevel() >= LOGLEVEL_TRACE) {
795 msg("<trace> clip\n");
799 swfoutput_startclip(&output, line);
800 states[statepos].clipping++;
803 void SWFOutputDev::eoClip(GfxState *state)
805 GfxPath * path = state->getPath();
806 gfxline_t*line = gfxPath_to_gfxline(state, path, 1);
808 if(getLogLevel() >= LOGLEVEL_TRACE) {
809 msg("<trace> eoclip\n");
813 swfoutput_startclip(&output, line);
814 states[statepos].clipping++;
818 /* pass through functions for swf_output */
819 int SWFOutputDev::save(char*filename)
821 return swfoutput_save(&output, filename);
823 void SWFOutputDev::pagefeed()
825 swfoutput_pagefeed(&output);
827 void* SWFOutputDev::getSWF()
829 return (void*)swfoutput_get(&output);
832 SWFOutputDev::~SWFOutputDev()
834 swfoutput_destroy(&output);
837 GBool SWFOutputDev::upsideDown()
839 msg("<debug> upsidedown? yes");
842 GBool SWFOutputDev::useDrawChar()
846 GBool SWFOutputDev::useGradients()
850 msg("<notice> File contains gradients");
856 char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
857 "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
859 void SWFOutputDev::beginString(GfxState *state, GString *s)
861 int render = state->getRender();
862 msg("<trace> beginString(%s) render=%d", s->getCString(), render);
863 double m11,m21,m12,m22;
864 // msg("<debug> %s beginstring \"%s\"\n", gfxstate2str(state), s->getCString());
865 state->getFontTransMat(&m11, &m12, &m21, &m22);
866 m11 *= state->getHorizScaling();
867 m21 *= state->getHorizScaling();
868 swfoutput_setfontmatrix(&output, m11, -m21, m12, -m22);
869 if(render != 3 && render != 0)
870 msg("<warning> Text rendering mode %d (%s) not fully supported yet (for text \"%s\")", render, renderModeDesc[render&7], s->getCString());
871 states[statepos].textRender = render;
874 static int textCount = 0;
876 void SWFOutputDev::drawChar(GfxState *state, double x, double y,
877 double dx, double dy,
878 double originX, double originY,
879 CharCode c, Unicode *_u, int uLen)
883 int render = state->getRender();
884 // check for invisible text -- this is used by Acrobat Capture
888 if(states[statepos].textRender != render)
889 msg("<error> Internal error: drawChar.render!=beginString.render");
892 double opaq = state->getFillOpacity();
893 state->getFillRGB(&rgb);
895 col.r = (unsigned char)(rgb.r*255);
896 col.g = (unsigned char)(rgb.g*255);
897 col.b = (unsigned char)(rgb.b*255);
898 col.a = (unsigned char)(opaq*255);
900 Gushort *CIDToGIDMap = 0;
901 GfxFont*font = state->getFont();
903 if(font->getType() == fontType3) {
904 /* type 3 chars are passed as graphics */
905 msg("<debug> type3 char at %f/%f", x, y);
911 state->transform(x, y, &x1, &y1);
920 /* find out char name from unicode index
921 TODO: should be precomputed
923 for(t=0;t<sizeof(nameToUnicodeTab)/sizeof(nameToUnicodeTab[0]);t++) {
924 if(nameToUnicodeTab[t].u == u) {
925 name = nameToUnicodeTab[t].name;
932 if(font->isCIDFont()) {
933 GfxCIDFont*cfont = (GfxCIDFont*)font;
935 if(font->getType() == fontCIDType2) {
936 CIDToGIDMap = cfont->getCIDToGID();
940 font8 = (Gfx8BitFont*)font;
941 char**enc=font8->getEncoding();
947 msg("<debug> drawChar(%f, %f, c='%c' (%d), GID=%d, u=%d <%d>) CID=%d name=\"%s\" render=%d\n", x, y, (c&127)>=32?c:'?', c, CIDToGIDMap[c], u, uLen, font->isCIDFont(), FIXNULL(name), render);
948 swfoutput_drawchar(&output, x1, y1, name, CIDToGIDMap[c], u, &col);
950 msg("<debug> drawChar(%f,%f,c='%c' (%d), u=%d <%d>) CID=%d name=\"%s\" render=%d\n",x,y,(c&127)>=32?c:'?',c,u, uLen, font->isCIDFont(), FIXNULL(name), render);
951 swfoutput_drawchar(&output, x1, y1, name, c, u, &col);
955 void SWFOutputDev::endString(GfxState *state)
957 msg("<trace> endString()");
960 void SWFOutputDev::endTextObject(GfxState *state)
962 msg("<trace> endTextObject()");
965 /* the logic seems to be as following:
966 first, beginType3Char is called, with the charcode and the coordinates.
967 if this function returns true, it already knew about the char and has now drawn it.
968 if the function returns false, it's a new char, and type3D1 is called with some parameters-
969 the all draw operations until endType3Char are part of the char (which in this moment is
970 at the position first passed to beginType3Char). the char ends with endType3Char.
972 The drawing operations between beginType3Char and endType3Char are somewhat different to
973 the normal ones. For example, the fillcolor equals the stroke color.
976 GBool SWFOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode code, Unicode *u, int uLen)
978 msg("<debug> beginType3Char %d, %08x, %d", code, *u, uLen);
980 /* the character itself is going to be passed using the draw functions */
981 return gFalse; /* gTrue= is_in_cache? */
984 void SWFOutputDev::type3D0(GfxState *state, double wx, double wy) {
985 msg("<debug> type3D0 width=%f height=%f", wx, wy);
987 void SWFOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
988 msg("<debug> type3D1 width=%f height=%f bbox=(%f,%f,%f,%f)", wx, wy,
992 void SWFOutputDev::endType3Char(GfxState *state)
995 msg("<debug> endType3Char");
998 void SWFOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
1000 this->currentpage = pageNum;
1002 int rot = doc->getPageRotate(1);
1004 msg("<verbose> startPage %d (%f,%f,%f,%f)\n", pageNum, crop_x1, crop_y1, crop_x2, crop_y2);
1006 msg("<verbose> page is rotated %d degrees\n", rot);
1008 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1009 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1010 Use CropBox, not MediaBox, as page size
1017 state->transform(crop_x1,crop_y1,&x1,&y1);
1018 state->transform(crop_x2,crop_y2,&x2,&y2);
1020 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1021 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1023 /* apply user clip box */
1024 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1025 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1026 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1027 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1028 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1031 if(!outputstarted) {
1032 msg("<verbose> Bounding box is (%f,%f)-(%f,%f)", x1,y1,x2,y2);
1033 swfoutput_init(&output);
1037 swfoutput_newpage(&output, pageNum, user_movex, user_movey, (int)x1, (int)y1, (int)x2, (int)y2);
1040 void SWFOutputDev::drawLink(Link *link, Catalog *catalog)
1042 msg("<debug> drawlink\n");
1043 double x1, y1, x2, y2, w;
1049 link->getBorder(&x1, &y1, &x2, &y2, &w);
1051 link->getRect(&x1, &y1, &x2, &y2);
1056 cvtUserToDev(x1, y1, &x, &y);
1057 points[0].x = points[4].x = (int)x;
1058 points[0].y = points[4].y = (int)y;
1059 cvtUserToDev(x2, y1, &x, &y);
1060 points[1].x = (int)x;
1061 points[1].y = (int)y;
1062 cvtUserToDev(x2, y2, &x, &y);
1063 points[2].x = (int)x;
1064 points[2].y = (int)y;
1065 cvtUserToDev(x1, y2, &x, &y);
1066 points[3].x = (int)x;
1067 points[3].y = (int)y;
1069 LinkAction*action=link->getAction();
1076 switch(action->getKind())
1080 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1081 LinkDest *dest=NULL;
1082 if (ha->getDest()==NULL)
1083 dest=catalog->findDest(ha->getNamedDest());
1084 else dest=ha->getDest();
1086 if (dest->isPageRef()){
1087 Ref pageref=dest->getPageRef();
1088 page=catalog->findPage(pageref.num,pageref.gen);
1090 else page=dest->getPageNum();
1091 sprintf(buf, "%d", page);
1098 LinkGoToR*l = (LinkGoToR*)action;
1099 GString*g = l->getNamedDest();
1101 s = strdup(g->getCString());
1106 LinkNamed*l = (LinkNamed*)action;
1107 GString*name = l->getName();
1109 s = strdup(name->lowerCase()->getCString());
1110 named = name->getCString();
1113 if(strstr(s, "next") || strstr(s, "forward"))
1115 page = currentpage + 1;
1117 else if(strstr(s, "prev") || strstr(s, "back"))
1119 page = currentpage - 1;
1121 else if(strstr(s, "last") || strstr(s, "end"))
1123 page = pagepos>0?pages[pagepos-1]:0;
1125 else if(strstr(s, "first") || strstr(s, "top"))
1133 case actionLaunch: {
1135 LinkLaunch*l = (LinkLaunch*)action;
1136 GString * str = new GString(l->getFileName());
1137 str->append(l->getParams());
1138 s = strdup(str->getCString());
1144 LinkURI*l = (LinkURI*)action;
1145 GString*g = l->getURI();
1147 url = g->getCString();
1152 case actionUnknown: {
1154 LinkUnknown*l = (LinkUnknown*)action;
1159 msg("<error> Unknown link type!\n");
1163 if(!s) s = strdup("-?-");
1165 if(!linkinfo && (page || url))
1167 msg("<notice> File contains links");
1173 for(t=0;t<pagepos;t++)
1177 swfoutput_linktopage(&output, t, points);
1181 swfoutput_linktourl(&output, url, points);
1185 swfoutput_namedlink(&output, named, points);
1187 msg("<verbose> \"%s\" link to \"%s\" (%d)\n", type, FIXNULL(s), page);
1191 void SWFOutputDev::saveState(GfxState *state) {
1192 msg("<trace> saveState\n");
1195 msg("<error> Too many nested states in pdf.");
1199 states[statepos].clipping = 0; //? shouldn't this be the current value?
1200 states[statepos].textRender = states[statepos-1].textRender;
1203 void SWFOutputDev::restoreState(GfxState *state) {
1204 msg("<trace> restoreState\n");
1206 while(states[statepos].clipping) {
1207 swfoutput_endclip(&output);
1208 states[statepos].clipping--;
1213 char* SWFOutputDev::searchFont(char*name)
1217 int is_standard_font = 0;
1219 msg("<verbose> SearchFont(%s)", name);
1221 /* see if it is a pdf standard font */
1222 for(i=0;i<sizeof(pdf2t1map)/sizeof(mapping);i++)
1224 if(!strcmp(name, pdf2t1map[i].pdffont))
1226 name = pdf2t1map[i].filename;
1227 is_standard_font = 1;
1231 /* look in all font files */
1232 for(i=0;i<fontnum;i++)
1234 if(strstr(fonts[i].filename, name))
1236 if(!fonts[i].used) {
1239 if(!is_standard_font)
1240 msg("<notice> Using %s for %s", fonts[i].filename, name);
1242 return strdup(fonts[i].filename);
1248 void SWFOutputDev::updateLineWidth(GfxState *state)
1250 double width = state->getTransformedLineWidth();
1251 //swfoutput_setlinewidth(&output, width);
1254 void SWFOutputDev::updateLineCap(GfxState *state)
1256 int c = state->getLineCap();
1259 void SWFOutputDev::updateLineJoin(GfxState *state)
1261 int j = state->getLineJoin();
1264 void SWFOutputDev::updateFillColor(GfxState *state)
1267 double opaq = state->getFillOpacity();
1268 state->getFillRGB(&rgb);
1270 //swfoutput_setfillcolor(&output, (char)(rgb.r*255), (char)(rgb.g*255), (char)(rgb.b*255), (char)(opaq*255));
1273 void SWFOutputDev::updateStrokeColor(GfxState *state)
1276 double opaq = state->getStrokeOpacity();
1277 state->getStrokeRGB(&rgb);
1278 //swfoutput_setstrokecolor(&output, (char)(rgb.r*255), (char)(rgb.g*255), (char)(rgb.b*255), (char)(opaq*255));
1281 void FoFiWrite(void *stream, char *data, int len)
1283 fwrite(data, len, 1, (FILE*)stream);
1286 char*SWFOutputDev::writeEmbeddedFontToFile(XRef*ref, GfxFont*font)
1288 char*tmpFileName = NULL;
1294 Object refObj, strObj;
1296 tmpFileName = mktmpname(namebuf);
1299 ret = font->getEmbeddedFontID(&embRef);
1301 msg("<verbose> Didn't get embedded font id");
1302 /* not embedded- the caller should now search the font
1303 directories for this font */
1307 f = fopen(tmpFileName, "wb");
1309 msg("<error> Couldn't create temporary Type 1 font file");
1313 /*if(font->isCIDFont()) {
1314 GfxCIDFont* cidFont = (GfxCIDFont *)font;
1315 GString c = cidFont->getCollection();
1316 msg("<notice> Collection: %s", c.getCString());
1319 if (font->getType() == fontType1C) {
1320 if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
1322 msg("<error> Couldn't read embedded font file");
1326 Type1CFontFile *cvt = new Type1CFontFile(fontBuf, fontLen);
1328 cvt->convertToType1(f);
1330 FoFiType1C *cvt = FoFiType1C::make(fontBuf, fontLen);
1332 cvt->convertToType1(NULL, gTrue, FoFiWrite, f);
1334 //cvt->convertToCIDType0("test", f);
1335 //cvt->convertToType0("test", f);
1338 } else if(font->getType() == fontTrueType) {
1339 msg("<verbose> writing font using TrueTypeFontFile::writeTTF");
1340 if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
1342 msg("<error> Couldn't read embedded font file");
1346 TrueTypeFontFile *cvt = new TrueTypeFontFile(fontBuf, fontLen);
1349 FoFiTrueType *cvt = FoFiTrueType::make(fontBuf, fontLen);
1350 cvt->writeTTF(FoFiWrite, f);
1355 font->getEmbeddedFontID(&embRef);
1356 refObj.initRef(embRef.num, embRef.gen);
1357 refObj.fetch(ref, &strObj);
1359 strObj.streamReset();
1364 f4[t] = strObj.streamGetChar();
1365 f4c[t] = (char)f4[t];
1370 if(!strncmp(f4c, "true", 4)) {
1371 /* some weird TTF fonts don't start with 0,1,0,0 but with "true".
1372 Change this on the fly */
1373 f4[0] = f4[2] = f4[3] = 0;
1381 while ((c = strObj.streamGetChar()) != EOF) {
1385 strObj.streamClose();
1390 return strdup(tmpFileName);
1393 char* searchForSuitableFont(GfxFont*gfxFont)
1395 char*name = getFontName(gfxFont);
1399 if(!config_use_fontconfig)
1402 #ifdef HAVE_FONTCONFIG
1403 FcPattern *pattern, *match;
1407 static int fcinitcalled = false;
1409 msg("<debug> searchForSuitableFont(%s)", name);
1411 // call init ony once
1412 if (!fcinitcalled) {
1413 msg("<debug> Initializing FontConfig...");
1414 fcinitcalled = true;
1416 msg("<debug> FontConfig Initialization failed. Disabling.");
1417 config_use_fontconfig = 0;
1420 msg("<debug> ...initialized FontConfig");
1423 msg("<debug> FontConfig: Create \"%s\" Family Pattern", name);
1424 pattern = FcPatternBuild(NULL, FC_FAMILY, FcTypeString, name, NULL);
1425 if (gfxFont->isItalic()) // check for italic
1426 msg("<debug> FontConfig: Adding Italic Slant");
1427 FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
1428 if (gfxFont->isBold()) // check for bold
1429 msg("<debug> FontConfig: Adding Bold Weight");
1430 FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
1432 msg("<debug> FontConfig: Try to match...");
1433 // configure and match using the original font name
1434 FcConfigSubstitute(0, pattern, FcMatchPattern);
1435 FcDefaultSubstitute(pattern);
1436 match = FcFontMatch(0, pattern, &result);
1438 if (FcPatternGetString(match, "family", 0, &v) == FcResultMatch) {
1439 msg("<debug> FontConfig: family=%s", (char*)v);
1440 // if we get an exact match
1441 if (strcmp((char *)v, name) == 0) {
1442 if (FcPatternGetString(match, "file", 0, &v) == FcResultMatch) {
1443 filename = strdup((char*)v);
1444 char *nfn = strrchr(filename, '/');
1445 if(nfn) fontname = strdup(nfn+1);
1446 else fontname = filename;
1448 msg("<debug> FontConfig: Returning \"%s\"", fontname);
1450 // initialize patterns
1451 FcPatternDestroy(pattern);
1452 FcPatternDestroy(match);
1454 // now match against serif etc.
1455 if (gfxFont->isSerif()) {
1456 msg("<debug> FontConfig: Create Serif Family Pattern");
1457 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "serif", NULL);
1458 } else if (gfxFont->isFixedWidth()) {
1459 msg("<debug> FontConfig: Create Monospace Family Pattern");
1460 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "monospace", NULL);
1462 msg("<debug> FontConfig: Create Sans Family Pattern");
1463 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "sans", NULL);
1467 if (gfxFont->isItalic()) {
1468 msg("<debug> FontConfig: Adding Italic Slant");
1469 int bb = FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
1472 if (gfxFont->isBold()) {
1473 msg("<debug> FontConfig: Adding Bold Weight");
1474 int bb = FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
1477 msg("<debug> FontConfig: Try to match... (2)");
1478 // configure and match using serif etc
1479 FcConfigSubstitute (0, pattern, FcMatchPattern);
1480 FcDefaultSubstitute (pattern);
1481 match = FcFontMatch (0, pattern, &result);
1483 if (FcPatternGetString(match, "file", 0, &v) == FcResultMatch) {
1484 filename = strdup((char*)v);
1485 char *nfn = strrchr(filename, '/');
1486 if(nfn) fontname = strdup(nfn+1);
1487 else fontname = filename;
1489 msg("<debug> FontConfig: Returning \"%s\"", fontname);
1493 //printf("FONTCONFIG: pattern");
1494 //FcPatternPrint(pattern);
1495 //printf("FONTCONFIG: match");
1496 //FcPatternPrint(match);
1498 FcPatternDestroy(pattern);
1499 FcPatternDestroy(match);
1501 pdfswf_addfont(filename);
1508 char* SWFOutputDev::substituteFont(GfxFont*gfxFont, char* oldname)
1510 char*fontname = 0, *filename = 0;
1511 msg("<notice> subsituteFont(%s)", oldname);
1513 if(!(fontname = searchForSuitableFont(gfxFont))) {
1514 fontname = "Times-Roman";
1516 filename = searchFont(fontname);
1518 msg("<error> Couldn't find font %s- did you install the default fonts?");
1522 if(substitutepos>=sizeof(substitutesource)/sizeof(char*)) {
1523 msg("<fatal> Too many fonts in file.");
1527 substitutesource[substitutepos] = oldname;
1528 substitutetarget[substitutepos] = fontname;
1529 msg("<notice> substituting %s -> %s", FIXNULL(oldname), FIXNULL(fontname));
1532 return strdup(filename);
1535 void unlinkfont(char* filename)
1542 if(!strncmp(&filename[l-4],".afm",4)) {
1543 memcpy(&filename[l-4],".pfb",4);
1545 memcpy(&filename[l-4],".pfa",4);
1547 memcpy(&filename[l-4],".afm",4);
1550 if(!strncmp(&filename[l-4],".pfa",4)) {
1551 memcpy(&filename[l-4],".afm",4);
1553 memcpy(&filename[l-4],".pfa",4);
1556 if(!strncmp(&filename[l-4],".pfb",4)) {
1557 memcpy(&filename[l-4],".afm",4);
1559 memcpy(&filename[l-4],".pfb",4);
1564 void SWFOutputDev::setXRef(PDFDoc*doc, XRef *xref)
1571 void SWFOutputDev::updateFont(GfxState *state)
1573 GfxFont*gfxFont = state->getFont();
1578 char * fontid = getFontID(gfxFont);
1581 /* first, look if we substituted this font before-
1582 this way, we don't initialize the T1 Fonts
1584 for(t=0;t<substitutepos;t++) {
1585 if(!strcmp(fontid, substitutesource[t])) {
1586 fontid = substitutetarget[t];
1591 /* second, see if swfoutput already has this font
1592 cached- if so, we are done */
1593 if(swfoutput_queryfont(&output, fontid))
1595 swfoutput_setfont(&output, fontid, 0);
1597 msg("<debug> updateFont(%s) [cached]", fontid);
1601 // look for Type 3 font
1602 if (gfxFont->getType() == fontType3) {
1604 type3Warning = gTrue;
1605 showFontError(gfxFont, 2);
1610 /* now either load the font, or find a substitution */
1613 GBool embedded = gfxFont->getEmbeddedFontID(&embRef);
1618 (gfxFont->getType() == fontType1 ||
1619 gfxFont->getType() == fontType1C ||
1620 (gfxFont->getType() == fontCIDType0C && forceType0Fonts) ||
1621 gfxFont->getType() == fontTrueType ||
1622 gfxFont->getType() == fontCIDType2
1625 fileName = writeEmbeddedFontToFile(xref, gfxFont);
1626 if(!fileName) showFontError(gfxFont,0);
1629 char * fontname = getFontName(gfxFont);
1630 fileName = searchFont(fontname);
1631 if(!fileName) showFontError(gfxFont,0);
1634 char * fontname = getFontName(gfxFont);
1635 msg("<warning> Font %s %scould not be loaded.", fontname, embedded?"":"(not embedded) ");
1636 msg("<warning> Try putting a TTF version of that font (named \"%s.ttf\") into /swftools/fonts", fontname);
1637 fileName = substituteFont(gfxFont, fontid);
1638 if(fontid) { fontid = substitutetarget[substitutepos-1]; /*ugly hack*/};
1639 msg("<notice> Font is now %s (%s)", fontid, fileName);
1643 msg("<error> Couldn't set font %s\n", fontid);
1647 msg("<verbose> updateFont(%s) -> %s", fontid, fileName);
1648 dumpFontInfo("<verbose>", gfxFont);
1650 swfoutput_setfont(&output, fontid, fileName);
1653 unlinkfont(fileName);
1658 #define SQR(x) ((x)*(x))
1660 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
1662 if((newwidth<2 || newheight<2) ||
1663 (width<=newwidth || height<=newheight))
1665 unsigned char*newdata;
1667 newdata= (unsigned char*)malloc(newwidth*newheight);
1669 double fx = (double)(width)/newwidth;
1670 double fy = (double)(height)/newheight;
1672 int blocksize = (int)(8192/(fx*fy));
1673 int r = 8192*256/palettesize;
1674 for(x=0;x<newwidth;x++) {
1675 double ex = px + fx;
1676 int fromx = (int)px;
1678 int xweight1 = (int)(((fromx+1)-px)*256);
1679 int xweight2 = (int)((ex-tox)*256);
1681 for(y=0;y<newheight;y++) {
1682 double ey = py + fy;
1683 int fromy = (int)py;
1685 int yweight1 = (int)(((fromy+1)-py)*256);
1686 int yweight2 = (int)((ey-toy)*256);
1689 for(xx=fromx;xx<=tox;xx++)
1690 for(yy=fromy;yy<=toy;yy++) {
1691 int b = 1-data[width*yy+xx];
1693 if(xx==fromx) weight = (weight*xweight1)/256;
1694 if(xx==tox) weight = (weight*xweight2)/256;
1695 if(yy==fromy) weight = (weight*yweight1)/256;
1696 if(yy==toy) weight = (weight*yweight2)/256;
1699 //if(a) a=(palettesize-1)*r/blocksize;
1700 newdata[y*newwidth+x] = (a*blocksize)/r;
1708 void SWFOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
1709 int width, int height, GfxImageColorMap*colorMap, GBool invert,
1710 GBool inlineImg, int mask, int*maskColors)
1715 double x1,y1,x2,y2,x3,y3,x4,y4;
1716 ImageStream *imgStr;
1723 ncomps = colorMap->getNumPixelComps();
1724 bits = colorMap->getBits();
1726 imgStr = new ImageStream(str, width, ncomps,bits);
1729 if(!width || !height || (height<=1 && width<=1))
1731 msg("<verbose> Ignoring %d by %d image", width, height);
1732 unsigned char buf[8];
1734 for (y = 0; y < height; ++y)
1735 for (x = 0; x < width; ++x) {
1736 imgStr->getPixel(buf);
1742 state->transform(0, 1, &x1, &y1);
1743 state->transform(0, 0, &x2, &y2);
1744 state->transform(1, 0, &x3, &y3);
1745 state->transform(1, 1, &x4, &y4);
1747 if(!pbminfo && !(str->getKind()==strDCT)) {
1749 msg("<notice> file contains pbm pictures %s",mask?"(masked)":"");
1753 msg("<verbose> drawing %d by %d masked picture\n", width, height);
1755 if(!jpeginfo && (str->getKind()==strDCT)) {
1756 msg("<notice> file contains jpeg pictures");
1762 unsigned char buf[8];
1764 unsigned char*pic = new unsigned char[width*height];
1767 state->getFillRGB(&rgb);
1769 memset(pal,255,sizeof(pal));
1770 pal[0].r = (int)(rgb.r*255); pal[1].r = 0;
1771 pal[0].g = (int)(rgb.g*255); pal[1].g = 0;
1772 pal[0].b = (int)(rgb.b*255); pal[1].b = 0;
1773 pal[0].a = 255; pal[1].a = 0;
1776 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
1777 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
1778 for (y = 0; y < height; ++y)
1779 for (x = 0; x < width; ++x)
1781 imgStr->getPixel(buf);
1784 pic[width*y+x] = buf[0];
1787 /* the size of the drawn image is added to the identifier
1788 as the same image may require different bitmaps if displayed
1789 at different sizes (due to antialiasing): */
1792 unsigned char*pic2 = 0;
1795 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
1804 height = realheight;
1808 /* make a black/white palette */
1813 float r = 255/(numpalette-1);
1815 for(t=0;t<numpalette;t++) {
1816 pal[t].r = (U8)(255*rgb.r);
1817 pal[t].g = (U8)(255*rgb.g);
1818 pal[t].b = (U8)(255*rgb.b);
1819 pal[t].a = (U8)(t*r);
1823 RGBA*pic2 = new RGBA[width*height];
1824 for (y = 0; y < height; ++y) {
1825 for (x = 0; x < width; ++x) {
1826 pic2[width*y+x] = pal[pic[y*width+x]];
1829 swfoutput_drawimagelossless(&output, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1838 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
1839 RGBA*pic=new RGBA[width*height];
1840 for (y = 0; y < height; ++y) {
1841 for (x = 0; x < width; ++x) {
1842 imgStr->getPixel(pixBuf);
1843 colorMap->getRGB(pixBuf, &rgb);
1844 pic[width*y+x].r = (U8)(rgb.r * 255 + 0.5);
1845 pic[width*y+x].g = (U8)(rgb.g * 255 + 0.5);
1846 pic[width*y+x].b = (U8)(rgb.b * 255 + 0.5);
1847 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
1850 if(str->getKind()==strDCT)
1851 swfoutput_drawimagejpeg(&output, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1853 swfoutput_drawimagelossless(&output, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1858 RGBA*pic=new RGBA[width*height];
1861 for(t=0;t<256;t++) {
1863 colorMap->getRGB(pixBuf, &rgb);
1864 /*if(maskColors && *maskColors==t) {
1865 msg("<notice> Color %d is transparent", t);
1866 if (imgData->maskColors) {
1868 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
1869 if (pix[i] < imgData->maskColors[2*i] ||
1870 pix[i] > imgData->maskColors[2*i+1]) {
1885 pal[t].r = (U8)(rgb.r * 255 + 0.5);
1886 pal[t].g = (U8)(rgb.g * 255 + 0.5);
1887 pal[t].b = (U8)(rgb.b * 255 + 0.5);
1888 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
1891 for (y = 0; y < height; ++y) {
1892 for (x = 0; x < width; ++x) {
1893 imgStr->getPixel(pixBuf);
1894 pic[width*y+x] = pal[pixBuf[0]];
1897 swfoutput_drawimagelossless(&output, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1905 void SWFOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
1906 int width, int height, GBool invert,
1909 if(states[statepos].textRender & 4) //clipped
1911 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
1912 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0);
1915 void SWFOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
1916 int width, int height, GfxImageColorMap *colorMap,
1917 int *maskColors, GBool inlineImg)
1919 if(states[statepos].textRender & 4) //clipped
1922 msg("<verbose> drawImage %dx%d, %s %s, inline=%d", width, height,
1923 colorMap?"colorMap":"no colorMap",
1924 maskColors?"maskColors":"no maskColors",
1927 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
1928 colorMap->getBits(),colorMap->getColorSpace()->getMode());
1929 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors);
1932 SWFOutputDev*output = 0;
1934 static void printInfoString(Dict *infoDict, char *key, char *fmt) {
1939 if (infoDict->lookup(key, &obj)->isString()) {
1940 s1 = obj.getString();
1941 if ((s1->getChar(0) & 0xff) == 0xfe &&
1942 (s1->getChar(1) & 0xff) == 0xff) {
1944 for (i = 2; i < obj.getString()->getLength(); i += 2) {
1945 if (s1->getChar(i) == '\0') {
1946 s2->append(s1->getChar(i+1));
1949 s2 = new GString("<unicode>");
1953 printf(fmt, s2->getCString());
1956 printf(fmt, s1->getCString());
1962 static void printInfoDate(Dict *infoDict, char *key, char *fmt) {
1966 if (infoDict->lookup(key, &obj)->isString()) {
1967 s = obj.getString()->getCString();
1968 if (s[0] == 'D' && s[1] == ':') {
1979 void pdfswf_setparameter(char*name, char*value)
1981 msg("<verbose> setting parameter %s to \"%s\"", name, value);
1982 if(!strcmp(name, "caplinewidth")) {
1983 caplinewidth = atof(value);
1984 } else if(!strcmp(name, "zoom")) {
1987 sprintf(buf, "%f", (double)jpeg_dpi/(double)zoom);
1988 swfoutput_setparameter("jpegsubpixels", buf);
1989 sprintf(buf, "%f", (double)ppm_dpi/(double)zoom);
1990 swfoutput_setparameter("ppmsubpixels", buf);
1991 } else if(!strcmp(name, "jpegdpi")) {
1993 jpeg_dpi = atoi(value);
1994 sprintf(buf, "%f", (double)jpeg_dpi/(double)zoom);
1995 swfoutput_setparameter("jpegsubpixels", buf);
1996 } else if(!strcmp(name, "ppmdpi")) {
1998 ppm_dpi = atoi(value);
1999 sprintf(buf, "%f", (double)ppm_dpi/(double)zoom);
2000 swfoutput_setparameter("ppmsubpixels", buf);
2001 } else if(!strcmp(name, "forceType0Fonts")) {
2002 forceType0Fonts = atoi(value);
2003 } else if(!strcmp(name, "fontdir")) {
2004 pdfswf_addfontdir(value);
2005 } else if(!strcmp(name, "languagedir")) {
2006 pdfswf_addlanguagedir(value);
2007 } else if(!strcmp(name, "fontconfig")) {
2008 config_use_fontconfig = atoi(value);
2010 swfoutput_setparameter(name, value);
2013 void pdfswf_addfont(char*filename)
2016 memset(&f, 0, sizeof(fontfile_t));
2017 f.filename = filename;
2018 if(fontnum < sizeof(fonts)/sizeof(fonts[0])) {
2019 fonts[fontnum++] = f;
2021 msg("<error> Too many external fonts. Not adding font file \"%s\".", filename);
2025 static char* dirseparator()
2034 void pdfswf_addlanguagedir(char*dir)
2037 globalParams = new GlobalParams("");
2039 msg("<notice> Adding %s to language pack directories", dir);
2043 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc"));
2044 strcpy(config_file, dir);
2045 strcat(config_file, dirseparator());
2046 strcat(config_file, "add-to-xpdfrc");
2048 fi = fopen(config_file, "rb");
2050 msg("<error> Could not open %s", config_file);
2053 globalParams->parseFile(new GString(config_file), fi);
2057 void pdfswf_addfontdir(char*dirname)
2059 #ifdef HAVE_DIRENT_H
2060 msg("<notice> Adding %s to font directories", dirname);
2061 DIR*dir = opendir(dirname);
2063 msg("<warning> Couldn't open directory %s\n", dirname);
2068 ent = readdir (dir);
2072 char*name = ent->d_name;
2078 if(!strncasecmp(&name[l-4], ".pfa", 4))
2080 if(!strncasecmp(&name[l-4], ".pfb", 4))
2082 if(!strncasecmp(&name[l-4], ".ttf", 4))
2086 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2087 strcpy(fontname, dirname);
2088 strcat(fontname, dirseparator());
2089 strcat(fontname, name);
2090 msg("<verbose> Adding %s to fonts", fontname);
2091 pdfswf_addfont(fontname);
2096 msg("<warning> No dirent.h- unable to add font dir %s", dir);
2101 typedef struct _pdf_doc_internal
2105 } pdf_doc_internal_t;
2106 typedef struct _pdf_page_internal
2108 } pdf_page_internal_t;
2109 typedef struct _swf_output_internal
2111 SWFOutputDev*outputDev;
2112 } swf_output_internal_t;
2114 pdf_doc_t* pdf_init(char*filename, char*userPassword)
2116 pdf_doc_t*pdf_doc = (pdf_doc_t*)malloc(sizeof(pdf_doc_t));
2117 memset(pdf_doc, 0, sizeof(pdf_doc_t));
2118 pdf_doc_internal_t*i= (pdf_doc_internal_t*)malloc(sizeof(pdf_doc_internal_t));
2119 memset(i, 0, sizeof(pdf_doc_internal_t));
2120 pdf_doc->internal = i;
2122 GString *fileName = new GString(filename);
2128 globalParams = new GlobalParams("");
2131 if (userPassword && userPassword[0]) {
2132 userPW = new GString(userPassword);
2136 i->doc = new PDFDoc(fileName, userPW);
2140 if (!i->doc->isOk()) {
2145 i->doc->getDocInfo(&info);
2146 if (info.isDict() &&
2147 (getScreenLogLevel()>=LOGLEVEL_NOTICE)) {
2148 printInfoString(info.getDict(), "Title", "Title: %s\n");
2149 printInfoString(info.getDict(), "Subject", "Subject: %s\n");
2150 printInfoString(info.getDict(), "Keywords", "Keywords: %s\n");
2151 printInfoString(info.getDict(), "Author", "Author: %s\n");
2152 printInfoString(info.getDict(), "Creator", "Creator: %s\n");
2153 printInfoString(info.getDict(), "Producer", "Producer: %s\n");
2154 printInfoDate(info.getDict(), "CreationDate", "CreationDate: %s\n");
2155 printInfoDate(info.getDict(), "ModDate", "ModDate: %s\n");
2156 printf("Pages: %d\n", i->doc->getNumPages());
2157 printf("Linearized: %s\n", i->doc->isLinearized() ? "yes" : "no");
2158 printf("Encrypted: ");
2159 if (i->doc->isEncrypted()) {
2160 printf("yes (print:%s copy:%s change:%s addNotes:%s)\n",
2161 i->doc->okToPrint() ? "yes" : "no",
2162 i->doc->okToCopy() ? "yes" : "no",
2163 i->doc->okToChange() ? "yes" : "no",
2164 i->doc->okToAddNotes() ? "yes" : "no");
2171 pdf_doc->num_pages = i->doc->getNumPages();
2173 if (i->doc->isEncrypted()) {
2174 if(!i->doc->okToCopy()) {
2175 printf("PDF disallows copying.\n");
2178 if(!i->doc->okToChange() || !i->doc->okToAddNotes())
2185 void pdfswf_preparepage(int page)
2189 pages = (int*)malloc(1024*sizeof(int));
2192 if(pagepos == pagebuflen)
2195 pages = (int*)realloc(pages, pagebuflen);
2198 pages[pagepos++] = page;
2205 delete globalParams;globalParams=0;
2206 Object::memCheck(stderr);
2211 void pdf_destroy(pdf_doc_t*pdf_doc)
2213 pdf_doc_internal_t*i= (pdf_doc_internal_t*)pdf_doc->internal;
2215 msg("<debug> pdfswf.cc: pdfswf_close()");
2216 delete i->doc; i->doc=0;
2218 free(pages); pages = 0; //FIXME
2220 free(pdf_doc->internal);pdf_doc->internal=0;
2221 free(pdf_doc);pdf_doc=0;
2224 pdf_page_t* pdf_getpage(pdf_doc_t*pdf_doc, int page)
2226 pdf_doc_internal_t*di= (pdf_doc_internal_t*)pdf_doc->internal;
2228 if(page < 1 || page > pdf_doc->num_pages)
2231 pdf_page_t* pdf_page = (pdf_page_t*)malloc(sizeof(pdf_page_t));
2232 pdf_page_internal_t*pi= (pdf_page_internal_t*)malloc(sizeof(pdf_page_internal_t));
2233 memset(pi, 0, sizeof(pdf_page_internal_t));
2234 pdf_page->internal = pi;
2236 pdf_page->parent = pdf_doc;
2237 pdf_page->nr = page;
2241 void pdf_page_destroy(pdf_page_t*pdf_page)
2243 pdf_page_internal_t*i= (pdf_page_internal_t*)pdf_page->internal;
2244 free(pdf_page->internal);pdf_page->internal = 0;
2245 free(pdf_page);pdf_page=0;
2248 swf_output_t* swf_output_init()
2250 swf_output_t*swf_output = (swf_output_t*)malloc(sizeof(swf_output_t));
2251 memset(swf_output, 0, sizeof(swf_output_t));
2252 swf_output_internal_t*i= (swf_output_internal_t*)malloc(sizeof(swf_output_internal_t));
2253 memset(i, 0, sizeof(swf_output_internal_t));
2254 swf_output->internal = i;
2256 i->outputDev = new SWFOutputDev();
2260 void swf_output_setparameter(swf_output_t*swf_output, char*name, char*value)
2263 pdfswf_setparameter(name, value);
2266 void swf_output_pagefeed(swf_output_t*swf)
2268 swf_output_internal_t*i= (swf_output_internal_t*)swf->internal;
2269 i->outputDev->pagefeed();
2270 i->outputDev->getDimensions(&swf->x1, &swf->y1, &swf->x2, &swf->y2);
2273 int swf_output_save(swf_output_t*swf, char*filename)
2275 swf_output_internal_t*i= (swf_output_internal_t*)swf->internal;
2276 int ret = i->outputDev->save(filename);
2277 i->outputDev->getDimensions(&swf->x1, &swf->y1, &swf->x2, &swf->y2);
2281 void* swf_output_get(swf_output_t*swf)
2283 swf_output_internal_t*i= (swf_output_internal_t*)swf->internal;
2284 void* ret = i->outputDev->getSWF();
2285 i->outputDev->getDimensions(&swf->x1, &swf->y1, &swf->x2, &swf->y2);
2289 void swf_output_destroy(swf_output_t*output)
2291 swf_output_internal_t*i = (swf_output_internal_t*)output->internal;
2292 delete i->outputDev; i->outputDev=0;
2293 free(output->internal);output->internal=0;
2297 void pdf_page_render2(pdf_page_t*page, swf_output_t*swf)
2299 pdf_doc_internal_t*pi = (pdf_doc_internal_t*)page->parent->internal;
2300 swf_output_internal_t*si = (swf_output_internal_t*)swf->internal;
2303 swfoutput_setparameter("protect", "1");
2305 si->outputDev->setXRef(pi->doc, pi->doc->getXRef());
2307 pi->doc->displayPage((OutputDev*)si->outputDev, page->nr, /*zoom*/zoom, /*rotate*/0, /*doLinks*/(int)1);
2309 pi->doc->displayPage((OutputDev*)si->outputDev, page->nr, zoom, zoom, /*rotate*/0, true, /*doLinks*/(int)1);
2311 si->outputDev->getDimensions(&swf->x1, &swf->y1, &swf->x2, &swf->y2);
2314 void pdf_page_rendersection(pdf_page_t*page, swf_output_t*output, int x, int y, int x1, int y1, int x2, int y2)
2316 pdf_doc_internal_t*pi = (pdf_doc_internal_t*)page->parent->internal;
2317 swf_output_internal_t*si = (swf_output_internal_t*)output->internal;
2319 si->outputDev->setMove(x,y);
2320 if((x1|y1|x2|y2)==0) x2++;
2321 si->outputDev->setClip(x1,y1,x2,y2);
2323 pdf_page_render2(page, output);
2325 void pdf_page_render(pdf_page_t*page, swf_output_t*output)
2327 pdf_doc_internal_t*pi = (pdf_doc_internal_t*)page->parent->internal;
2328 swf_output_internal_t*si = (swf_output_internal_t*)output->internal;
2330 si->outputDev->setMove(0,0);
2331 si->outputDev->setClip(0,0,0,0);
2333 pdf_page_render2(page, output);
2337 pdf_page_info_t* pdf_page_getinfo(pdf_page_t*page)
2339 pdf_doc_internal_t*pi = (pdf_doc_internal_t*)page->parent->internal;
2340 pdf_page_internal_t*i= (pdf_page_internal_t*)page->internal;
2341 pdf_page_info_t*info = (pdf_page_info_t*)malloc(sizeof(pdf_page_info_t));
2342 memset(info, 0, sizeof(pdf_page_info_t));
2344 InfoOutputDev*output = new InfoOutputDev;
2347 pi->doc->displayPage((OutputDev*)output, page->nr, /*zoom*/zoom, /*rotate*/0, /*doLinks*/(int)1);
2349 pi->doc->displayPage((OutputDev*)output, page->nr, zoom, zoom, /*rotate*/0, true, /*doLinks*/(int)1);
2352 info->xMin = output->x1;
2353 info->yMin = output->y1;
2354 info->xMax = output->x2;
2355 info->yMax = output->y2;
2356 info->number_of_images = output->num_images;
2357 info->number_of_links = output->num_links;
2358 info->number_of_fonts = output->num_fonts;
2365 void pdf_page_info_destroy(pdf_page_info_t*info)