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* id = getFontID(font);
531 char* name = getFontName(font);
532 Ref* r=font->getID();
533 msg("%s=========== %s (ID:%d,%d) ==========\n", loglevel, name, r->num,r->gen);
535 GString*gstr = font->getTag();
537 msg("%s| Tag: %s\n", loglevel, id);
539 if(font->isCIDFont()) msg("%s| is CID font\n", loglevel);
541 GfxFontType type=font->getType();
543 case fontUnknownType:
544 msg("%s| Type: unknown\n",loglevel);
547 msg("%s| Type: 1\n",loglevel);
550 msg("%s| Type: 1C\n",loglevel);
553 msg("%s| Type: 3\n",loglevel);
556 msg("%s| Type: TrueType\n",loglevel);
559 msg("%s| Type: CIDType0\n",loglevel);
562 msg("%s| Type: CIDType0C\n",loglevel);
565 msg("%s| Type: CIDType2\n",loglevel);
570 GBool embedded = font->getEmbeddedFontID(&embRef);
572 if(font->getEmbeddedFontName()) {
573 embeddedName = font->getEmbeddedFontName()->getCString();
576 msg("%s| Embedded id: %s id: %d\n",loglevel, FIXNULL(embeddedName), embRef.num);
578 gstr = font->getExtFontFile();
580 msg("%s| External Font file: %s\n", loglevel, FIXNULL(gstr->getCString()));
582 // Get font descriptor flags.
583 if(font->isFixedWidth()) msg("%s| is fixed width\n", loglevel);
584 if(font->isSerif()) msg("%s| is serif\n", loglevel);
585 if(font->isSymbolic()) msg("%s| is symbolic\n", loglevel);
586 if(font->isItalic()) msg("%s| is italic\n", loglevel);
587 if(font->isBold()) msg("%s| is bold\n", loglevel);
593 //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");}
594 //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");}
597 void dump_outline(gfxline_t*line)
600 if(line->type == gfx_moveTo) {
601 msg("<debug> | moveTo %.2f %.2f", line->x,line->y);
602 } else if(line->type == gfx_lineTo) {
603 msg("<debug> | lineTo %.2f %.2f", line->x,line->y);
604 } else if(line->type == gfx_splineTo) {
605 msg("<debug> | splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y);
611 gfxline_t* gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed)
613 int num = path->getNumSubpaths();
616 double lastx=0,lasty=0,posx=0,posy=0;
619 msg("<warning> empty path");
623 gfxdrawer_target_gfxline(&draw);
625 for(t = 0; t < num; t++) {
626 GfxSubpath *subpath = path->getSubpath(t);
627 int subnum = subpath->getNumPoints();
628 double bx=0,by=0,cx=0,cy=0;
630 for(s=0;s<subnum;s++) {
632 state->transform(subpath->getX(s),subpath->getY(s),&x,&y);
634 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
635 draw.lineTo(&draw, lastx, lasty);
637 draw.moveTo(&draw, x,y);
642 } else if(subpath->getCurve(s) && cpos==0) {
646 } else if(subpath->getCurve(s) && cpos==1) {
654 draw.lineTo(&draw, x,y);
656 gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y);
663 /* fix non-closed lines */
664 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
665 draw.lineTo(&draw, lastx, lasty);
667 gfxline_t*result = (gfxline_t*)draw.result(&draw);
671 /*----------------------------------------------------------------------------
672 * Primitive Graphic routines
673 *----------------------------------------------------------------------------*/
675 void SWFOutputDev::stroke(GfxState *state)
677 GfxPath * path = state->getPath();
678 int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
679 int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
680 double miterLimit = state->getMiterLimit();
681 double width = state->getTransformedLineWidth();
684 double opaq = state->getStrokeOpacity();
686 state->getFillRGB(&rgb);
688 state->getStrokeRGB(&rgb);
690 col.r = (unsigned char)(rgb.r*255);
691 col.g = (unsigned char)(rgb.g*255);
692 col.b = (unsigned char)(rgb.b*255);
693 col.a = (unsigned char)(opaq*255);
695 gfx_capType capType = gfx_capRound;
696 if(lineCap == 0) capType = gfx_capButt;
697 else if(lineCap == 1) capType = gfx_capRound;
698 else if(lineCap == 2) capType = gfx_capSquare;
700 gfx_joinType joinType = gfx_joinRound;
701 if(lineJoin == 0) joinType = gfx_joinMiter;
702 else if(lineJoin == 1) joinType = gfx_joinRound;
703 else if(lineJoin == 2) joinType = gfx_joinBevel;
705 gfxline_t*line= gfxPath_to_gfxline(state, path, 0);
708 double dashphase = 0;
710 state->getLineDash(&ldash, &dashnum, &dashphase);
712 if(dashnum && ldash) {
713 float * dash = (float*)malloc(sizeof(float)*(dashnum+1));
717 msg("<trace> %d dashes", dashnum);
718 msg("<trace> | phase: %f", dashphase);
719 for(t=0;t<dashnum;t++) {
721 msg("<trace> | d%-3d: %f", t, ldash[t]);
724 if(getLogLevel() >= LOGLEVEL_TRACE) {
728 gfxline_t*line2 = gfxtool_dash_line(line, dash, dashphase);
731 msg("<trace> After dashing:");
734 if(getLogLevel() >= LOGLEVEL_TRACE) {
736 state->getStrokeGray(&gray);
737 msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x gray=%f\n",
739 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
740 lineCap==0?"butt": (lineJoin==1?"round":"square"),
742 col.r,col.g,col.b,col.a,
748 swfoutput_drawgfxline(&output, line, width, &col, capType, joinType, miterLimit);
751 void SWFOutputDev::fill(GfxState *state)
753 GfxPath * path = state->getPath();
754 double opaq = state->getFillOpacity();
756 state->getFillRGB(&rgb);
758 col.r = (unsigned char)(rgb.r*255);
759 col.g = (unsigned char)(rgb.g*255);
760 col.b = (unsigned char)(rgb.b*255);
761 col.a = (unsigned char)(opaq*255);
763 gfxline_t*line= gfxPath_to_gfxline(state, path, 1);
765 if(getLogLevel() >= LOGLEVEL_TRACE) {
766 msg("<trace> fill %02x%02x%02x%02x\n", col.r, col.g, col.b, col.a);
770 swfoutput_fillgfxline(&output, line, &col);
773 void SWFOutputDev::eoFill(GfxState *state)
775 GfxPath * path = state->getPath();
776 double opaq = state->getFillOpacity();
778 state->getFillRGB(&rgb);
780 col.r = (unsigned char)(rgb.r*255);
781 col.g = (unsigned char)(rgb.g*255);
782 col.b = (unsigned char)(rgb.b*255);
783 col.a = (unsigned char)(opaq*255);
785 gfxline_t*line= gfxPath_to_gfxline(state, path, 1);
787 if(getLogLevel() >= LOGLEVEL_TRACE) {
788 msg("<trace> eofill\n");
792 swfoutput_fillgfxline(&output, line, &col);
795 void SWFOutputDev::clip(GfxState *state)
797 GfxPath * path = state->getPath();
798 gfxline_t*line = gfxPath_to_gfxline(state, path, 1);
800 if(getLogLevel() >= LOGLEVEL_TRACE) {
801 msg("<trace> clip\n");
805 swfoutput_startclip(&output, line);
806 states[statepos].clipping++;
809 void SWFOutputDev::eoClip(GfxState *state)
811 GfxPath * path = state->getPath();
812 gfxline_t*line = gfxPath_to_gfxline(state, path, 1);
814 if(getLogLevel() >= LOGLEVEL_TRACE) {
815 msg("<trace> eoclip\n");
819 swfoutput_startclip(&output, line);
820 states[statepos].clipping++;
824 /* pass through functions for swf_output */
825 int SWFOutputDev::save(char*filename)
827 return swfoutput_save(&output, filename);
829 void SWFOutputDev::pagefeed()
831 swfoutput_pagefeed(&output);
833 void* SWFOutputDev::getSWF()
835 return (void*)swfoutput_get(&output);
838 SWFOutputDev::~SWFOutputDev()
840 swfoutput_destroy(&output);
843 GBool SWFOutputDev::upsideDown()
845 msg("<debug> upsidedown? yes");
848 GBool SWFOutputDev::useDrawChar()
852 GBool SWFOutputDev::useGradients()
856 msg("<notice> File contains gradients");
862 char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
863 "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
865 void SWFOutputDev::beginString(GfxState *state, GString *s)
867 int render = state->getRender();
868 msg("<trace> beginString(%s) render=%d", s->getCString(), render);
869 double m11,m21,m12,m22;
870 // msg("<debug> %s beginstring \"%s\"\n", gfxstate2str(state), s->getCString());
871 state->getFontTransMat(&m11, &m12, &m21, &m22);
872 m11 *= state->getHorizScaling();
873 m21 *= state->getHorizScaling();
874 swfoutput_setfontmatrix(&output, m11, -m21, m12, -m22);
875 if(render != 3 && render != 0)
876 msg("<warning> Text rendering mode %d (%s) not fully supported yet (for text \"%s\")", render, renderModeDesc[render&7], s->getCString());
877 states[statepos].textRender = render;
880 static int textCount = 0;
882 void SWFOutputDev::drawChar(GfxState *state, double x, double y,
883 double dx, double dy,
884 double originX, double originY,
885 CharCode c, Unicode *_u, int uLen)
889 int render = state->getRender();
890 // check for invisible text -- this is used by Acrobat Capture
894 if(states[statepos].textRender != render)
895 msg("<error> Internal error: drawChar.render!=beginString.render");
898 double opaq = state->getFillOpacity();
899 state->getFillRGB(&rgb);
901 col.r = (unsigned char)(rgb.r*255);
902 col.g = (unsigned char)(rgb.g*255);
903 col.b = (unsigned char)(rgb.b*255);
904 col.a = (unsigned char)(opaq*255);
906 Gushort *CIDToGIDMap = 0;
907 GfxFont*font = state->getFont();
909 if(font->getType() == fontType3) {
910 /* type 3 chars are passed as graphics */
911 msg("<debug> type3 char at %f/%f", x, y);
917 state->transform(x, y, &x1, &y1);
926 /* find out char name from unicode index
927 TODO: should be precomputed
929 for(t=0;t<sizeof(nameToUnicodeTab)/sizeof(nameToUnicodeTab[0]);t++) {
930 if(nameToUnicodeTab[t].u == u) {
931 name = nameToUnicodeTab[t].name;
938 if(font->isCIDFont()) {
939 GfxCIDFont*cfont = (GfxCIDFont*)font;
941 if(font->getType() == fontCIDType2)
942 CIDToGIDMap = cfont->getCIDToGID();
945 font8 = (Gfx8BitFont*)font;
946 char**enc=font8->getEncoding();
952 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);
953 swfoutput_drawchar(&output, x1, y1, name, CIDToGIDMap[c], u, &col);
955 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);
956 swfoutput_drawchar(&output, x1, y1, name, c, u, &col);
960 void SWFOutputDev::endString(GfxState *state)
962 msg("<trace> endString()");
965 void SWFOutputDev::endTextObject(GfxState *state)
967 msg("<trace> endTextObject()");
970 /* the logic seems to be as following:
971 first, beginType3Char is called, with the charcode and the coordinates.
972 if this function returns true, it already knew about the char and has now drawn it.
973 if the function returns false, it's a new char, and type3D1 is called with some parameters-
974 the all draw operations until endType3Char are part of the char (which in this moment is
975 at the position first passed to beginType3Char). the char ends with endType3Char.
977 The drawing operations between beginType3Char and endType3Char are somewhat different to
978 the normal ones. For example, the fillcolor equals the stroke color.
981 GBool SWFOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode code, Unicode *u, int uLen)
983 msg("<debug> beginType3Char %d, %08x, %d", code, *u, uLen);
985 /* the character itself is going to be passed using the draw functions */
986 return gFalse; /* gTrue= is_in_cache? */
989 void SWFOutputDev::type3D0(GfxState *state, double wx, double wy) {
990 msg("<debug> type3D0 width=%f height=%f", wx, wy);
992 void SWFOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
993 msg("<debug> type3D1 width=%f height=%f bbox=(%f,%f,%f,%f)", wx, wy,
997 void SWFOutputDev::endType3Char(GfxState *state)
1000 msg("<debug> endType3Char");
1003 void SWFOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
1005 this->currentpage = pageNum;
1007 int rot = doc->getPageRotate(1);
1009 msg("<verbose> startPage %d (%f,%f,%f,%f)\n", pageNum, crop_x1, crop_y1, crop_x2, crop_y2);
1011 msg("<verbose> page is rotated %d degrees\n", rot);
1013 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1014 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1015 Use CropBox, not MediaBox, as page size
1022 state->transform(crop_x1,crop_y1,&x1,&y1);
1023 state->transform(crop_x2,crop_y2,&x2,&y2);
1025 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1026 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1028 /* apply user clip box */
1029 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1030 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1031 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1032 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1033 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1036 if(!outputstarted) {
1037 msg("<verbose> Bounding box is (%f,%f)-(%f,%f)", x1,y1,x2,y2);
1038 swfoutput_init(&output);
1042 swfoutput_newpage(&output, pageNum, user_movex, user_movey, (int)x1, (int)y1, (int)x2, (int)y2);
1045 void SWFOutputDev::drawLink(Link *link, Catalog *catalog)
1047 msg("<debug> drawlink\n");
1048 double x1, y1, x2, y2, w;
1054 link->getBorder(&x1, &y1, &x2, &y2, &w);
1056 link->getRect(&x1, &y1, &x2, &y2);
1061 cvtUserToDev(x1, y1, &x, &y);
1062 points[0].x = points[4].x = (int)x;
1063 points[0].y = points[4].y = (int)y;
1064 cvtUserToDev(x2, y1, &x, &y);
1065 points[1].x = (int)x;
1066 points[1].y = (int)y;
1067 cvtUserToDev(x2, y2, &x, &y);
1068 points[2].x = (int)x;
1069 points[2].y = (int)y;
1070 cvtUserToDev(x1, y2, &x, &y);
1071 points[3].x = (int)x;
1072 points[3].y = (int)y;
1074 LinkAction*action=link->getAction();
1081 switch(action->getKind())
1085 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1086 LinkDest *dest=NULL;
1087 if (ha->getDest()==NULL)
1088 dest=catalog->findDest(ha->getNamedDest());
1089 else dest=ha->getDest();
1091 if (dest->isPageRef()){
1092 Ref pageref=dest->getPageRef();
1093 page=catalog->findPage(pageref.num,pageref.gen);
1095 else page=dest->getPageNum();
1096 sprintf(buf, "%d", page);
1103 LinkGoToR*l = (LinkGoToR*)action;
1104 GString*g = l->getNamedDest();
1106 s = strdup(g->getCString());
1111 LinkNamed*l = (LinkNamed*)action;
1112 GString*name = l->getName();
1114 s = strdup(name->lowerCase()->getCString());
1115 named = name->getCString();
1118 if(strstr(s, "next") || strstr(s, "forward"))
1120 page = currentpage + 1;
1122 else if(strstr(s, "prev") || strstr(s, "back"))
1124 page = currentpage - 1;
1126 else if(strstr(s, "last") || strstr(s, "end"))
1128 page = pagepos>0?pages[pagepos-1]:0;
1130 else if(strstr(s, "first") || strstr(s, "top"))
1138 case actionLaunch: {
1140 LinkLaunch*l = (LinkLaunch*)action;
1141 GString * str = new GString(l->getFileName());
1142 str->append(l->getParams());
1143 s = strdup(str->getCString());
1149 LinkURI*l = (LinkURI*)action;
1150 GString*g = l->getURI();
1152 url = g->getCString();
1157 case actionUnknown: {
1159 LinkUnknown*l = (LinkUnknown*)action;
1164 msg("<error> Unknown link type!\n");
1168 if(!s) s = strdup("-?-");
1170 if(!linkinfo && (page || url))
1172 msg("<notice> File contains links");
1178 for(t=0;t<pagepos;t++)
1182 swfoutput_linktopage(&output, t, points);
1186 swfoutput_linktourl(&output, url, points);
1190 swfoutput_namedlink(&output, named, points);
1192 msg("<verbose> \"%s\" link to \"%s\" (%d)\n", type, FIXNULL(s), page);
1196 void SWFOutputDev::saveState(GfxState *state) {
1197 msg("<trace> saveState\n");
1200 msg("<error> Too many nested states in pdf.");
1204 states[statepos].clipping = 0; //? shouldn't this be the current value?
1205 states[statepos].textRender = states[statepos-1].textRender;
1208 void SWFOutputDev::restoreState(GfxState *state) {
1209 msg("<trace> restoreState\n");
1211 while(states[statepos].clipping) {
1212 swfoutput_endclip(&output);
1213 states[statepos].clipping--;
1218 char* SWFOutputDev::searchFont(char*name)
1222 int is_standard_font = 0;
1224 msg("<verbose> SearchFont(%s)", name);
1226 /* see if it is a pdf standard font */
1227 for(i=0;i<sizeof(pdf2t1map)/sizeof(mapping);i++)
1229 if(!strcmp(name, pdf2t1map[i].pdffont))
1231 name = pdf2t1map[i].filename;
1232 is_standard_font = 1;
1236 /* look in all font files */
1237 for(i=0;i<fontnum;i++)
1239 if(strstr(fonts[i].filename, name))
1241 if(!fonts[i].used) {
1244 if(!is_standard_font)
1245 msg("<notice> Using %s for %s", fonts[i].filename, name);
1247 return strdup(fonts[i].filename);
1253 void SWFOutputDev::updateLineWidth(GfxState *state)
1255 double width = state->getTransformedLineWidth();
1256 //swfoutput_setlinewidth(&output, width);
1259 void SWFOutputDev::updateLineCap(GfxState *state)
1261 int c = state->getLineCap();
1264 void SWFOutputDev::updateLineJoin(GfxState *state)
1266 int j = state->getLineJoin();
1269 void SWFOutputDev::updateFillColor(GfxState *state)
1272 double opaq = state->getFillOpacity();
1273 state->getFillRGB(&rgb);
1275 //swfoutput_setfillcolor(&output, (char)(rgb.r*255), (char)(rgb.g*255), (char)(rgb.b*255), (char)(opaq*255));
1278 void SWFOutputDev::updateStrokeColor(GfxState *state)
1281 double opaq = state->getStrokeOpacity();
1282 state->getStrokeRGB(&rgb);
1283 //swfoutput_setstrokecolor(&output, (char)(rgb.r*255), (char)(rgb.g*255), (char)(rgb.b*255), (char)(opaq*255));
1286 void FoFiWrite(void *stream, char *data, int len)
1288 fwrite(data, len, 1, (FILE*)stream);
1291 char*SWFOutputDev::writeEmbeddedFontToFile(XRef*ref, GfxFont*font)
1293 char*tmpFileName = NULL;
1299 Object refObj, strObj;
1301 tmpFileName = mktmpname(namebuf);
1304 ret = font->getEmbeddedFontID(&embRef);
1306 msg("<verbose> Didn't get embedded font id");
1307 /* not embedded- the caller should now search the font
1308 directories for this font */
1312 f = fopen(tmpFileName, "wb");
1314 msg("<error> Couldn't create temporary Type 1 font file");
1318 /*if(font->isCIDFont()) {
1319 GfxCIDFont* cidFont = (GfxCIDFont *)font;
1320 GString c = cidFont->getCollection();
1321 msg("<notice> Collection: %s", c.getCString());
1324 if (font->getType() == fontType1C) {
1325 if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
1327 msg("<error> Couldn't read embedded font file");
1331 Type1CFontFile *cvt = new Type1CFontFile(fontBuf, fontLen);
1333 cvt->convertToType1(f);
1335 FoFiType1C *cvt = FoFiType1C::make(fontBuf, fontLen);
1337 cvt->convertToType1(NULL, gTrue, FoFiWrite, f);
1339 //cvt->convertToCIDType0("test", f);
1340 //cvt->convertToType0("test", f);
1343 } else if(font->getType() == fontTrueType) {
1344 msg("<verbose> writing font using TrueTypeFontFile::writeTTF");
1345 if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
1347 msg("<error> Couldn't read embedded font file");
1351 TrueTypeFontFile *cvt = new TrueTypeFontFile(fontBuf, fontLen);
1354 FoFiTrueType *cvt = FoFiTrueType::make(fontBuf, fontLen);
1355 cvt->writeTTF(FoFiWrite, f);
1360 font->getEmbeddedFontID(&embRef);
1361 refObj.initRef(embRef.num, embRef.gen);
1362 refObj.fetch(ref, &strObj);
1364 strObj.streamReset();
1369 f4[t] = strObj.streamGetChar();
1370 f4c[t] = (char)f4[t];
1375 if(!strncmp(f4c, "true", 4)) {
1376 /* some weird TTF fonts don't start with 0,1,0,0 but with "true".
1377 Change this on the fly */
1378 f4[0] = f4[2] = f4[3] = 0;
1386 while ((c = strObj.streamGetChar()) != EOF) {
1390 strObj.streamClose();
1395 return strdup(tmpFileName);
1398 char* searchForSuitableFont(GfxFont*gfxFont)
1400 char*name = getFontName(gfxFont);
1404 if(!config_use_fontconfig)
1407 #ifdef HAVE_FONTCONFIG
1408 FcPattern *pattern, *match;
1412 static int fcinitcalled = false;
1414 msg("<debug> searchForSuitableFont(%s)", name);
1416 // call init ony once
1417 if (!fcinitcalled) {
1418 msg("<debug> Initializing FontConfig...");
1419 fcinitcalled = true;
1421 msg("<debug> FontConfig Initialization failed. Disabling.");
1422 config_use_fontconfig = 0;
1425 msg("<debug> ...initialized FontConfig");
1428 msg("<debug> FontConfig: Create \"%s\" Family Pattern", name);
1429 pattern = FcPatternBuild(NULL, FC_FAMILY, FcTypeString, name, NULL);
1430 if (gfxFont->isItalic()) // check for italic
1431 msg("<debug> FontConfig: Adding Italic Slant");
1432 FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
1433 if (gfxFont->isBold()) // check for bold
1434 msg("<debug> FontConfig: Adding Bold Weight");
1435 FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
1437 msg("<debug> FontConfig: Try to match...");
1438 // configure and match using the original font name
1439 FcConfigSubstitute(0, pattern, FcMatchPattern);
1440 FcDefaultSubstitute(pattern);
1441 match = FcFontMatch(0, pattern, &result);
1443 if (FcPatternGetString(match, "family", 0, &v) == FcResultMatch) {
1444 msg("<debug> FontConfig: family=%s", (char*)v);
1445 // if we get an exact match
1446 if (strcmp((char *)v, name) == 0) {
1447 if (FcPatternGetString(match, "file", 0, &v) == FcResultMatch) {
1448 filename = strdup((char*)v); // mem leak
1449 char *nfn = strrchr(filename, '/');
1450 if(nfn) fontname = strdup(nfn+1);
1451 else fontname = filename;
1453 msg("<debug> FontConfig: Returning \"%s\"", fontname);
1455 // initialize patterns
1456 FcPatternDestroy(pattern);
1457 FcPatternDestroy(match);
1459 // now match against serif etc.
1460 if (gfxFont->isSerif()) {
1461 msg("<debug> FontConfig: Create Serif Family Pattern");
1462 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "serif", NULL);
1463 } else if (gfxFont->isFixedWidth()) {
1464 msg("<debug> FontConfig: Create Monospace Family Pattern");
1465 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "monospace", NULL);
1467 msg("<debug> FontConfig: Create Sans Family Pattern");
1468 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "sans", NULL);
1472 if (gfxFont->isItalic()) {
1473 msg("<debug> FontConfig: Adding Italic Slant");
1474 int bb = FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
1477 if (gfxFont->isBold()) {
1478 msg("<debug> FontConfig: Adding Bold Weight");
1479 int bb = FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
1482 msg("<debug> FontConfig: Try to match... (2)");
1483 // configure and match using serif etc
1484 FcConfigSubstitute (0, pattern, FcMatchPattern);
1485 FcDefaultSubstitute (pattern);
1486 match = FcFontMatch (0, pattern, &result);
1488 if (FcPatternGetString(match, "file", 0, &v) == FcResultMatch) {
1489 filename = strdup((char*)v); // mem leak
1490 char *nfn = strrchr(filename, '/');
1491 if(nfn) fontname = strdup(nfn+1);
1492 else fontname = filename;
1494 msg("<debug> FontConfig: Returning \"%s\"", fontname);
1498 //printf("FONTCONFIG: pattern");
1499 //FcPatternPrint(pattern);
1500 //printf("FONTCONFIG: match");
1501 //FcPatternPrint(match);
1503 FcPatternDestroy(pattern);
1504 FcPatternDestroy(match);
1506 pdfswf_addfont(filename);
1513 char* SWFOutputDev::substituteFont(GfxFont*gfxFont, char* oldname)
1515 char*fontname = 0, *filename = 0;
1516 msg("<notice> subsituteFont(%s)", oldname);
1518 if(!(fontname = searchForSuitableFont(gfxFont))) {
1519 fontname = "Times-Roman";
1521 filename = searchFont(fontname);
1523 msg("<error> Couldn't find font %s- did you install the default fonts?", fontname);
1527 if(substitutepos>=sizeof(substitutesource)/sizeof(char*)) {
1528 msg("<fatal> Too many fonts in file.");
1532 substitutesource[substitutepos] = strdup(oldname); //mem leak
1533 substitutetarget[substitutepos] = fontname;
1534 msg("<notice> substituting %s -> %s", FIXNULL(oldname), FIXNULL(fontname));
1537 return strdup(filename); //mem leak
1540 void unlinkfont(char* filename)
1547 if(!strncmp(&filename[l-4],".afm",4)) {
1548 memcpy(&filename[l-4],".pfb",4);
1550 memcpy(&filename[l-4],".pfa",4);
1552 memcpy(&filename[l-4],".afm",4);
1555 if(!strncmp(&filename[l-4],".pfa",4)) {
1556 memcpy(&filename[l-4],".afm",4);
1558 memcpy(&filename[l-4],".pfa",4);
1561 if(!strncmp(&filename[l-4],".pfb",4)) {
1562 memcpy(&filename[l-4],".afm",4);
1564 memcpy(&filename[l-4],".pfb",4);
1569 void SWFOutputDev::setXRef(PDFDoc*doc, XRef *xref)
1576 void SWFOutputDev::updateFont(GfxState *state)
1578 GfxFont*gfxFont = state->getFont();
1583 char * fontid = getFontID(gfxFont);
1586 /* first, look if we substituted this font before-
1587 this way, we don't initialize the T1 Fonts
1589 for(t=0;t<substitutepos;t++) {
1590 if(!strcmp(fontid, substitutesource[t])) {
1591 free(fontid);fontid=0;
1592 fontid = strdup(substitutetarget[t]);
1597 /* second, see if swfoutput already has this font
1598 cached- if so, we are done */
1599 if(swfoutput_queryfont(&output, fontid))
1601 swfoutput_setfont(&output, fontid, 0);
1603 msg("<debug> updateFont(%s) [cached]", fontid);
1608 // look for Type 3 font
1609 if (gfxFont->getType() == fontType3) {
1611 type3Warning = gTrue;
1612 showFontError(gfxFont, 2);
1618 /* now either load the font, or find a substitution */
1621 GBool embedded = gfxFont->getEmbeddedFontID(&embRef);
1626 (gfxFont->getType() == fontType1 ||
1627 gfxFont->getType() == fontType1C ||
1628 (gfxFont->getType() == fontCIDType0C && forceType0Fonts) ||
1629 gfxFont->getType() == fontTrueType ||
1630 gfxFont->getType() == fontCIDType2
1633 fileName = writeEmbeddedFontToFile(xref, gfxFont);
1634 if(!fileName) showFontError(gfxFont,0);
1637 char * fontname = getFontName(gfxFont);
1638 fileName = searchFont(fontname);
1639 if(!fileName) showFontError(gfxFont,0);
1642 char * fontname = getFontName(gfxFont);
1643 msg("<warning> Font %s %scould not be loaded.", fontname, embedded?"":"(not embedded) ");
1644 msg("<warning> Try putting a TTF version of that font (named \"%s.ttf\") into /swftools/fonts", fontname);
1645 fileName = substituteFont(gfxFont, fontid);
1646 if(fontid) { free(fontid);fontid = strdup(substitutetarget[substitutepos-1]); /*ugly hack*/};
1647 msg("<notice> Font is now %s (%s)", fontid, fileName);
1651 msg("<error> Couldn't set font %s\n", fontid);
1656 msg("<verbose> updateFont(%s) -> %s", fontid, fileName);
1657 dumpFontInfo("<verbose>", gfxFont);
1659 swfoutput_setfont(&output, fontid, fileName);
1661 /*if(fileName && del)
1662 unlinkfont(fileName);*/
1668 #define SQR(x) ((x)*(x))
1670 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
1672 if((newwidth<2 || newheight<2) ||
1673 (width<=newwidth || height<=newheight))
1675 unsigned char*newdata;
1677 newdata= (unsigned char*)malloc(newwidth*newheight);
1679 double fx = (double)(width)/newwidth;
1680 double fy = (double)(height)/newheight;
1682 int blocksize = (int)(8192/(fx*fy));
1683 int r = 8192*256/palettesize;
1684 for(x=0;x<newwidth;x++) {
1685 double ex = px + fx;
1686 int fromx = (int)px;
1688 int xweight1 = (int)(((fromx+1)-px)*256);
1689 int xweight2 = (int)((ex-tox)*256);
1691 for(y=0;y<newheight;y++) {
1692 double ey = py + fy;
1693 int fromy = (int)py;
1695 int yweight1 = (int)(((fromy+1)-py)*256);
1696 int yweight2 = (int)((ey-toy)*256);
1699 for(xx=fromx;xx<=tox;xx++)
1700 for(yy=fromy;yy<=toy;yy++) {
1701 int b = 1-data[width*yy+xx];
1703 if(xx==fromx) weight = (weight*xweight1)/256;
1704 if(xx==tox) weight = (weight*xweight2)/256;
1705 if(yy==fromy) weight = (weight*yweight1)/256;
1706 if(yy==toy) weight = (weight*yweight2)/256;
1709 //if(a) a=(palettesize-1)*r/blocksize;
1710 newdata[y*newwidth+x] = (a*blocksize)/r;
1718 void SWFOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
1719 int width, int height, GfxImageColorMap*colorMap, GBool invert,
1720 GBool inlineImg, int mask, int*maskColors)
1725 double x1,y1,x2,y2,x3,y3,x4,y4;
1726 ImageStream *imgStr;
1733 ncomps = colorMap->getNumPixelComps();
1734 bits = colorMap->getBits();
1736 imgStr = new ImageStream(str, width, ncomps,bits);
1739 if(!width || !height || (height<=1 && width<=1))
1741 msg("<verbose> Ignoring %d by %d image", width, height);
1742 unsigned char buf[8];
1744 for (y = 0; y < height; ++y)
1745 for (x = 0; x < width; ++x) {
1746 imgStr->getPixel(buf);
1752 state->transform(0, 1, &x1, &y1);
1753 state->transform(0, 0, &x2, &y2);
1754 state->transform(1, 0, &x3, &y3);
1755 state->transform(1, 1, &x4, &y4);
1757 if(!pbminfo && !(str->getKind()==strDCT)) {
1759 msg("<notice> file contains pbm pictures %s",mask?"(masked)":"");
1763 msg("<verbose> drawing %d by %d masked picture\n", width, height);
1765 if(!jpeginfo && (str->getKind()==strDCT)) {
1766 msg("<notice> file contains jpeg pictures");
1772 unsigned char buf[8];
1774 unsigned char*pic = new unsigned char[width*height];
1777 state->getFillRGB(&rgb);
1779 memset(pal,255,sizeof(pal));
1780 pal[0].r = (int)(rgb.r*255); pal[1].r = 0;
1781 pal[0].g = (int)(rgb.g*255); pal[1].g = 0;
1782 pal[0].b = (int)(rgb.b*255); pal[1].b = 0;
1783 pal[0].a = 255; pal[1].a = 0;
1786 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
1787 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
1788 for (y = 0; y < height; ++y)
1789 for (x = 0; x < width; ++x)
1791 imgStr->getPixel(buf);
1794 pic[width*y+x] = buf[0];
1797 /* the size of the drawn image is added to the identifier
1798 as the same image may require different bitmaps if displayed
1799 at different sizes (due to antialiasing): */
1802 unsigned char*pic2 = 0;
1805 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
1814 height = realheight;
1818 /* make a black/white palette */
1823 float r = 255/(numpalette-1);
1825 for(t=0;t<numpalette;t++) {
1826 pal[t].r = (U8)(255*rgb.r);
1827 pal[t].g = (U8)(255*rgb.g);
1828 pal[t].b = (U8)(255*rgb.b);
1829 pal[t].a = (U8)(t*r);
1833 RGBA*pic2 = new RGBA[width*height];
1834 for (y = 0; y < height; ++y) {
1835 for (x = 0; x < width; ++x) {
1836 pic2[width*y+x] = pal[pic[y*width+x]];
1839 swfoutput_drawimagelossless(&output, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1848 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
1849 RGBA*pic=new RGBA[width*height];
1850 for (y = 0; y < height; ++y) {
1851 for (x = 0; x < width; ++x) {
1852 imgStr->getPixel(pixBuf);
1853 colorMap->getRGB(pixBuf, &rgb);
1854 pic[width*y+x].r = (U8)(rgb.r * 255 + 0.5);
1855 pic[width*y+x].g = (U8)(rgb.g * 255 + 0.5);
1856 pic[width*y+x].b = (U8)(rgb.b * 255 + 0.5);
1857 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
1860 if(str->getKind()==strDCT)
1861 swfoutput_drawimagejpeg(&output, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1863 swfoutput_drawimagelossless(&output, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1868 RGBA*pic=new RGBA[width*height];
1871 for(t=0;t<256;t++) {
1873 colorMap->getRGB(pixBuf, &rgb);
1874 /*if(maskColors && *maskColors==t) {
1875 msg("<notice> Color %d is transparent", t);
1876 if (imgData->maskColors) {
1878 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
1879 if (pix[i] < imgData->maskColors[2*i] ||
1880 pix[i] > imgData->maskColors[2*i+1]) {
1895 pal[t].r = (U8)(rgb.r * 255 + 0.5);
1896 pal[t].g = (U8)(rgb.g * 255 + 0.5);
1897 pal[t].b = (U8)(rgb.b * 255 + 0.5);
1898 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
1901 for (y = 0; y < height; ++y) {
1902 for (x = 0; x < width; ++x) {
1903 imgStr->getPixel(pixBuf);
1904 pic[width*y+x] = pal[pixBuf[0]];
1907 swfoutput_drawimagelossless(&output, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1915 void SWFOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
1916 int width, int height, GBool invert,
1919 if(states[statepos].textRender & 4) //clipped
1921 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
1922 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0);
1925 void SWFOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
1926 int width, int height, GfxImageColorMap *colorMap,
1927 int *maskColors, GBool inlineImg)
1929 if(states[statepos].textRender & 4) //clipped
1932 msg("<verbose> drawImage %dx%d, %s %s, inline=%d", width, height,
1933 colorMap?"colorMap":"no colorMap",
1934 maskColors?"maskColors":"no maskColors",
1937 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
1938 colorMap->getBits(),colorMap->getColorSpace()->getMode());
1939 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors);
1942 SWFOutputDev*output = 0;
1944 static void printInfoString(Dict *infoDict, char *key, char *fmt) {
1949 if (infoDict->lookup(key, &obj)->isString()) {
1950 s1 = obj.getString();
1951 if ((s1->getChar(0) & 0xff) == 0xfe &&
1952 (s1->getChar(1) & 0xff) == 0xff) {
1954 for (i = 2; i < obj.getString()->getLength(); i += 2) {
1955 if (s1->getChar(i) == '\0') {
1956 s2->append(s1->getChar(i+1));
1959 s2 = new GString("<unicode>");
1963 printf(fmt, s2->getCString());
1966 printf(fmt, s1->getCString());
1972 static void printInfoDate(Dict *infoDict, char *key, char *fmt) {
1976 if (infoDict->lookup(key, &obj)->isString()) {
1977 s = obj.getString()->getCString();
1978 if (s[0] == 'D' && s[1] == ':') {
1989 void pdfswf_setparameter(char*name, char*value)
1991 msg("<verbose> setting parameter %s to \"%s\"", name, value);
1992 if(!strcmp(name, "caplinewidth")) {
1993 caplinewidth = atof(value);
1994 } else if(!strcmp(name, "zoom")) {
1997 sprintf(buf, "%f", (double)jpeg_dpi/(double)zoom);
1998 swfoutput_setparameter("jpegsubpixels", buf);
1999 sprintf(buf, "%f", (double)ppm_dpi/(double)zoom);
2000 swfoutput_setparameter("ppmsubpixels", buf);
2001 } else if(!strcmp(name, "jpegdpi")) {
2003 jpeg_dpi = atoi(value);
2004 sprintf(buf, "%f", (double)jpeg_dpi/(double)zoom);
2005 swfoutput_setparameter("jpegsubpixels", buf);
2006 } else if(!strcmp(name, "ppmdpi")) {
2008 ppm_dpi = atoi(value);
2009 sprintf(buf, "%f", (double)ppm_dpi/(double)zoom);
2010 swfoutput_setparameter("ppmsubpixels", buf);
2011 } else if(!strcmp(name, "forceType0Fonts")) {
2012 forceType0Fonts = atoi(value);
2013 } else if(!strcmp(name, "fontdir")) {
2014 pdfswf_addfontdir(value);
2015 } else if(!strcmp(name, "languagedir")) {
2016 pdfswf_addlanguagedir(value);
2017 } else if(!strcmp(name, "fontconfig")) {
2018 config_use_fontconfig = atoi(value);
2020 swfoutput_setparameter(name, value);
2023 void pdfswf_addfont(char*filename)
2026 memset(&f, 0, sizeof(fontfile_t));
2027 f.filename = filename;
2028 if(fontnum < sizeof(fonts)/sizeof(fonts[0])) {
2029 fonts[fontnum++] = f;
2031 msg("<error> Too many external fonts. Not adding font file \"%s\".", filename);
2035 static char* dirseparator()
2044 void pdfswf_addlanguagedir(char*dir)
2047 globalParams = new GlobalParams("");
2049 msg("<notice> Adding %s to language pack directories", dir);
2053 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc"));
2054 strcpy(config_file, dir);
2055 strcat(config_file, dirseparator());
2056 strcat(config_file, "add-to-xpdfrc");
2058 fi = fopen(config_file, "rb");
2060 msg("<error> Could not open %s", config_file);
2063 globalParams->parseFile(new GString(config_file), fi);
2067 void pdfswf_addfontdir(char*dirname)
2069 #ifdef HAVE_DIRENT_H
2070 msg("<notice> Adding %s to font directories", dirname);
2071 DIR*dir = opendir(dirname);
2073 msg("<warning> Couldn't open directory %s\n", dirname);
2078 ent = readdir (dir);
2082 char*name = ent->d_name;
2088 if(!strncasecmp(&name[l-4], ".pfa", 4))
2090 if(!strncasecmp(&name[l-4], ".pfb", 4))
2092 if(!strncasecmp(&name[l-4], ".ttf", 4))
2096 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2097 strcpy(fontname, dirname);
2098 strcat(fontname, dirseparator());
2099 strcat(fontname, name);
2100 msg("<verbose> Adding %s to fonts", fontname);
2101 pdfswf_addfont(fontname);
2106 msg("<warning> No dirent.h- unable to add font dir %s", dir);
2111 typedef struct _pdf_doc_internal
2115 } pdf_doc_internal_t;
2116 typedef struct _pdf_page_internal
2118 } pdf_page_internal_t;
2119 typedef struct _swf_output_internal
2121 SWFOutputDev*outputDev;
2122 } swf_output_internal_t;
2124 pdf_doc_t* pdf_init(char*filename, char*userPassword)
2126 pdf_doc_t*pdf_doc = (pdf_doc_t*)malloc(sizeof(pdf_doc_t));
2127 memset(pdf_doc, 0, sizeof(pdf_doc_t));
2128 pdf_doc_internal_t*i= (pdf_doc_internal_t*)malloc(sizeof(pdf_doc_internal_t));
2129 memset(i, 0, sizeof(pdf_doc_internal_t));
2130 pdf_doc->internal = i;
2132 GString *fileName = new GString(filename);
2138 globalParams = new GlobalParams("");
2141 if (userPassword && userPassword[0]) {
2142 userPW = new GString(userPassword);
2146 i->doc = new PDFDoc(fileName, userPW);
2150 if (!i->doc->isOk()) {
2155 i->doc->getDocInfo(&info);
2156 if (info.isDict() &&
2157 (getScreenLogLevel()>=LOGLEVEL_NOTICE)) {
2158 printInfoString(info.getDict(), "Title", "Title: %s\n");
2159 printInfoString(info.getDict(), "Subject", "Subject: %s\n");
2160 printInfoString(info.getDict(), "Keywords", "Keywords: %s\n");
2161 printInfoString(info.getDict(), "Author", "Author: %s\n");
2162 printInfoString(info.getDict(), "Creator", "Creator: %s\n");
2163 printInfoString(info.getDict(), "Producer", "Producer: %s\n");
2164 printInfoDate(info.getDict(), "CreationDate", "CreationDate: %s\n");
2165 printInfoDate(info.getDict(), "ModDate", "ModDate: %s\n");
2166 printf("Pages: %d\n", i->doc->getNumPages());
2167 printf("Linearized: %s\n", i->doc->isLinearized() ? "yes" : "no");
2168 printf("Encrypted: ");
2169 if (i->doc->isEncrypted()) {
2170 printf("yes (print:%s copy:%s change:%s addNotes:%s)\n",
2171 i->doc->okToPrint() ? "yes" : "no",
2172 i->doc->okToCopy() ? "yes" : "no",
2173 i->doc->okToChange() ? "yes" : "no",
2174 i->doc->okToAddNotes() ? "yes" : "no");
2181 pdf_doc->num_pages = i->doc->getNumPages();
2183 if (i->doc->isEncrypted()) {
2184 if(!i->doc->okToCopy()) {
2185 printf("PDF disallows copying.\n");
2188 if(!i->doc->okToChange() || !i->doc->okToAddNotes())
2195 void pdfswf_preparepage(int page)
2199 pages = (int*)malloc(1024*sizeof(int));
2202 if(pagepos == pagebuflen)
2205 pages = (int*)realloc(pages, pagebuflen);
2208 pages[pagepos++] = page;
2215 delete globalParams;globalParams=0;
2216 Object::memCheck(stderr);
2221 void pdf_destroy(pdf_doc_t*pdf_doc)
2223 pdf_doc_internal_t*i= (pdf_doc_internal_t*)pdf_doc->internal;
2225 msg("<debug> pdfswf.cc: pdfswf_close()");
2226 delete i->doc; i->doc=0;
2228 free(pages); pages = 0; //FIXME
2230 free(pdf_doc->internal);pdf_doc->internal=0;
2231 free(pdf_doc);pdf_doc=0;
2234 pdf_page_t* pdf_getpage(pdf_doc_t*pdf_doc, int page)
2236 pdf_doc_internal_t*di= (pdf_doc_internal_t*)pdf_doc->internal;
2238 if(page < 1 || page > pdf_doc->num_pages)
2241 pdf_page_t* pdf_page = (pdf_page_t*)malloc(sizeof(pdf_page_t));
2242 pdf_page_internal_t*pi= (pdf_page_internal_t*)malloc(sizeof(pdf_page_internal_t));
2243 memset(pi, 0, sizeof(pdf_page_internal_t));
2244 pdf_page->internal = pi;
2246 pdf_page->parent = pdf_doc;
2247 pdf_page->nr = page;
2251 void pdf_page_destroy(pdf_page_t*pdf_page)
2253 pdf_page_internal_t*i= (pdf_page_internal_t*)pdf_page->internal;
2254 free(pdf_page->internal);pdf_page->internal = 0;
2255 free(pdf_page);pdf_page=0;
2258 swf_output_t* swf_output_init()
2260 swf_output_t*swf_output = (swf_output_t*)malloc(sizeof(swf_output_t));
2261 memset(swf_output, 0, sizeof(swf_output_t));
2262 swf_output_internal_t*i= (swf_output_internal_t*)malloc(sizeof(swf_output_internal_t));
2263 memset(i, 0, sizeof(swf_output_internal_t));
2264 swf_output->internal = i;
2266 i->outputDev = new SWFOutputDev();
2270 void swf_output_setparameter(swf_output_t*swf_output, char*name, char*value)
2273 pdfswf_setparameter(name, value);
2276 void swf_output_pagefeed(swf_output_t*swf)
2278 swf_output_internal_t*i= (swf_output_internal_t*)swf->internal;
2279 i->outputDev->pagefeed();
2280 i->outputDev->getDimensions(&swf->x1, &swf->y1, &swf->x2, &swf->y2);
2283 int swf_output_save(swf_output_t*swf, char*filename)
2285 swf_output_internal_t*i= (swf_output_internal_t*)swf->internal;
2286 int ret = i->outputDev->save(filename);
2287 i->outputDev->getDimensions(&swf->x1, &swf->y1, &swf->x2, &swf->y2);
2291 void* swf_output_get(swf_output_t*swf)
2293 swf_output_internal_t*i= (swf_output_internal_t*)swf->internal;
2294 void* ret = i->outputDev->getSWF();
2295 i->outputDev->getDimensions(&swf->x1, &swf->y1, &swf->x2, &swf->y2);
2299 void swf_output_destroy(swf_output_t*output)
2301 swf_output_internal_t*i = (swf_output_internal_t*)output->internal;
2302 delete i->outputDev; i->outputDev=0;
2303 free(output->internal);output->internal=0;
2307 void pdf_page_render2(pdf_page_t*page, swf_output_t*swf)
2309 pdf_doc_internal_t*pi = (pdf_doc_internal_t*)page->parent->internal;
2310 swf_output_internal_t*si = (swf_output_internal_t*)swf->internal;
2313 swfoutput_setparameter("protect", "1");
2315 si->outputDev->setXRef(pi->doc, pi->doc->getXRef());
2317 pi->doc->displayPage((OutputDev*)si->outputDev, page->nr, /*zoom*/zoom, /*rotate*/0, /*doLinks*/(int)1);
2319 pi->doc->displayPage((OutputDev*)si->outputDev, page->nr, zoom, zoom, /*rotate*/0, true, /*doLinks*/(int)1);
2321 si->outputDev->getDimensions(&swf->x1, &swf->y1, &swf->x2, &swf->y2);
2324 void pdf_page_rendersection(pdf_page_t*page, swf_output_t*output, int x, int y, int x1, int y1, int x2, int y2)
2326 pdf_doc_internal_t*pi = (pdf_doc_internal_t*)page->parent->internal;
2327 swf_output_internal_t*si = (swf_output_internal_t*)output->internal;
2329 si->outputDev->setMove(x,y);
2330 if((x1|y1|x2|y2)==0) x2++;
2331 si->outputDev->setClip(x1,y1,x2,y2);
2333 pdf_page_render2(page, output);
2335 void pdf_page_render(pdf_page_t*page, swf_output_t*output)
2337 pdf_doc_internal_t*pi = (pdf_doc_internal_t*)page->parent->internal;
2338 swf_output_internal_t*si = (swf_output_internal_t*)output->internal;
2340 si->outputDev->setMove(0,0);
2341 si->outputDev->setClip(0,0,0,0);
2343 pdf_page_render2(page, output);
2347 pdf_page_info_t* pdf_page_getinfo(pdf_page_t*page)
2349 pdf_doc_internal_t*pi = (pdf_doc_internal_t*)page->parent->internal;
2350 pdf_page_internal_t*i= (pdf_page_internal_t*)page->internal;
2351 pdf_page_info_t*info = (pdf_page_info_t*)malloc(sizeof(pdf_page_info_t));
2352 memset(info, 0, sizeof(pdf_page_info_t));
2354 InfoOutputDev*output = new InfoOutputDev;
2357 pi->doc->displayPage((OutputDev*)output, page->nr, /*zoom*/zoom, /*rotate*/0, /*doLinks*/(int)1);
2359 pi->doc->displayPage((OutputDev*)output, page->nr, zoom, zoom, /*rotate*/0, true, /*doLinks*/(int)1);
2362 info->xMin = output->x1;
2363 info->yMin = output->y1;
2364 info->xMax = output->x2;
2365 info->yMax = output->y2;
2366 info->number_of_images = output->num_images;
2367 info->number_of_links = output->num_links;
2368 info->number_of_fonts = output->num_fonts;
2375 void pdf_page_info_destroy(pdf_page_info_t*info)