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
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"
63 #include "SWFOutputDev.h"
65 //swftools header files
66 #include "swfoutput.h"
67 #include "../lib/log.h"
68 #include "../lib/gfxdevice.h"
69 #include "../lib/gfxtools.h"
70 #include "../lib/gfxfont.h"
74 typedef struct _fontfile
80 typedef struct _parameter
84 struct _parameter*next;
87 static parameter_t* device_config = 0;
88 static parameter_t* device_config_next = 0;
91 static fontfile_t fonts[2048];
92 static int fontnum = 0;
94 static int config_use_fontconfig = 1;
97 static double caplinewidth = 3.0;
98 static double zoom = 72; /* xpdf: 86 */
99 static int forceType0Fonts = 1;
101 static void printInfoString(Dict *infoDict, char *key, char *fmt);
102 static void printInfoDate(Dict *infoDict, char *key, char *fmt);
104 static char* lastfontdir = 0;
110 {"Times-Roman", "n021003l"},
111 {"Times-Italic", "n021023l"},
112 {"Times-Bold", "n021004l"},
113 {"Times-BoldItalic", "n021024l"},
114 {"Helvetica", "n019003l"},
115 {"Helvetica-Oblique", "n019023l"},
116 {"Helvetica-Bold", "n019004l"},
117 {"Helvetica-BoldOblique", "n019024l"},
118 {"Courier", "n022003l"},
119 {"Courier-Oblique", "n022023l"},
120 {"Courier-Bold", "n022004l"},
121 {"Courier-BoldOblique", "n022024l"},
122 {"Symbol", "s050000l"},
123 {"ZapfDingbats", "d050000l"}};
125 class SWFOutputState {
131 this->textRender = 0;
135 typedef struct _fontlist
145 class SWFOutputDev: public OutputDev {
153 virtual ~SWFOutputDev() ;
155 void setMove(int x,int y);
156 void setClip(int x1,int y1,int x2,int y2);
158 void setInfo(InfoOutputDev*info) {this->info = info;}
160 int save(char*filename);
163 void startFrame(int width, int height);
165 virtual void startPage(int pageNum, GfxState *state, double x1, double y1, double x2, double y2) ;
168 void* get(char*name);
170 //----- get info about output device
172 // Does this device use upside-down coordinates?
173 // (Upside-down means (0,0) is the top left corner of the page.)
174 virtual GBool upsideDown();
176 // Does this device use drawChar() or drawString()?
177 virtual GBool useDrawChar();
179 // Can this device draw gradients?
180 virtual GBool useGradients();
182 virtual GBool interpretType3Chars() {return gTrue;}
184 //----- initialization and control
186 void setXRef(PDFDoc*doc, XRef *xref);
189 virtual void drawLink(Link *link, Catalog *catalog) ;
191 //----- save/restore graphics state
192 virtual void saveState(GfxState *state) ;
193 virtual void restoreState(GfxState *state) ;
195 //----- update graphics state
197 virtual void updateFont(GfxState *state);
198 virtual void updateFillColor(GfxState *state);
199 virtual void updateStrokeColor(GfxState *state);
200 virtual void updateLineWidth(GfxState *state);
201 virtual void updateLineJoin(GfxState *state);
202 virtual void updateLineCap(GfxState *state);
204 virtual void updateAll(GfxState *state)
207 updateFillColor(state);
208 updateStrokeColor(state);
209 updateLineWidth(state);
210 updateLineJoin(state);
211 updateLineCap(state);
214 //----- path painting
215 virtual void stroke(GfxState *state) ;
216 virtual void fill(GfxState *state) ;
217 virtual void eoFill(GfxState *state) ;
219 //----- path clipping
220 virtual void clip(GfxState *state) ;
221 virtual void eoClip(GfxState *state) ;
224 virtual void beginString(GfxState *state, GString *s) ;
225 virtual void endString(GfxState *state) ;
226 virtual void endTextObject(GfxState *state);
227 virtual void drawChar(GfxState *state, double x, double y,
228 double dx, double dy,
229 double originX, double originY,
230 CharCode code, Unicode *u, int uLen);
232 //----- image drawing
233 virtual void drawImageMask(GfxState *state, Object *ref, Stream *str,
234 int width, int height, GBool invert,
236 virtual void drawImage(GfxState *state, Object *ref, Stream *str,
237 int width, int height, GfxImageColorMap *colorMap,
238 int *maskColors, GBool inlineImg);
240 virtual GBool beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode code, Unicode *u, int uLen);
241 virtual void endType3Char(GfxState *state);
243 virtual void type3D0(GfxState *state, double wx, double wy);
244 virtual void type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury);
247 void drawGeneralImage(GfxState *state, Object *ref, Stream *str,
248 int width, int height, GfxImageColorMap*colorMap, GBool invert,
249 GBool inlineImg, int mask, int *maskColors);
250 int SWFOutputDev::setGfxFont(char*id, char*filename, double quality);
251 void strokeGfxline(GfxState *state, gfxline_t*line);
252 void clipToGfxLine(GfxState *state, gfxline_t*line);
253 void fillGfxLine(GfxState *state, gfxline_t*line);
257 gfxresult_t*result; //filled when complete
259 char outer_clip_box; //whether the page clip box is still on
262 SWFOutputState states[64];
270 char* searchFont(char*name);
271 char* substituteFont(GfxFont*gfxFont, char*oldname);
272 char* writeEmbeddedFontToFile(XRef*ref, GfxFont*font);
274 int textmodeinfo; // did we write "Text will be rendered as polygon" yet?
275 int jpeginfo; // did we write "File contains jpegs" yet?
276 int pbminfo; // did we write "File contains jpegs" yet?
277 int linkinfo; // did we write "File contains links" yet?
278 int ttfinfo; // did we write "File contains TrueType Fonts" yet?
279 int gradientinfo; // did we write "File contains Gradients yet?
281 int type3active; // are we between beginType3()/endType3()?
287 char* substitutetarget[256];
288 char* substitutesource[256];
291 int user_movex,user_movey;
292 int user_clipx1,user_clipx2,user_clipy1,user_clipy2;
294 gfxline_t* current_text_stroke;
295 gfxline_t* current_text_clip;
296 char* current_font_id;
297 gfxfont_t* current_gfxfont;
298 gfxmatrix_t current_font_matrix;
300 fontlist_t* fontlist;
306 friend void swf_output_preparepage(swf_output_t*swf, int pdfpage, int outputpage);
309 typedef struct _drawnchar
327 chars = (drawnchar_t*)malloc(sizeof(drawnchar_t)*buf_size);
328 memset(chars, 0, sizeof(drawnchar_t)*buf_size);
333 free(chars);chars = 0;
340 chars = (drawnchar_t*)realloc(chars, sizeof(drawnchar_t)*buf_size);
344 void addChar(int charid, gfxcoord_t x, gfxcoord_t y, gfxcolor_t color)
347 chars[num_chars].x = x;
348 chars[num_chars].y = y;
349 chars[num_chars].color = color;
350 chars[num_chars].charid = charid;
354 static char*getFontID(GfxFont*font);
362 class InfoOutputDev: public OutputDev
365 FontInfo* currentfont;
377 id2font = new GHash();
379 virtual ~InfoOutputDev()
383 virtual GBool upsideDown() {return gTrue;}
384 virtual GBool useDrawChar() {return gTrue;}
385 virtual GBool useGradients() {return gTrue;}
386 virtual GBool interpretType3Chars() {return gTrue;}
387 virtual void startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
390 state->transform(crop_x1,crop_y1,&x1,&y1);
391 state->transform(crop_x2,crop_y2,&x2,&y2);
392 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
393 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
399 virtual void drawLink(Link *link, Catalog *catalog)
403 virtual double getMaximumFontSize(char*id)
405 FontInfo*info = (FontInfo*)id2font->lookup(id);
407 msg("<error> Unknown font id: %s", id);
410 return info->max_size;
413 virtual void updateFont(GfxState *state)
415 GfxFont*font = state->getFont();
418 char*id = getFontID(font);
420 FontInfo*info = (FontInfo*)id2font->lookup(id);
422 GString* idStr = new GString(id);
426 id2font->add(idStr, (void*)info);
432 virtual void drawChar(GfxState *state, double x, double y,
433 double dx, double dy,
434 double originX, double originY,
435 CharCode code, Unicode *u, int uLen)
437 int render = state->getRender();
440 double m11,m21,m12,m22;
441 state->getFontTransMat(&m11, &m12, &m21, &m22);
442 m11 *= state->getHorizScaling();
443 m21 *= state->getHorizScaling();
444 double lenx = sqrt(m11*m11 + m12*m12);
445 double leny = sqrt(m21*m21 + m22*m22);
446 double len = lenx>leny?lenx:leny;
447 if(currentfont && currentfont->max_size < len) {
448 currentfont->max_size = len;
451 virtual void drawImageMask(GfxState *state, Object *ref, Stream *str,
452 int width, int height, GBool invert,
457 virtual void drawImage(GfxState *state, Object *ref, Stream *str,
458 int width, int height, GfxImageColorMap *colorMap,
459 int *maskColors, GBool inlineImg)
465 SWFOutputDev::SWFOutputDev()
483 current_text_stroke = 0;
484 current_text_clip = 0;
491 output = (gfxdevice_t*)malloc(sizeof(gfxdevice_t));
492 gfxdevice_swf_init(output);
493 /* configure device */
494 parameter_t*p = device_config;
496 output->setparameter(output, p->name, p->value);
501 void SWFOutputDev::setMove(int x,int y)
503 this->user_movex = x;
504 this->user_movey = y;
507 void SWFOutputDev::setClip(int x1,int y1,int x2,int y2)
509 if(x2<x1) {int x3=x1;x1=x2;x2=x3;}
510 if(y2<y1) {int y3=y1;y1=y2;y2=y3;}
512 this->user_clipx1 = x1;
513 this->user_clipy1 = y1;
514 this->user_clipx2 = x2;
515 this->user_clipy2 = y2;
518 static char*getFontID(GfxFont*font)
520 GString*gstr = font->getName();
521 char* fontname = gstr==0?0:gstr->getCString();
525 sprintf(buf, "UFONT%d", r->num);
528 return strdup(fontname);
531 static char*getFontName(GfxFont*font)
533 char*fontid = getFontID(font);
535 char* plus = strchr(fontid, '+');
536 if(plus && plus < &fontid[strlen(fontid)-1]) {
537 fontname = strdup(plus+1);
539 fontname = strdup(fontid);
545 static char mybuf[1024];
546 static char* gfxstate2str(GfxState *state)
550 bufpos+=sprintf(bufpos,"CTM[%.3f/%.3f/%.3f/%.3f/%.3f/%.3f] ",
557 if(state->getX1()!=0.0)
558 bufpos+=sprintf(bufpos,"X1-%.1f ",state->getX1());
559 if(state->getY1()!=0.0)
560 bufpos+=sprintf(bufpos,"Y1-%.1f ",state->getY1());
561 bufpos+=sprintf(bufpos,"X2-%.1f ",state->getX2());
562 bufpos+=sprintf(bufpos,"Y2-%.1f ",state->getY2());
563 bufpos+=sprintf(bufpos,"PW%.1f ",state->getPageWidth());
564 bufpos+=sprintf(bufpos,"PH%.1f ",state->getPageHeight());
565 /*bufpos+=sprintf(bufpos,"FC[%.1f/%.1f] ",
566 state->getFillColor()->c[0], state->getFillColor()->c[1]);
567 bufpos+=sprintf(bufpos,"SC[%.1f/%.1f] ",
568 state->getStrokeColor()->c[0], state->getFillColor()->c[1]);*/
569 /* bufpos+=sprintf(bufpos,"FC[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f]",
570 state->getFillColor()->c[0], state->getFillColor()->c[1],
571 state->getFillColor()->c[2], state->getFillColor()->c[3],
572 state->getFillColor()->c[4], state->getFillColor()->c[5],
573 state->getFillColor()->c[6], state->getFillColor()->c[7]);
574 bufpos+=sprintf(bufpos,"SC[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f]",
575 state->getStrokeColor()->c[0], state->getFillColor()->c[1],
576 state->getStrokeColor()->c[2], state->getFillColor()->c[3],
577 state->getStrokeColor()->c[4], state->getFillColor()->c[5],
578 state->getStrokeColor()->c[6], state->getFillColor()->c[7]);*/
579 state->getFillRGB(&rgb);
580 if(rgb.r || rgb.g || rgb.b)
581 bufpos+=sprintf(bufpos,"FR[%.1f/%.1f/%.1f] ", rgb.r,rgb.g,rgb.b);
582 state->getStrokeRGB(&rgb);
583 if(rgb.r || rgb.g || rgb.b)
584 bufpos+=sprintf(bufpos,"SR[%.1f/%.1f/%.1f] ", rgb.r,rgb.g,rgb.b);
585 if(state->getFillColorSpace()->getNComps()>1)
586 bufpos+=sprintf(bufpos,"CS[[%d]] ",state->getFillColorSpace()->getNComps());
587 if(state->getStrokeColorSpace()->getNComps()>1)
588 bufpos+=sprintf(bufpos,"SS[[%d]] ",state->getStrokeColorSpace()->getNComps());
589 if(state->getFillPattern())
590 bufpos+=sprintf(bufpos,"FP%08x ", state->getFillPattern());
591 if(state->getStrokePattern())
592 bufpos+=sprintf(bufpos,"SP%08x ", state->getStrokePattern());
594 if(state->getFillOpacity()!=1.0)
595 bufpos+=sprintf(bufpos,"FO%.1f ", state->getFillOpacity());
596 if(state->getStrokeOpacity()!=1.0)
597 bufpos+=sprintf(bufpos,"SO%.1f ", state->getStrokeOpacity());
599 bufpos+=sprintf(bufpos,"LW%.1f ", state->getLineWidth());
604 state->getLineDash(&dash, &length, &start);
608 bufpos+=sprintf(bufpos,"DASH%.1f[",start);
609 for(t=0;t<length;t++) {
610 bufpos+=sprintf(bufpos,"D%.1f",dash[t]);
612 bufpos+=sprintf(bufpos,"]");
615 if(state->getFlatness()!=1)
616 bufpos+=sprintf(bufpos,"F%d ", state->getFlatness());
617 if(state->getLineJoin()!=0)
618 bufpos+=sprintf(bufpos,"J%d ", state->getLineJoin());
619 if(state->getLineJoin()!=0)
620 bufpos+=sprintf(bufpos,"C%d ", state->getLineCap());
621 if(state->getLineJoin()!=0)
622 bufpos+=sprintf(bufpos,"ML%d ", state->getMiterLimit());
624 if(state->getFont() && getFontID(state->getFont()))
625 bufpos+=sprintf(bufpos,"F\"%s\" ",getFontID(state->getFont()));
626 bufpos+=sprintf(bufpos,"FS%.1f ", state->getFontSize());
627 bufpos+=sprintf(bufpos,"MAT[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f] ", state->getTextMat()[0],state->getTextMat()[1],state->getTextMat()[2],
628 state->getTextMat()[3],state->getTextMat()[4],state->getTextMat()[5]);
629 if(state->getCharSpace())
630 bufpos+=sprintf(bufpos,"CS%.5f ", state->getCharSpace());
631 if(state->getWordSpace())
632 bufpos+=sprintf(bufpos,"WS%.5f ", state->getWordSpace());
633 if(state->getHorizScaling()!=1.0)
634 bufpos+=sprintf(bufpos,"SC%.1f ", state->getHorizScaling());
635 if(state->getLeading())
636 bufpos+=sprintf(bufpos,"L%.1f ", state->getLeading());
638 bufpos+=sprintf(bufpos,"R%.1f ", state->getRise());
639 if(state->getRender())
640 bufpos+=sprintf(bufpos,"R%d ", state->getRender());
641 bufpos+=sprintf(bufpos,"P%08x ", state->getPath());
642 bufpos+=sprintf(bufpos,"CX%.1f ", state->getCurX());
643 bufpos+=sprintf(bufpos,"CY%.1f ", state->getCurY());
644 if(state->getLineX())
645 bufpos+=sprintf(bufpos,"LX%.1f ", state->getLineX());
646 if(state->getLineY())
647 bufpos+=sprintf(bufpos,"LY%.1f ", state->getLineY());
648 bufpos+=sprintf(bufpos," ");
652 static void dumpFontInfo(char*loglevel, GfxFont*font);
653 static int lastdumps[1024];
654 static int lastdumppos = 0;
659 static void showFontError(GfxFont*font, int nr)
663 for(t=0;t<lastdumppos;t++)
664 if(lastdumps[t] == r->num)
668 if(lastdumppos<sizeof(lastdumps)/sizeof(int))
669 lastdumps[lastdumppos++] = r->num;
671 msg("<warning> The following font caused problems:");
673 msg("<warning> The following font caused problems (substituting):");
675 msg("<warning> The following Type 3 Font will be rendered as bitmap:");
676 dumpFontInfo("<warning>", font);
679 static void dumpFontInfo(char*loglevel, GfxFont*font)
681 char* id = getFontID(font);
682 char* name = getFontName(font);
683 Ref* r=font->getID();
684 msg("%s=========== %s (ID:%d,%d) ==========\n", loglevel, name, r->num,r->gen);
686 GString*gstr = font->getTag();
688 msg("%s| Tag: %s\n", loglevel, id);
690 if(font->isCIDFont()) msg("%s| is CID font\n", loglevel);
692 GfxFontType type=font->getType();
694 case fontUnknownType:
695 msg("%s| Type: unknown\n",loglevel);
698 msg("%s| Type: 1\n",loglevel);
701 msg("%s| Type: 1C\n",loglevel);
704 msg("%s| Type: 3\n",loglevel);
707 msg("%s| Type: TrueType\n",loglevel);
710 msg("%s| Type: CIDType0\n",loglevel);
713 msg("%s| Type: CIDType0C\n",loglevel);
716 msg("%s| Type: CIDType2\n",loglevel);
721 GBool embedded = font->getEmbeddedFontID(&embRef);
723 if(font->getEmbeddedFontName()) {
724 embeddedName = font->getEmbeddedFontName()->getCString();
727 msg("%s| Embedded id: %s id: %d\n",loglevel, FIXNULL(embeddedName), embRef.num);
729 gstr = font->getExtFontFile();
731 msg("%s| External Font file: %s\n", loglevel, FIXNULL(gstr->getCString()));
733 // Get font descriptor flags.
734 if(font->isFixedWidth()) msg("%s| is fixed width\n", loglevel);
735 if(font->isSerif()) msg("%s| is serif\n", loglevel);
736 if(font->isSymbolic()) msg("%s| is symbolic\n", loglevel);
737 if(font->isItalic()) msg("%s| is italic\n", loglevel);
738 if(font->isBold()) msg("%s| is bold\n", loglevel);
744 //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");}
745 //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");}
748 void dump_outline(gfxline_t*line)
751 if(line->type == gfx_moveTo) {
752 msg("<debug> | moveTo %.2f %.2f", line->x,line->y);
753 } else if(line->type == gfx_lineTo) {
754 msg("<debug> | lineTo %.2f %.2f", line->x,line->y);
755 } else if(line->type == gfx_splineTo) {
756 msg("<debug> | splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y);
762 gfxline_t* gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user_movex, int user_movey)
764 int num = path->getNumSubpaths();
767 double lastx=0,lasty=0,posx=0,posy=0;
770 msg("<warning> empty path");
774 gfxdrawer_target_gfxline(&draw);
776 for(t = 0; t < num; t++) {
777 GfxSubpath *subpath = path->getSubpath(t);
778 int subnum = subpath->getNumPoints();
779 double bx=0,by=0,cx=0,cy=0;
781 for(s=0;s<subnum;s++) {
784 state->transform(subpath->getX(s),subpath->getY(s),&x,&y);
789 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
790 draw.lineTo(&draw, lastx, lasty);
792 draw.moveTo(&draw, x,y);
797 } else if(subpath->getCurve(s) && cpos==0) {
801 } else if(subpath->getCurve(s) && cpos==1) {
809 draw.lineTo(&draw, x,y);
811 gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y, 0.05);
818 /* fix non-closed lines */
819 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
820 draw.lineTo(&draw, lastx, lasty);
822 gfxline_t*result = (gfxline_t*)draw.result(&draw);
826 /*----------------------------------------------------------------------------
827 * Primitive Graphic routines
828 *----------------------------------------------------------------------------*/
830 void SWFOutputDev::stroke(GfxState *state)
832 GfxPath * path = state->getPath();
833 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex, user_movey);
834 strokeGfxline(state, line);
838 void SWFOutputDev::strokeGfxline(GfxState *state, gfxline_t*line)
840 int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
841 int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
842 double miterLimit = state->getMiterLimit();
843 double width = state->getTransformedLineWidth();
846 double opaq = state->getStrokeOpacity();
848 state->getFillRGB(&rgb);
850 state->getStrokeRGB(&rgb);
852 col.r = (unsigned char)(rgb.r*255);
853 col.g = (unsigned char)(rgb.g*255);
854 col.b = (unsigned char)(rgb.b*255);
855 col.a = (unsigned char)(opaq*255);
857 gfx_capType capType = gfx_capRound;
858 if(lineCap == 0) capType = gfx_capButt;
859 else if(lineCap == 1) capType = gfx_capRound;
860 else if(lineCap == 2) capType = gfx_capSquare;
862 gfx_joinType joinType = gfx_joinRound;
863 if(lineJoin == 0) joinType = gfx_joinMiter;
864 else if(lineJoin == 1) joinType = gfx_joinRound;
865 else if(lineJoin == 2) joinType = gfx_joinBevel;
868 double dashphase = 0;
870 state->getLineDash(&ldash, &dashnum, &dashphase);
874 if(dashnum && ldash) {
875 float * dash = (float*)malloc(sizeof(float)*(dashnum+1));
879 msg("<trace> %d dashes", dashnum);
880 msg("<trace> | phase: %f", dashphase);
881 for(t=0;t<dashnum;t++) {
883 msg("<trace> | d%-3d: %f", t, ldash[t]);
886 if(getLogLevel() >= LOGLEVEL_TRACE) {
890 line2 = gfxtool_dash_line(line, dash, dashphase);
893 msg("<trace> After dashing:");
896 if(getLogLevel() >= LOGLEVEL_TRACE) {
898 state->getStrokeGray(&gray);
899 msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x gray=%f\n",
901 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
902 lineCap==0?"butt": (lineJoin==1?"round":"square"),
904 col.r,col.g,col.b,col.a,
910 //swfoutput_drawgfxline(output, line, width, &col, capType, joinType, miterLimit);
911 output->stroke(output, line, width, &col, capType, joinType, miterLimit);
917 gfxcolor_t getFillColor(GfxState * state)
920 double opaq = state->getFillOpacity();
921 state->getFillRGB(&rgb);
923 col.r = (unsigned char)(rgb.r*255);
924 col.g = (unsigned char)(rgb.g*255);
925 col.b = (unsigned char)(rgb.b*255);
926 col.a = (unsigned char)(opaq*255);
930 void SWFOutputDev::fillGfxLine(GfxState *state, gfxline_t*line)
932 gfxcolor_t col = getFillColor(state);
934 if(getLogLevel() >= LOGLEVEL_TRACE) {
935 msg("<trace> fill %02x%02x%02x%02x\n", col.r, col.g, col.b, col.a);
938 output->fill(output, line, &col);
940 void SWFOutputDev::fill(GfxState *state)
942 GfxPath * path = state->getPath();
943 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex, user_movey);
944 fillGfxLine(state, line);
947 void SWFOutputDev::eoFill(GfxState *state)
949 GfxPath * path = state->getPath();
950 gfxcolor_t col = getFillColor(state);
952 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex, user_movey);
954 if(getLogLevel() >= LOGLEVEL_TRACE) {
955 msg("<trace> eofill\n");
959 output->fill(output, line, &col);
963 void SWFOutputDev::clip(GfxState *state)
965 GfxPath * path = state->getPath();
966 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex, user_movey);
967 clipToGfxLine(state, line);
971 void SWFOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line)
973 if(getLogLevel() >= LOGLEVEL_TRACE) {
974 msg("<trace> clip\n");
978 output->startclip(output, line);
979 states[statepos].clipping++;
981 void SWFOutputDev::eoClip(GfxState *state)
983 GfxPath * path = state->getPath();
984 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex, user_movey);
986 if(getLogLevel() >= LOGLEVEL_TRACE) {
987 msg("<trace> eoclip\n");
991 output->startclip(output, line);
992 states[statepos].clipping++;
996 void SWFOutputDev::endframe()
999 output->endclip(output);
1003 output->endpage(output);
1006 void SWFOutputDev::finish()
1008 if(outer_clip_box) {
1010 output->endclip(output);
1015 this->result = output->finish(output);
1016 free(output);output=0;
1020 int SWFOutputDev::save(char*filename)
1023 return result->save(result, filename);
1025 void* SWFOutputDev::get(char*name)
1028 return result->get(result, name);
1031 SWFOutputDev::~SWFOutputDev()
1036 this->result->destroy(this->result);
1041 free(this->pages); this->pages = 0;
1044 fontlist_t*l = this->fontlist;
1046 fontlist_t*next = l->next;
1048 gfxfont_free(l->font);
1056 GBool SWFOutputDev::upsideDown()
1060 GBool SWFOutputDev::useDrawChar()
1064 GBool SWFOutputDev::useGradients()
1068 msg("<notice> File contains gradients");
1074 char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
1075 "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
1077 #define RENDER_FILL 0
1078 #define RENDER_STROKE 1
1079 #define RENDER_FILLSTROKE 2
1080 #define RENDER_INVISIBLE 3
1081 #define RENDER_CLIP 4
1083 static char tmp_printstr[4096];
1084 char* makeStringPrintable(char*str)
1086 int len = strlen(str);
1093 for(t=0;t<len;t++) {
1098 tmp_printstr[t] = c;
1101 tmp_printstr[len++] = '.';
1102 tmp_printstr[len++] = '.';
1103 tmp_printstr[len++] = '.';
1105 tmp_printstr[len] = 0;
1106 return tmp_printstr;
1110 int getGfxCharID(gfxfont_t*font, int charnr, char *charname, int u)
1114 for(t=0;t<font->num_glyphs;t++) {
1115 if(font->glyphs[t].name && !strcmp(font->glyphs[t].name,charname)) {
1116 msg("<debug> Char [%d,>%s<,%d] maps to %d\n", charnr, charname, u, t);
1120 /* if we didn't find the character, maybe
1121 we can find the capitalized version */
1122 for(t=0;t<font->num_glyphs;t++) {
1123 if(font->glyphs[t].name && !strcasecmp(font->glyphs[t].name,charname)) {
1124 msg("<debug> Char [%d,>>%s<<,%d] maps to %d\n", charnr, charname, u, t);
1130 /* try to use the unicode id */
1131 if(u>=0 && u<font->max_unicode && font->unicode2glyph[u]>=0) {
1132 msg("<debug> Char [%d,%s,>%d<] maps to %d\n", charnr, charname, u, font->unicode2glyph[u]);
1133 return font->unicode2glyph[u];
1136 /* we don't need to "draw" space characters, so don't overdo the search
1137 for a matching glyph */
1138 if(charname && !strcasecmp(charname, "space"))
1141 if(charnr>=0 && charnr<font->num_glyphs) {
1142 msg("<debug> Char [>%d<,%s,%d] maps to %d\n", charnr, charname, u, charnr);
1150 void SWFOutputDev::beginString(GfxState *state, GString *s)
1152 int render = state->getRender();
1153 if(current_text_stroke) {
1154 msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
1157 msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
1158 double m11,m21,m12,m22;
1159 // msg("<debug> %s beginstring \"%s\"\n", gfxstate2str(state), s->getCString());
1160 state->getFontTransMat(&m11, &m12, &m21, &m22);
1161 m11 *= state->getHorizScaling();
1162 m21 *= state->getHorizScaling();
1164 this->current_font_matrix.m00 = m11 / 1024.0;
1165 this->current_font_matrix.m01 = m12 / 1024.0;
1166 this->current_font_matrix.m10 = -m21 / 1024.0;
1167 this->current_font_matrix.m11 = -m22 / 1024.0;
1168 this->current_font_matrix.tx = 0;
1169 this->current_font_matrix.ty = 0;
1171 gfxmatrix_t m = this->current_font_matrix;
1173 /*if(render != 3 && render != 0)
1174 msg("<warning> Text rendering mode %d (%s) not fully supported yet (for text \"%s\")", render, renderModeDesc[render&7], makeStringPrintable(s->getCString()));*/
1175 states[statepos].textRender = render;
1178 void SWFOutputDev::drawChar(GfxState *state, double x, double y,
1179 double dx, double dy,
1180 double originX, double originY,
1181 CharCode c, Unicode *_u, int uLen)
1183 int render = state->getRender();
1184 // check for invisible text -- this is used by Acrobat Capture
1186 msg("<debug> Ignoring invisible text: char %d at %f,%f", c, x, y);
1190 if(states[statepos].textRender != render)
1191 msg("<error> Internal error: drawChar.render!=beginString.render");
1193 gfxcolor_t col = getFillColor(state);
1195 Gushort *CIDToGIDMap = 0;
1196 GfxFont*font = state->getFont();
1198 if(font->getType() == fontType3) {
1199 /* type 3 chars are passed as graphics */
1200 msg("<debug> type3 char at %f/%f", x, y);
1211 /* find out char name from unicode index
1212 TODO: should be precomputed
1214 for(t=0;t<sizeof(nameToUnicodeTab)/sizeof(nameToUnicodeTab[0]);t++) {
1215 if(nameToUnicodeTab[t].u == u) {
1216 name = nameToUnicodeTab[t].name;
1223 if(font->isCIDFont()) {
1224 GfxCIDFont*cfont = (GfxCIDFont*)font;
1226 if(font->getType() == fontCIDType2)
1227 CIDToGIDMap = cfont->getCIDToGID();
1230 font8 = (Gfx8BitFont*)font;
1231 char**enc=font8->getEncoding();
1232 if(enc && enc[c] && strcasecmp(enc[c], "space")) {
1237 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);
1240 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);
1246 charid = getGfxCharID(current_gfxfont, c, name, u);
1248 charid = getGfxCharID(current_gfxfont, c, 0, -1);
1250 /* multiple unicodes- should usually map to a ligature.
1251 if the ligature doesn't exist, we need to draw
1252 the characters one-by-one. */
1254 msg("<warning> ligature %d missing in font %s\n", c, current_font_id);
1255 for(t=0;t<uLen;t++) {
1256 drawChar(state, x, y, dx, dy, originX, originY, c, _u+t, 1);
1262 if(charid<0 && name) {
1263 if(strcasecmp(name, "space")) {
1264 msg("<warning> Didn't find character '%s' (c=%d,u=%d) in current charset (%s, %d characters)",
1265 FIXNULL(name),c, u, FIXNULL((char*)current_font_id), current_gfxfont->num_glyphs);
1270 gfxmatrix_t m = this->current_font_matrix;
1271 state->transform(x, y, &m.tx, &m.ty);
1275 if(render == RENDER_FILL) {
1276 output->drawchar(output, current_font_id, charid, &col, &m);
1278 msg("<debug> Drawing glyph %d as shape", charid);
1280 msg("<notice> Some texts will be rendered as shape");
1283 gfxline_t*glyph = current_gfxfont->glyphs[charid].line;
1284 gfxline_t*tglyph = gfxline_clone(glyph);
1285 gfxline_transform(tglyph, &m);
1286 if((render&3) != RENDER_INVISIBLE) {
1287 gfxline_t*add = gfxline_clone(tglyph);
1288 current_text_stroke = gfxline_append(current_text_stroke, add);
1290 if(render&RENDER_CLIP) {
1291 gfxline_t*add = gfxline_clone(tglyph);
1292 current_text_clip = gfxline_append(current_text_clip, add);
1294 gfxline_free(tglyph);
1298 void SWFOutputDev::endString(GfxState *state)
1300 int render = state->getRender();
1301 msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
1302 if(states[statepos].textRender != render)
1303 msg("<error> Internal error: drawChar.render!=beginString.render");
1305 if(current_text_stroke) {
1306 /* fillstroke and stroke text rendering objects we can process right
1307 now (as there may be texts of other rendering modes in this
1308 text object)- clipping objects have to wait until endTextObject,
1310 if((render&3) == RENDER_FILL) {
1311 fillGfxLine(state, current_text_stroke);
1312 gfxline_free(current_text_stroke);
1313 current_text_stroke = 0;
1314 } else if((render&3) == RENDER_FILLSTROKE) {
1315 fillGfxLine(state, current_text_stroke);
1316 strokeGfxline(state, current_text_stroke);
1317 gfxline_free(current_text_stroke);
1318 current_text_stroke = 0;
1319 } else if((render&3) == RENDER_STROKE) {
1320 strokeGfxline(state, current_text_stroke);
1321 gfxline_free(current_text_stroke);
1322 current_text_stroke = 0;
1327 void SWFOutputDev::endTextObject(GfxState *state)
1329 int render = state->getRender();
1330 msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1331 if(states[statepos].textRender != render)
1332 msg("<error> Internal error: drawChar.render!=beginString.render");
1334 if(current_text_clip) {
1335 clipToGfxLine(state, current_text_clip);
1336 gfxline_free(current_text_clip);
1337 current_text_clip = 0;
1341 /* the logic seems to be as following:
1342 first, beginType3Char is called, with the charcode and the coordinates.
1343 if this function returns true, it already knew about the char and has now drawn it.
1344 if the function returns false, it's a new char, and type3D1 is called with some parameters-
1345 the all draw operations until endType3Char are part of the char (which in this moment is
1346 at the position first passed to beginType3Char). the char ends with endType3Char.
1348 The drawing operations between beginType3Char and endType3Char are somewhat different to
1349 the normal ones. For example, the fillcolor equals the stroke color.
1352 GBool SWFOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode code, Unicode *u, int uLen)
1354 msg("<debug> beginType3Char %d, %08x, %d", code, *u, uLen);
1356 /* the character itself is going to be passed using the draw functions */
1357 return gFalse; /* gTrue= is_in_cache? */
1360 void SWFOutputDev::type3D0(GfxState *state, double wx, double wy) {
1361 msg("<debug> type3D0 width=%f height=%f", wx, wy);
1363 void SWFOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1364 msg("<debug> type3D1 width=%f height=%f bbox=(%f,%f,%f,%f)", wx, wy,
1368 void SWFOutputDev::endType3Char(GfxState *state)
1371 msg("<debug> endType3Char");
1374 void SWFOutputDev::startFrame(int width, int height)
1376 output->startpage(output, width, height);
1379 void SWFOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
1381 this->currentpage = pageNum;
1383 int rot = doc->getPageRotate(1);
1386 gfxline_t clippath[5];
1388 white.r = white.g = white.b = white.a = 255;
1390 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1391 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1392 Use CropBox, not MediaBox, as page size
1399 state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1400 state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1402 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1403 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1406 /* apply user clip box */
1407 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1408 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1409 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1410 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1411 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1414 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1416 if(outer_clip_box) {
1417 output->endclip(output);
1421 msg("<notice> processing PDF page %d (%dx%d:%d:%d) (move:%d:%d)", pageNum, (int)x2-(int)x1,(int)y2-(int)y1, (int)x1, (int)y1, user_movex, user_movey);
1423 msg("<verbose> page is rotated %d degrees\n", rot);
1425 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1426 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1427 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1428 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1429 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1430 output->startclip(output, clippath); outer_clip_box = 1;
1431 output->fill(output, clippath, &white);
1434 void SWFOutputDev::drawLink(Link *link, Catalog *catalog)
1436 msg("<debug> drawlink\n");
1437 double x1, y1, x2, y2, w;
1439 gfxline_t points[5];
1443 link->getBorder(&x1, &y1, &x2, &y2, &w);
1445 link->getRect(&x1, &y1, &x2, &y2);
1450 cvtUserToDev(x1, y1, &x, &y);
1451 points[0].type = gfx_moveTo;
1452 points[0].x = points[4].x = x + user_movex;
1453 points[0].y = points[4].y = y + user_movey;
1454 points[0].next = &points[1];
1455 cvtUserToDev(x2, y1, &x, &y);
1456 points[1].type = gfx_lineTo;
1457 points[1].x = x + user_movex;
1458 points[1].y = y + user_movey;
1459 points[1].next = &points[2];
1460 cvtUserToDev(x2, y2, &x, &y);
1461 points[2].type = gfx_lineTo;
1462 points[2].x = x + user_movex;
1463 points[2].y = y + user_movey;
1464 points[2].next = &points[3];
1465 cvtUserToDev(x1, y2, &x, &y);
1466 points[3].type = gfx_lineTo;
1467 points[3].x = x + user_movex;
1468 points[3].y = y + user_movey;
1469 points[3].next = &points[4];
1470 cvtUserToDev(x1, y1, &x, &y);
1471 points[4].type = gfx_lineTo;
1472 points[4].x = x + user_movex;
1473 points[4].y = y + user_movey;
1476 LinkAction*action=link->getAction();
1483 switch(action->getKind())
1487 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1488 LinkDest *dest=NULL;
1489 if (ha->getDest()==NULL)
1490 dest=catalog->findDest(ha->getNamedDest());
1491 else dest=ha->getDest();
1493 if (dest->isPageRef()){
1494 Ref pageref=dest->getPageRef();
1495 page=catalog->findPage(pageref.num,pageref.gen);
1497 else page=dest->getPageNum();
1498 sprintf(buf, "%d", page);
1505 LinkGoToR*l = (LinkGoToR*)action;
1506 GString*g = l->getNamedDest();
1508 s = strdup(g->getCString());
1513 LinkNamed*l = (LinkNamed*)action;
1514 GString*name = l->getName();
1516 s = strdup(name->lowerCase()->getCString());
1517 named = name->getCString();
1520 if(strstr(s, "next") || strstr(s, "forward"))
1522 page = currentpage + 1;
1524 else if(strstr(s, "prev") || strstr(s, "back"))
1526 page = currentpage - 1;
1528 else if(strstr(s, "last") || strstr(s, "end"))
1530 if(pages && pagepos>0)
1531 page = pages[pagepos-1];
1533 else if(strstr(s, "first") || strstr(s, "top"))
1541 case actionLaunch: {
1543 LinkLaunch*l = (LinkLaunch*)action;
1544 GString * str = new GString(l->getFileName());
1545 str->append(l->getParams());
1546 s = strdup(str->getCString());
1552 LinkURI*l = (LinkURI*)action;
1553 GString*g = l->getURI();
1555 url = g->getCString();
1560 case actionUnknown: {
1562 LinkUnknown*l = (LinkUnknown*)action;
1567 msg("<error> Unknown link type!\n");
1571 if(!s) s = strdup("-?-");
1573 if(!linkinfo && (page || url))
1575 msg("<notice> File contains links");
1583 for(t=1;t<=pagepos;t++) {
1584 if(pages[t]==page) {
1591 sprintf(buf, "page%d", t);
1592 output->drawlink(output, points, buf);
1594 msg("<warning> Invalid link to page %d", page);
1599 output->drawlink(output, points, url);
1602 msg("<verbose> \"%s\" link to \"%s\" (%d)\n", type, FIXNULL(s), page);
1606 void SWFOutputDev::saveState(GfxState *state) {
1607 msg("<trace> saveState\n");
1610 msg("<error> Too many nested states in pdf.");
1614 states[statepos].clipping = 0; //? shouldn't this be the current value?
1615 states[statepos].textRender = states[statepos-1].textRender;
1618 void SWFOutputDev::restoreState(GfxState *state) {
1619 msg("<trace> restoreState\n");
1621 while(states[statepos].clipping) {
1622 output->endclip(output);
1623 states[statepos].clipping--;
1628 char* SWFOutputDev::searchFont(char*name)
1632 int is_standard_font = 0;
1634 msg("<verbose> SearchFont(%s)", name);
1636 /* see if it is a pdf standard font */
1637 for(i=0;i<sizeof(pdf2t1map)/sizeof(mapping);i++)
1639 if(!strcmp(name, pdf2t1map[i].pdffont))
1641 name = pdf2t1map[i].filename;
1642 is_standard_font = 1;
1646 /* look in all font files */
1647 for(i=0;i<fontnum;i++)
1649 if(strstr(fonts[i].filename, name))
1651 if(!fonts[i].used) {
1654 if(!is_standard_font)
1655 msg("<notice> Using %s for %s", fonts[i].filename, name);
1657 return strdup(fonts[i].filename);
1663 void SWFOutputDev::updateLineWidth(GfxState *state)
1665 double width = state->getTransformedLineWidth();
1666 //swfoutput_setlinewidth(&output, width);
1669 void SWFOutputDev::updateLineCap(GfxState *state)
1671 int c = state->getLineCap();
1674 void SWFOutputDev::updateLineJoin(GfxState *state)
1676 int j = state->getLineJoin();
1679 void SWFOutputDev::updateFillColor(GfxState *state)
1682 double opaq = state->getFillOpacity();
1683 state->getFillRGB(&rgb);
1685 //swfoutput_setfillcolor(&output, (char)(rgb.r*255), (char)(rgb.g*255), (char)(rgb.b*255), (char)(opaq*255));
1688 void SWFOutputDev::updateStrokeColor(GfxState *state)
1691 double opaq = state->getStrokeOpacity();
1692 state->getStrokeRGB(&rgb);
1693 //swfoutput_setstrokecolor(&output, (char)(rgb.r*255), (char)(rgb.g*255), (char)(rgb.b*255), (char)(opaq*255));
1696 void FoFiWrite(void *stream, char *data, int len)
1698 fwrite(data, len, 1, (FILE*)stream);
1701 char*SWFOutputDev::writeEmbeddedFontToFile(XRef*ref, GfxFont*font)
1703 char*tmpFileName = NULL;
1709 Object refObj, strObj;
1711 tmpFileName = mktmpname(namebuf);
1714 ret = font->getEmbeddedFontID(&embRef);
1716 msg("<verbose> Didn't get embedded font id");
1717 /* not embedded- the caller should now search the font
1718 directories for this font */
1722 f = fopen(tmpFileName, "wb");
1724 msg("<error> Couldn't create temporary Type 1 font file");
1728 /*if(font->isCIDFont()) {
1729 GfxCIDFont* cidFont = (GfxCIDFont *)font;
1730 GString c = cidFont->getCollection();
1731 msg("<notice> Collection: %s", c.getCString());
1734 //if (font->getType() == fontType1C) {
1735 if (0) { //font->getType() == fontType1C) {
1736 if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
1738 msg("<error> Couldn't read embedded font file");
1742 Type1CFontFile *cvt = new Type1CFontFile(fontBuf, fontLen);
1744 cvt->convertToType1(f);
1746 FoFiType1C *cvt = FoFiType1C::make(fontBuf, fontLen);
1748 cvt->convertToType1(NULL, gTrue, FoFiWrite, f);
1750 //cvt->convertToCIDType0("test", f);
1751 //cvt->convertToType0("test", f);
1754 } else if(font->getType() == fontTrueType) {
1755 msg("<verbose> writing font using TrueTypeFontFile::writeTTF");
1756 if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
1758 msg("<error> Couldn't read embedded font file");
1762 TrueTypeFontFile *cvt = new TrueTypeFontFile(fontBuf, fontLen);
1765 FoFiTrueType *cvt = FoFiTrueType::make(fontBuf, fontLen);
1766 cvt->writeTTF(FoFiWrite, f);
1771 font->getEmbeddedFontID(&embRef);
1772 refObj.initRef(embRef.num, embRef.gen);
1773 refObj.fetch(ref, &strObj);
1775 strObj.streamReset();
1780 f4[t] = strObj.streamGetChar();
1781 f4c[t] = (char)f4[t];
1786 if(!strncmp(f4c, "true", 4)) {
1787 /* some weird TTF fonts don't start with 0,1,0,0 but with "true".
1788 Change this on the fly */
1789 f4[0] = f4[2] = f4[3] = 0;
1797 while ((c = strObj.streamGetChar()) != EOF) {
1801 strObj.streamClose();
1806 return strdup(tmpFileName);
1809 char* searchForSuitableFont(GfxFont*gfxFont)
1811 char*name = getFontName(gfxFont);
1815 if(!config_use_fontconfig)
1818 #ifdef HAVE_FONTCONFIG
1819 FcPattern *pattern, *match;
1823 static int fcinitcalled = false;
1825 msg("<debug> searchForSuitableFont(%s)", name);
1827 // call init ony once
1828 if (!fcinitcalled) {
1829 msg("<debug> Initializing FontConfig...");
1830 fcinitcalled = true;
1832 msg("<debug> FontConfig Initialization failed. Disabling.");
1833 config_use_fontconfig = 0;
1836 msg("<debug> ...initialized FontConfig");
1839 msg("<debug> FontConfig: Create \"%s\" Family Pattern", name);
1840 pattern = FcPatternBuild(NULL, FC_FAMILY, FcTypeString, name, NULL);
1841 if (gfxFont->isItalic()) // check for italic
1842 msg("<debug> FontConfig: Adding Italic Slant");
1843 FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
1844 if (gfxFont->isBold()) // check for bold
1845 msg("<debug> FontConfig: Adding Bold Weight");
1846 FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
1848 msg("<debug> FontConfig: Try to match...");
1849 // configure and match using the original font name
1850 FcConfigSubstitute(0, pattern, FcMatchPattern);
1851 FcDefaultSubstitute(pattern);
1852 match = FcFontMatch(0, pattern, &result);
1854 if (FcPatternGetString(match, "family", 0, &v) == FcResultMatch) {
1855 msg("<debug> FontConfig: family=%s", (char*)v);
1856 // if we get an exact match
1857 if (strcmp((char *)v, name) == 0) {
1858 if (FcPatternGetString(match, "file", 0, &v) == FcResultMatch) {
1859 filename = strdup((char*)v); // mem leak
1860 char *nfn = strrchr(filename, '/');
1861 if(nfn) fontname = strdup(nfn+1);
1862 else fontname = filename;
1864 msg("<debug> FontConfig: Returning \"%s\"", fontname);
1866 // initialize patterns
1867 FcPatternDestroy(pattern);
1868 FcPatternDestroy(match);
1870 // now match against serif etc.
1871 if (gfxFont->isSerif()) {
1872 msg("<debug> FontConfig: Create Serif Family Pattern");
1873 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "serif", NULL);
1874 } else if (gfxFont->isFixedWidth()) {
1875 msg("<debug> FontConfig: Create Monospace Family Pattern");
1876 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "monospace", NULL);
1878 msg("<debug> FontConfig: Create Sans Family Pattern");
1879 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "sans", NULL);
1883 if (gfxFont->isItalic()) {
1884 msg("<debug> FontConfig: Adding Italic Slant");
1885 int bb = FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
1888 if (gfxFont->isBold()) {
1889 msg("<debug> FontConfig: Adding Bold Weight");
1890 int bb = FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
1893 msg("<debug> FontConfig: Try to match... (2)");
1894 // configure and match using serif etc
1895 FcConfigSubstitute (0, pattern, FcMatchPattern);
1896 FcDefaultSubstitute (pattern);
1897 match = FcFontMatch (0, pattern, &result);
1899 if (FcPatternGetString(match, "file", 0, &v) == FcResultMatch) {
1900 filename = strdup((char*)v); // mem leak
1901 char *nfn = strrchr(filename, '/');
1902 if(nfn) fontname = strdup(nfn+1);
1903 else fontname = filename;
1905 msg("<debug> FontConfig: Returning \"%s\"", fontname);
1909 //printf("FONTCONFIG: pattern");
1910 //FcPatternPrint(pattern);
1911 //printf("FONTCONFIG: match");
1912 //FcPatternPrint(match);
1914 FcPatternDestroy(pattern);
1915 FcPatternDestroy(match);
1917 pdfswf_addfont(filename);
1924 char* SWFOutputDev::substituteFont(GfxFont*gfxFont, char* oldname)
1926 char*fontname = 0, *filename = 0;
1927 msg("<notice> substituteFont(%s)", oldname);
1929 if(!(fontname = searchForSuitableFont(gfxFont))) {
1930 fontname = "Times-Roman";
1932 filename = searchFont(fontname);
1934 msg("<error> Couldn't find font %s- did you install the default fonts?", fontname);
1938 if(substitutepos>=sizeof(substitutesource)/sizeof(char*)) {
1939 msg("<fatal> Too many fonts in file.");
1943 substitutesource[substitutepos] = strdup(oldname); //mem leak
1944 substitutetarget[substitutepos] = fontname;
1945 msg("<notice> substituting %s -> %s", FIXNULL(oldname), FIXNULL(fontname));
1948 return strdup(filename); //mem leak
1951 void unlinkfont(char* filename)
1958 if(!strncmp(&filename[l-4],".afm",4)) {
1959 memcpy(&filename[l-4],".pfb",4);
1961 memcpy(&filename[l-4],".pfa",4);
1963 memcpy(&filename[l-4],".afm",4);
1966 if(!strncmp(&filename[l-4],".pfa",4)) {
1967 memcpy(&filename[l-4],".afm",4);
1969 memcpy(&filename[l-4],".pfa",4);
1972 if(!strncmp(&filename[l-4],".pfb",4)) {
1973 memcpy(&filename[l-4],".afm",4);
1975 memcpy(&filename[l-4],".pfb",4);
1980 void SWFOutputDev::setXRef(PDFDoc*doc, XRef *xref)
1986 int SWFOutputDev::setGfxFont(char*id, char*filename, double maxSize)
1989 fontlist_t*last=0,*l = this->fontlist;
1991 /* TODO: should this be part of the state? */
1994 if(!strcmp(l->id, id)) {
1995 current_font_id = l->id;
1996 current_gfxfont = l->font;
1998 output->addfont(output, id, current_gfxfont);
2003 if(!filename) return 0;
2005 /* A font size of e.g. 9 means the font will be scaled down by
2006 1024 and scaled up by 9. So to have a maximum error of 1/20px,
2007 we have to divide 0.05 by (fontsize/1024)
2009 double quality = (1024 * 0.05) / maxSize;
2011 font = gfxfont_load(filename, quality);
2014 l->filename = strdup(filename);
2017 current_font_id = l->id;
2018 current_gfxfont = l->font;
2024 output->addfont(output, id, current_gfxfont);
2028 void SWFOutputDev::updateFont(GfxState *state)
2030 GfxFont*gfxFont = state->getFont();
2035 char * fontid = getFontID(gfxFont);
2036 double maxSize = 1.0;
2039 maxSize = this->info->getMaximumFontSize(fontid);
2043 /* first, look if we substituted this font before-
2044 this way, we don't initialize the T1 Fonts
2046 for(t=0;t<substitutepos;t++) {
2047 if(!strcmp(fontid, substitutesource[t])) {
2048 free(fontid);fontid=0;
2049 fontid = strdup(substitutetarget[t]);
2054 /* second, see if this is a font which was used before-
2055 if so, we are done */
2056 if(setGfxFont(fontid, 0, 0)) {
2060 /* if(swfoutput_queryfont(&output, fontid))
2061 swfoutput_setfont(&output, fontid, 0);
2063 msg("<debug> updateFont(%s) [cached]", fontid);
2067 // look for Type 3 font
2068 if (gfxFont->getType() == fontType3) {
2070 type3Warning = gTrue;
2071 showFontError(gfxFont, 2);
2077 /* now either load the font, or find a substitution */
2080 GBool embedded = gfxFont->getEmbeddedFontID(&embRef);
2085 (gfxFont->getType() == fontType1 ||
2086 gfxFont->getType() == fontType1C ||
2087 (gfxFont->getType() == fontCIDType0C && forceType0Fonts) ||
2088 gfxFont->getType() == fontTrueType ||
2089 gfxFont->getType() == fontCIDType2
2092 fileName = writeEmbeddedFontToFile(xref, gfxFont);
2093 if(!fileName) showFontError(gfxFont,0);
2096 char * fontname = getFontName(gfxFont);
2097 fileName = searchFont(fontname);
2098 if(!fileName) showFontError(gfxFont,0);
2102 char * fontname = getFontName(gfxFont);
2103 msg("<warning> Font %s %scould not be loaded.", fontname, embedded?"":"(not embedded) ");
2106 msg("<warning> Try putting a TTF version of that font (named \"%s.ttf\") into %s/swftools/fonts", fontname, lastfontdir);
2108 msg("<warning> Try specifying one or more font directories");
2110 fileName = substituteFont(gfxFont, fontid);
2111 if(fontid) { free(fontid);fontid = strdup(substitutetarget[substitutepos-1]); /*ugly hack*/};
2112 msg("<notice> Font is now %s (%s)", fontid, fileName);
2116 msg("<error> Couldn't set font %s\n", fontid);
2121 msg("<verbose> updateFont(%s) -> %s (max size: %f)", fontid, fileName, maxSize);
2122 dumpFontInfo("<verbose>", gfxFont);
2124 //swfoutput_setfont(&output, fontid, fileName);
2126 if(!setGfxFont(fontid, 0, 0)) {
2127 setGfxFont(fontid, fileName, maxSize);
2131 unlinkfont(fileName);
2137 #define SQR(x) ((x)*(x))
2139 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
2141 if((newwidth<2 || newheight<2) ||
2142 (width<=newwidth || height<=newheight))
2144 unsigned char*newdata;
2146 newdata= (unsigned char*)malloc(newwidth*newheight);
2148 double fx = (double)(width)/newwidth;
2149 double fy = (double)(height)/newheight;
2151 int blocksize = (int)(8192/(fx*fy));
2152 int r = 8192*256/palettesize;
2153 for(x=0;x<newwidth;x++) {
2154 double ex = px + fx;
2155 int fromx = (int)px;
2157 int xweight1 = (int)(((fromx+1)-px)*256);
2158 int xweight2 = (int)((ex-tox)*256);
2160 for(y=0;y<newheight;y++) {
2161 double ey = py + fy;
2162 int fromy = (int)py;
2164 int yweight1 = (int)(((fromy+1)-py)*256);
2165 int yweight2 = (int)((ey-toy)*256);
2168 for(xx=fromx;xx<=tox;xx++)
2169 for(yy=fromy;yy<=toy;yy++) {
2170 int b = 1-data[width*yy+xx];
2172 if(xx==fromx) weight = (weight*xweight1)/256;
2173 if(xx==tox) weight = (weight*xweight2)/256;
2174 if(yy==fromy) weight = (weight*yweight1)/256;
2175 if(yy==toy) weight = (weight*yweight2)/256;
2178 //if(a) a=(palettesize-1)*r/blocksize;
2179 newdata[y*newwidth+x] = (a*blocksize)/r;
2187 #define IMAGE_TYPE_JPEG 0
2188 #define IMAGE_TYPE_LOSSLESS 1
2190 static void drawimage(gfxdevice_t*dev, RGBA* data, int sizex,int sizey,
2191 double x1,double y1,
2192 double x2,double y2,
2193 double x3,double y3,
2194 double x4,double y4, int type)
2198 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
2199 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
2201 gfxline_t p1,p2,p3,p4,p5;
2202 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
2203 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
2204 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
2205 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
2206 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
2208 {p1.x = (int)(p1.x*20)/20.0;
2209 p1.y = (int)(p1.y*20)/20.0;
2210 p2.x = (int)(p2.x*20)/20.0;
2211 p2.y = (int)(p2.y*20)/20.0;
2212 p3.x = (int)(p3.x*20)/20.0;
2213 p3.y = (int)(p3.y*20)/20.0;
2214 p4.x = (int)(p4.x*20)/20.0;
2215 p4.y = (int)(p4.y*20)/20.0;
2216 p5.x = (int)(p5.x*20)/20.0;
2217 p5.y = (int)(p5.y*20)/20.0;
2224 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2225 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2230 img.data = (gfxcolor_t*)data;
2234 if(type == IMAGE_TYPE_JPEG)
2235 /* TODO: pass image_dpi to device instead */
2236 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2238 dev->fillbitmap(dev, &p1, &img, &m, 0);
2241 void drawimagejpeg(gfxdevice_t*dev, RGBA*mem, int sizex,int sizey,
2242 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
2244 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG);
2247 void drawimagelossless(gfxdevice_t*dev, RGBA*mem, int sizex,int sizey,
2248 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
2250 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS);
2254 void SWFOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2255 int width, int height, GfxImageColorMap*colorMap, GBool invert,
2256 GBool inlineImg, int mask, int*maskColors)
2261 double x1,y1,x2,y2,x3,y3,x4,y4;
2262 ImageStream *imgStr;
2269 ncomps = colorMap->getNumPixelComps();
2270 bits = colorMap->getBits();
2272 imgStr = new ImageStream(str, width, ncomps,bits);
2275 if(!width || !height || (height<=1 && width<=1))
2277 msg("<verbose> Ignoring %d by %d image", width, height);
2278 unsigned char buf[8];
2280 for (y = 0; y < height; ++y)
2281 for (x = 0; x < width; ++x) {
2282 imgStr->getPixel(buf);
2288 state->transform(0, 1, &x1, &y1); x1 += user_movex; y1+= user_movey;
2289 state->transform(0, 0, &x2, &y2); x2 += user_movex; y2+= user_movey;
2290 state->transform(1, 0, &x3, &y3); x3 += user_movex; y3+= user_movey;
2291 state->transform(1, 1, &x4, &y4); x4 += user_movex; y4+= user_movey;
2293 if(!pbminfo && !(str->getKind()==strDCT)) {
2295 msg("<notice> file contains pbm pictures %s",mask?"(masked)":"");
2299 msg("<verbose> drawing %d by %d masked picture\n", width, height);
2301 if(!jpeginfo && (str->getKind()==strDCT)) {
2302 msg("<notice> file contains jpeg pictures");
2308 unsigned char buf[8];
2310 unsigned char*pic = new unsigned char[width*height];
2313 state->getFillRGB(&rgb);
2315 memset(pal,255,sizeof(pal));
2316 pal[0].r = (int)(rgb.r*255); pal[1].r = 0;
2317 pal[0].g = (int)(rgb.g*255); pal[1].g = 0;
2318 pal[0].b = (int)(rgb.b*255); pal[1].b = 0;
2319 pal[0].a = 255; pal[1].a = 0;
2322 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2323 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2324 for (y = 0; y < height; ++y)
2325 for (x = 0; x < width; ++x)
2327 imgStr->getPixel(buf);
2330 pic[width*y+x] = buf[0];
2333 /* the size of the drawn image is added to the identifier
2334 as the same image may require different bitmaps if displayed
2335 at different sizes (due to antialiasing): */
2338 unsigned char*pic2 = 0;
2341 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2350 height = realheight;
2354 /* make a black/white palette */
2359 float r = 255/(numpalette-1);
2361 for(t=0;t<numpalette;t++) {
2362 pal[t].r = (U8)(255*rgb.r);
2363 pal[t].g = (U8)(255*rgb.g);
2364 pal[t].b = (U8)(255*rgb.b);
2365 pal[t].a = (U8)(t*r);
2369 RGBA*pic2 = new RGBA[width*height];
2370 for (y = 0; y < height; ++y) {
2371 for (x = 0; x < width; ++x) {
2372 pic2[width*y+x] = pal[pic[y*width+x]];
2375 drawimagelossless(output, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2384 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2385 RGBA*pic=new RGBA[width*height];
2386 for (y = 0; y < height; ++y) {
2387 for (x = 0; x < width; ++x) {
2388 imgStr->getPixel(pixBuf);
2389 colorMap->getRGB(pixBuf, &rgb);
2390 pic[width*y+x].r = (U8)(rgb.r * 255 + 0.5);
2391 pic[width*y+x].g = (U8)(rgb.g * 255 + 0.5);
2392 pic[width*y+x].b = (U8)(rgb.b * 255 + 0.5);
2393 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2396 if(str->getKind()==strDCT)
2397 drawimagejpeg(output, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2399 drawimagelossless(output, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2404 RGBA*pic=new RGBA[width*height];
2407 for(t=0;t<256;t++) {
2409 colorMap->getRGB(pixBuf, &rgb);
2410 /*if(maskColors && *maskColors==t) {
2411 msg("<notice> Color %d is transparent", t);
2412 if (imgData->maskColors) {
2414 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2415 if (pix[i] < imgData->maskColors[2*i] ||
2416 pix[i] > imgData->maskColors[2*i+1]) {
2431 pal[t].r = (U8)(rgb.r * 255 + 0.5);
2432 pal[t].g = (U8)(rgb.g * 255 + 0.5);
2433 pal[t].b = (U8)(rgb.b * 255 + 0.5);
2434 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2437 for (y = 0; y < height; ++y) {
2438 for (x = 0; x < width; ++x) {
2439 imgStr->getPixel(pixBuf);
2440 pic[width*y+x] = pal[pixBuf[0]];
2443 drawimagelossless(output, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2451 void SWFOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2452 int width, int height, GBool invert,
2455 if(states[statepos].textRender & 4) //clipped
2457 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2458 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0);
2461 void SWFOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2462 int width, int height, GfxImageColorMap *colorMap,
2463 int *maskColors, GBool inlineImg)
2465 if(states[statepos].textRender & 4) //clipped
2468 msg("<verbose> drawImage %dx%d, %s %s, inline=%d", width, height,
2469 colorMap?"colorMap":"no colorMap",
2470 maskColors?"maskColors":"no maskColors",
2473 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2474 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2475 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors);
2478 //SWFOutputDev*output = 0;
2480 static void printInfoString(Dict *infoDict, char *key, char *fmt) {
2485 if (infoDict->lookup(key, &obj)->isString()) {
2486 s1 = obj.getString();
2487 if ((s1->getChar(0) & 0xff) == 0xfe &&
2488 (s1->getChar(1) & 0xff) == 0xff) {
2490 for (i = 2; i < obj.getString()->getLength(); i += 2) {
2491 if (s1->getChar(i) == '\0') {
2492 s2->append(s1->getChar(i+1));
2495 s2 = new GString("<unicode>");
2499 printf(fmt, s2->getCString());
2502 printf(fmt, s1->getCString());
2508 static void printInfoDate(Dict *infoDict, char *key, char *fmt) {
2512 if (infoDict->lookup(key, &obj)->isString()) {
2513 s = obj.getString()->getCString();
2514 if (s[0] == 'D' && s[1] == ':') {
2525 void storeDeviceParameter(char*name, char*value)
2527 parameter_t*p = new parameter_t();
2528 p->name = strdup(name);
2529 p->value = strdup(value);
2531 if(device_config_next) {
2532 device_config_next->next = p;
2533 device_config_next = p;
2536 device_config_next = p;
2540 void pdfswf_setparameter(char*name, char*value)
2542 msg("<verbose> setting parameter %s to \"%s\"", name, value);
2543 if(!strcmp(name, "caplinewidth")) {
2544 caplinewidth = atof(value);
2545 } else if(!strcmp(name, "zoom")) {
2548 sprintf(buf, "%f", (double)jpeg_dpi/(double)zoom);
2549 storeDeviceParameter("jpegsubpixels", buf);
2550 sprintf(buf, "%f", (double)ppm_dpi/(double)zoom);
2551 storeDeviceParameter("ppmsubpixels", buf);
2552 } else if(!strcmp(name, "jpegdpi")) {
2554 jpeg_dpi = atoi(value);
2555 sprintf(buf, "%f", (double)jpeg_dpi/(double)zoom);
2556 storeDeviceParameter("jpegsubpixels", buf);
2557 } else if(!strcmp(name, "ppmdpi")) {
2559 ppm_dpi = atoi(value);
2560 sprintf(buf, "%f", (double)ppm_dpi/(double)zoom);
2561 storeDeviceParameter("ppmsubpixels", buf);
2562 } else if(!strcmp(name, "forceType0Fonts")) {
2563 forceType0Fonts = atoi(value);
2564 } else if(!strcmp(name, "fontdir")) {
2565 pdfswf_addfontdir(value);
2566 } else if(!strcmp(name, "languagedir")) {
2567 pdfswf_addlanguagedir(value);
2568 } else if(!strcmp(name, "fontconfig")) {
2569 config_use_fontconfig = atoi(value);
2571 storeDeviceParameter(name,value);
2574 void pdfswf_addfont(char*filename)
2577 memset(&f, 0, sizeof(fontfile_t));
2578 f.filename = filename;
2579 if(fontnum < sizeof(fonts)/sizeof(fonts[0])) {
2580 fonts[fontnum++] = f;
2582 msg("<error> Too many external fonts. Not adding font file \"%s\".", filename);
2586 static char* dirseparator()
2595 void pdfswf_addlanguagedir(char*dir)
2598 globalParams = new GlobalParams("");
2600 msg("<notice> Adding %s to language pack directories", dir);
2604 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc"));
2605 strcpy(config_file, dir);
2606 strcat(config_file, dirseparator());
2607 strcat(config_file, "add-to-xpdfrc");
2609 fi = fopen(config_file, "rb");
2611 msg("<error> Could not open %s", config_file);
2614 globalParams->parseFile(new GString(config_file), fi);
2618 void pdfswf_addfontdir(char*dirname)
2620 #ifdef HAVE_DIRENT_H
2621 msg("<notice> Adding %s to font directories", dirname);
2622 lastfontdir = strdup(dirname);
2623 DIR*dir = opendir(dirname);
2625 msg("<warning> Couldn't open directory %s\n", dirname);
2630 ent = readdir (dir);
2634 char*name = ent->d_name;
2640 if(!strncasecmp(&name[l-4], ".pfa", 4))
2642 if(!strncasecmp(&name[l-4], ".pfb", 4))
2644 if(!strncasecmp(&name[l-4], ".ttf", 4))
2648 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2649 strcpy(fontname, dirname);
2650 strcat(fontname, dirseparator());
2651 strcat(fontname, name);
2652 msg("<verbose> Adding %s to fonts", fontname);
2653 pdfswf_addfont(fontname);
2658 msg("<warning> No dirent.h- unable to add font dir %s", dir);
2663 typedef struct _pdf_doc_internal
2668 } pdf_doc_internal_t;
2669 typedef struct _pdf_page_internal
2671 } pdf_page_internal_t;
2672 typedef struct _swf_output_internal
2674 SWFOutputDev*outputDev;
2675 } swf_output_internal_t;
2677 pdf_doc_t* pdf_init(char*filename, char*userPassword)
2679 pdf_doc_t*pdf_doc = (pdf_doc_t*)malloc(sizeof(pdf_doc_t));
2680 memset(pdf_doc, 0, sizeof(pdf_doc_t));
2681 pdf_doc_internal_t*i= (pdf_doc_internal_t*)malloc(sizeof(pdf_doc_internal_t));
2682 memset(i, 0, sizeof(pdf_doc_internal_t));
2683 pdf_doc->internal = i;
2685 GString *fileName = new GString(filename);
2691 globalParams = new GlobalParams("");
2694 if (userPassword && userPassword[0]) {
2695 userPW = new GString(userPassword);
2699 i->doc = new PDFDoc(fileName, userPW);
2703 if (!i->doc->isOk()) {
2708 i->doc->getDocInfo(&info);
2709 if (info.isDict() &&
2710 (getScreenLogLevel()>=LOGLEVEL_NOTICE)) {
2711 printInfoString(info.getDict(), "Title", "Title: %s\n");
2712 printInfoString(info.getDict(), "Subject", "Subject: %s\n");
2713 printInfoString(info.getDict(), "Keywords", "Keywords: %s\n");
2714 printInfoString(info.getDict(), "Author", "Author: %s\n");
2715 printInfoString(info.getDict(), "Creator", "Creator: %s\n");
2716 printInfoString(info.getDict(), "Producer", "Producer: %s\n");
2717 printInfoDate(info.getDict(), "CreationDate", "CreationDate: %s\n");
2718 printInfoDate(info.getDict(), "ModDate", "ModDate: %s\n");
2719 printf("Pages: %d\n", i->doc->getNumPages());
2720 printf("Linearized: %s\n", i->doc->isLinearized() ? "yes" : "no");
2721 printf("Encrypted: ");
2722 if (i->doc->isEncrypted()) {
2723 printf("yes (print:%s copy:%s change:%s addNotes:%s)\n",
2724 i->doc->okToPrint() ? "yes" : "no",
2725 i->doc->okToCopy() ? "yes" : "no",
2726 i->doc->okToChange() ? "yes" : "no",
2727 i->doc->okToAddNotes() ? "yes" : "no");
2734 pdf_doc->num_pages = i->doc->getNumPages();
2736 if (i->doc->isEncrypted()) {
2737 if(!i->doc->okToCopy()) {
2738 printf("PDF disallows copying.\n");
2741 if(!i->doc->okToChange() || !i->doc->okToAddNotes())
2745 InfoOutputDev*io = new InfoOutputDev();
2747 for(t=1;t<=pdf_doc->num_pages;t++) {
2749 i->doc->displayPage((OutputDev*)io, t, /*zoom*/zoom, /*rotate*/0, /*doLinks*/(int)1);
2751 i->doc->displayPage((OutputDev*)io, t, zoom, zoom, /*rotate*/0, true, /*doLinks*/(int)1);
2763 delete globalParams;globalParams=0;
2764 Object::memCheck(stderr);
2769 void pdf_destroy(pdf_doc_t*pdf_doc)
2771 pdf_doc_internal_t*i= (pdf_doc_internal_t*)pdf_doc->internal;
2773 delete i->doc; i->doc=0;
2776 delete i->info;i->info=0;
2779 free(pdf_doc->internal);pdf_doc->internal=0;
2780 free(pdf_doc);pdf_doc=0;
2783 pdf_page_t* pdf_getpage(pdf_doc_t*pdf_doc, int page)
2785 pdf_doc_internal_t*di= (pdf_doc_internal_t*)pdf_doc->internal;
2787 if(page < 1 || page > pdf_doc->num_pages)
2790 pdf_page_t* pdf_page = (pdf_page_t*)malloc(sizeof(pdf_page_t));
2791 pdf_page_internal_t*pi= (pdf_page_internal_t*)malloc(sizeof(pdf_page_internal_t));
2792 memset(pi, 0, sizeof(pdf_page_internal_t));
2793 pdf_page->internal = pi;
2795 pdf_page->parent = pdf_doc;
2796 pdf_page->nr = page;
2800 void pdf_page_destroy(pdf_page_t*pdf_page)
2802 pdf_page_internal_t*i= (pdf_page_internal_t*)pdf_page->internal;
2803 free(pdf_page->internal);pdf_page->internal = 0;
2804 free(pdf_page);pdf_page=0;
2807 swf_output_t* swf_output_init()
2809 swf_output_t*swf_output = (swf_output_t*)malloc(sizeof(swf_output_t));
2810 memset(swf_output, 0, sizeof(swf_output_t));
2811 swf_output_internal_t*i= (swf_output_internal_t*)malloc(sizeof(swf_output_internal_t));
2812 memset(i, 0, sizeof(swf_output_internal_t));
2813 swf_output->internal = i;
2815 i->outputDev = new SWFOutputDev();
2819 void swf_output_setparameter(swf_output_t*swf, char*name, char*value)
2821 pdfswf_setparameter(name, value);
2824 void swf_output_startframe(swf_output_t*swf, int width, int height)
2826 swf_output_internal_t*i= (swf_output_internal_t*)swf->internal;
2827 i->outputDev->startFrame(width, height);
2830 void swf_output_endframe(swf_output_t*swf)
2832 swf_output_internal_t*i= (swf_output_internal_t*)swf->internal;
2833 i->outputDev->endframe();
2836 void swf_output_preparepage(swf_output_t*swf, int pdfpage, int outputpage)
2838 swf_output_internal_t*i= (swf_output_internal_t*)swf->internal;
2839 SWFOutputDev*o = i->outputDev;
2845 o->pagebuflen = 1024;
2846 o->pages = (int*)malloc(o->pagebuflen*sizeof(int));
2847 memset(o->pages, -1, o->pagebuflen*sizeof(int));
2849 while(pdfpage >= o->pagebuflen)
2851 int oldlen = o->pagebuflen;
2852 o->pagebuflen+=1024;
2853 o->pages = (int*)realloc(o->pages, o->pagebuflen*sizeof(int));
2854 memset(&o->pages[oldlen], -1, (o->pagebuflen-oldlen)*sizeof(int));
2857 o->pages[pdfpage] = outputpage;
2858 if(pdfpage>o->pagepos)
2859 o->pagepos = pdfpage;
2862 int swf_output_save(swf_output_t*swf, char*filename)
2864 swf_output_internal_t*i= (swf_output_internal_t*)swf->internal;
2865 int ret = i->outputDev->save(filename);
2869 void* swf_output_get(swf_output_t*swf,char*name)
2871 swf_output_internal_t*i= (swf_output_internal_t*)swf->internal;
2872 void* ret = i->outputDev->get(name);
2876 void swf_output_destroy(swf_output_t*output)
2878 swf_output_internal_t*i = (swf_output_internal_t*)output->internal;
2879 delete i->outputDev; i->outputDev=0;
2880 free(output->internal);output->internal=0;
2884 void pdf_page_render2(pdf_page_t*page, swf_output_t*swf)
2886 pdf_doc_internal_t*pi = (pdf_doc_internal_t*)page->parent->internal;
2887 swf_output_internal_t*si = (swf_output_internal_t*)swf->internal;
2890 msg("<fatal> pdf_page_render: Parent PDF this page belongs to doesn't exist yet/anymore");
2895 gfxdevice_t*dev = si->outputDev->output;
2896 dev->setparameter(dev, "protect", "1");
2898 si->outputDev->setInfo(pi->info);
2899 si->outputDev->setXRef(pi->doc, pi->doc->getXRef());
2901 pi->doc->displayPage((OutputDev*)si->outputDev, page->nr, /*zoom*/zoom, /*rotate*/0, /*doLinks*/(int)1);
2903 pi->doc->displayPage((OutputDev*)si->outputDev, page->nr, zoom, zoom, /*rotate*/0, true, /*doLinks*/(int)1);
2907 void pdf_page_rendersection(pdf_page_t*page, swf_output_t*output, int x, int y, int x1, int y1, int x2, int y2)
2909 pdf_doc_internal_t*pi = (pdf_doc_internal_t*)page->parent->internal;
2910 swf_output_internal_t*si = (swf_output_internal_t*)output->internal;
2912 si->outputDev->setMove(x,y);
2913 if((x1|y1|x2|y2)==0) x2++;
2914 si->outputDev->setClip(x1,y1,x2,y2);
2916 pdf_page_render2(page, output);
2918 void pdf_page_render(pdf_page_t*page, swf_output_t*output)
2920 pdf_doc_internal_t*pi = (pdf_doc_internal_t*)page->parent->internal;
2921 swf_output_internal_t*si = (swf_output_internal_t*)output->internal;
2923 si->outputDev->setMove(0,0);
2924 si->outputDev->setClip(0,0,0,0);
2926 pdf_page_render2(page, output);
2930 pdf_page_info_t* pdf_page_getinfo(pdf_page_t*page)
2932 pdf_doc_internal_t*pi = (pdf_doc_internal_t*)page->parent->internal;
2933 pdf_page_internal_t*i= (pdf_page_internal_t*)page->internal;
2934 pdf_page_info_t*info = (pdf_page_info_t*)malloc(sizeof(pdf_page_info_t));
2935 memset(info, 0, sizeof(pdf_page_info_t));
2937 InfoOutputDev*output = new InfoOutputDev;
2940 pi->doc->displayPage((OutputDev*)output, page->nr, /*zoom*/zoom, /*rotate*/0, /*doLinks*/(int)1);
2942 pi->doc->displayPage((OutputDev*)output, page->nr, zoom, zoom, /*rotate*/0, true, /*doLinks*/(int)1);
2945 info->xMin = output->x1;
2946 info->yMin = output->y1;
2947 info->xMax = output->x2;
2948 info->yMax = output->y2;
2949 info->number_of_images = output->num_images;
2950 info->number_of_links = output->num_links;
2951 info->number_of_fonts = output->num_fonts;
2958 void pdf_page_info_destroy(pdf_page_info_t*info)