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>
50 #include "OutputDev.h"
53 #include "CharCodeToUnicode.h"
54 #include "NameToUnicodeTable.h"
55 #include "GlobalParams.h"
56 #include "FoFiType1C.h"
57 #include "FoFiTrueType.h"
59 #include "GFXOutputDev.h"
61 //swftools header files
63 #include "../gfxdevice.h"
64 #include "../gfxtools.h"
65 #include "../gfxfont.h"
69 typedef struct _fontfile
76 static fontfile_t fonts[2048];
77 static int fontnum = 0;
81 static char* lastfontdir = 0;
87 {"Times-Roman", "n021003l"},
88 {"Times-Italic", "n021023l"},
89 {"Times-Bold", "n021004l"},
90 {"Times-BoldItalic", "n021024l"},
91 {"Helvetica", "n019003l"},
92 {"Helvetica-Oblique", "n019023l"},
93 {"Helvetica-Bold", "n019004l"},
94 {"Helvetica-BoldOblique", "n019024l"},
95 {"Courier", "n022003l"},
96 {"Courier-Oblique", "n022023l"},
97 {"Courier-Bold", "n022004l"},
98 {"Courier-BoldOblique", "n022024l"},
99 {"Symbol", "s050000l"},
100 {"ZapfDingbats", "d050000l"}};
102 GFXOutputState::GFXOutputState() {
104 this->textRender = 0;
107 GBool GFXOutputDev::interpretType3Chars()
112 typedef struct _drawnchar
130 chars = (drawnchar_t*)malloc(sizeof(drawnchar_t)*buf_size);
131 memset(chars, 0, sizeof(drawnchar_t)*buf_size);
136 free(chars);chars = 0;
143 chars = (drawnchar_t*)realloc(chars, sizeof(drawnchar_t)*buf_size);
147 void addChar(int charid, gfxcoord_t x, gfxcoord_t y, gfxcolor_t color)
150 chars[num_chars].x = x;
151 chars[num_chars].y = y;
152 chars[num_chars].color = color;
153 chars[num_chars].charid = charid;
157 static char*getFontID(GfxFont*font);
159 GFXOutputDev::GFXOutputDev(parameter_t*p)
162 this->textmodeinfo = 0;
166 this->type3active = 0;
169 this->substitutepos = 0;
170 this->type3Warning = 0;
171 this->user_movex = 0;
172 this->user_movey = 0;
173 this->user_clipx1 = 0;
174 this->user_clipy1 = 0;
175 this->user_clipx2 = 0;
176 this->user_clipy2 = 0;
177 this->current_text_stroke = 0;
178 this->current_text_clip = 0;
180 this->outer_clip_box = 0;
182 this->pagebuflen = 0;
185 this->forceType0Fonts=1;
186 this->config_use_fontconfig=1;
188 /* configure device */
190 if(!strcmp(p->name,"forceType0Fonts")) {
191 this->forceType0Fonts = atoi(p->value);
192 } else if(!strcmp(p->name,"fontconfig")) {
193 this->config_use_fontconfig = atoi(p->value);
195 msg("<warning> Ignored Paramter %s=%s", p->name, p->value);
201 void GFXOutputDev::setDevice(gfxdevice_t*dev)
206 void GFXOutputDev::setMove(int x,int y)
208 this->user_movex = x;
209 this->user_movey = y;
212 void GFXOutputDev::setClip(int x1,int y1,int x2,int y2)
214 if(x2<x1) {int x3=x1;x1=x2;x2=x3;}
215 if(y2<y1) {int y3=y1;y1=y2;y2=y3;}
217 this->user_clipx1 = x1;
218 this->user_clipy1 = y1;
219 this->user_clipx2 = x2;
220 this->user_clipy2 = y2;
223 static char*getFontID(GfxFont*font)
225 Ref*ref = font->getID();
226 GString*gstr = font->getName();
227 char* fname = gstr==0?0:gstr->getCString();
230 sprintf(buf, "font-%d-%d", ref->num, ref->gen);
232 sprintf(buf, "%s-%d-%d", fname, ref->num, ref->gen);
237 static char*getFontName(GfxFont*font)
240 GString*gstr = font->getName();
241 char* fname = gstr==0?0:gstr->getCString();
245 sprintf(buf, "UFONT%d", r->num);
246 fontid = strdup(buf);
248 fontid = strdup(fname);
251 char* plus = strchr(fontid, '+');
252 if(plus && plus < &fontid[strlen(fontid)-1]) {
253 fontname = strdup(plus+1);
255 fontname = strdup(fontid);
261 static char mybuf[1024];
262 static char* gfxstate2str(GfxState *state)
266 bufpos+=sprintf(bufpos,"CTM[%.3f/%.3f/%.3f/%.3f/%.3f/%.3f] ",
273 if(state->getX1()!=0.0)
274 bufpos+=sprintf(bufpos,"X1-%.1f ",state->getX1());
275 if(state->getY1()!=0.0)
276 bufpos+=sprintf(bufpos,"Y1-%.1f ",state->getY1());
277 bufpos+=sprintf(bufpos,"X2-%.1f ",state->getX2());
278 bufpos+=sprintf(bufpos,"Y2-%.1f ",state->getY2());
279 bufpos+=sprintf(bufpos,"PW%.1f ",state->getPageWidth());
280 bufpos+=sprintf(bufpos,"PH%.1f ",state->getPageHeight());
281 /*bufpos+=sprintf(bufpos,"FC[%.1f/%.1f] ",
282 state->getFillColor()->c[0], state->getFillColor()->c[1]);
283 bufpos+=sprintf(bufpos,"SC[%.1f/%.1f] ",
284 state->getStrokeColor()->c[0], state->getFillColor()->c[1]);*/
285 /* bufpos+=sprintf(bufpos,"FC[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f]",
286 state->getFillColor()->c[0], state->getFillColor()->c[1],
287 state->getFillColor()->c[2], state->getFillColor()->c[3],
288 state->getFillColor()->c[4], state->getFillColor()->c[5],
289 state->getFillColor()->c[6], state->getFillColor()->c[7]);
290 bufpos+=sprintf(bufpos,"SC[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f]",
291 state->getStrokeColor()->c[0], state->getFillColor()->c[1],
292 state->getStrokeColor()->c[2], state->getFillColor()->c[3],
293 state->getStrokeColor()->c[4], state->getFillColor()->c[5],
294 state->getStrokeColor()->c[6], state->getFillColor()->c[7]);*/
295 state->getFillRGB(&rgb);
296 if(rgb.r || rgb.g || rgb.b)
297 bufpos+=sprintf(bufpos,"FR[%.1f/%.1f/%.1f] ", rgb.r,rgb.g,rgb.b);
298 state->getStrokeRGB(&rgb);
299 if(rgb.r || rgb.g || rgb.b)
300 bufpos+=sprintf(bufpos,"SR[%.1f/%.1f/%.1f] ", rgb.r,rgb.g,rgb.b);
301 if(state->getFillColorSpace()->getNComps()>1)
302 bufpos+=sprintf(bufpos,"CS[[%d]] ",state->getFillColorSpace()->getNComps());
303 if(state->getStrokeColorSpace()->getNComps()>1)
304 bufpos+=sprintf(bufpos,"SS[[%d]] ",state->getStrokeColorSpace()->getNComps());
305 if(state->getFillPattern())
306 bufpos+=sprintf(bufpos,"FP%08x ", state->getFillPattern());
307 if(state->getStrokePattern())
308 bufpos+=sprintf(bufpos,"SP%08x ", state->getStrokePattern());
310 if(state->getFillOpacity()!=1.0)
311 bufpos+=sprintf(bufpos,"FO%.1f ", state->getFillOpacity());
312 if(state->getStrokeOpacity()!=1.0)
313 bufpos+=sprintf(bufpos,"SO%.1f ", state->getStrokeOpacity());
315 bufpos+=sprintf(bufpos,"LW%.1f ", state->getLineWidth());
320 state->getLineDash(&dash, &length, &start);
324 bufpos+=sprintf(bufpos,"DASH%.1f[",start);
325 for(t=0;t<length;t++) {
326 bufpos+=sprintf(bufpos,"D%.1f",dash[t]);
328 bufpos+=sprintf(bufpos,"]");
331 if(state->getFlatness()!=1)
332 bufpos+=sprintf(bufpos,"F%d ", state->getFlatness());
333 if(state->getLineJoin()!=0)
334 bufpos+=sprintf(bufpos,"J%d ", state->getLineJoin());
335 if(state->getLineJoin()!=0)
336 bufpos+=sprintf(bufpos,"C%d ", state->getLineCap());
337 if(state->getLineJoin()!=0)
338 bufpos+=sprintf(bufpos,"ML%d ", state->getMiterLimit());
340 if(state->getFont() && getFontID(state->getFont()))
341 bufpos+=sprintf(bufpos,"F\"%s\" ",getFontID(state->getFont()));
342 bufpos+=sprintf(bufpos,"FS%.1f ", state->getFontSize());
343 bufpos+=sprintf(bufpos,"MAT[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f] ", state->getTextMat()[0],state->getTextMat()[1],state->getTextMat()[2],
344 state->getTextMat()[3],state->getTextMat()[4],state->getTextMat()[5]);
345 if(state->getCharSpace())
346 bufpos+=sprintf(bufpos,"CS%.5f ", state->getCharSpace());
347 if(state->getWordSpace())
348 bufpos+=sprintf(bufpos,"WS%.5f ", state->getWordSpace());
349 if(state->getHorizScaling()!=1.0)
350 bufpos+=sprintf(bufpos,"SC%.1f ", state->getHorizScaling());
351 if(state->getLeading())
352 bufpos+=sprintf(bufpos,"L%.1f ", state->getLeading());
354 bufpos+=sprintf(bufpos,"R%.1f ", state->getRise());
355 if(state->getRender())
356 bufpos+=sprintf(bufpos,"R%d ", state->getRender());
357 bufpos+=sprintf(bufpos,"P%08x ", state->getPath());
358 bufpos+=sprintf(bufpos,"CX%.1f ", state->getCurX());
359 bufpos+=sprintf(bufpos,"CY%.1f ", state->getCurY());
360 if(state->getLineX())
361 bufpos+=sprintf(bufpos,"LX%.1f ", state->getLineX());
362 if(state->getLineY())
363 bufpos+=sprintf(bufpos,"LY%.1f ", state->getLineY());
364 bufpos+=sprintf(bufpos," ");
368 static void dumpFontInfo(char*loglevel, GfxFont*font);
369 static int lastdumps[1024];
370 static int lastdumppos = 0;
375 static void showFontError(GfxFont*font, int nr)
379 for(t=0;t<lastdumppos;t++)
380 if(lastdumps[t] == r->num)
384 if(lastdumppos<sizeof(lastdumps)/sizeof(int))
385 lastdumps[lastdumppos++] = r->num;
387 msg("<warning> The following font caused problems:");
389 msg("<warning> The following font caused problems (substituting):");
391 msg("<warning> The following Type 3 Font will be rendered as bitmap:");
392 dumpFontInfo("<warning>", font);
395 static void dumpFontInfo(char*loglevel, GfxFont*font)
397 char* id = getFontID(font);
398 char* name = getFontName(font);
399 Ref* r=font->getID();
400 msg("%s=========== %s (ID:%d,%d) ==========\n", loglevel, name, r->num,r->gen);
402 GString*gstr = font->getTag();
404 msg("%s| Tag: %s\n", loglevel, id);
406 if(font->isCIDFont()) msg("%s| is CID font\n", loglevel);
408 GfxFontType type=font->getType();
410 case fontUnknownType:
411 msg("%s| Type: unknown\n",loglevel);
414 msg("%s| Type: 1\n",loglevel);
417 msg("%s| Type: 1C\n",loglevel);
420 msg("%s| Type: 3\n",loglevel);
423 msg("%s| Type: TrueType\n",loglevel);
426 msg("%s| Type: CIDType0\n",loglevel);
429 msg("%s| Type: CIDType0C\n",loglevel);
432 msg("%s| Type: CIDType2\n",loglevel);
437 GBool embedded = font->getEmbeddedFontID(&embRef);
439 if(font->getEmbeddedFontName()) {
440 embeddedName = font->getEmbeddedFontName()->getCString();
443 msg("%s| Embedded id: %s id: %d\n",loglevel, FIXNULL(embeddedName), embRef.num);
445 gstr = font->getExtFontFile();
447 msg("%s| External Font file: %s\n", loglevel, FIXNULL(gstr->getCString()));
449 // Get font descriptor flags.
450 if(font->isFixedWidth()) msg("%s| is fixed width\n", loglevel);
451 if(font->isSerif()) msg("%s| is serif\n", loglevel);
452 if(font->isSymbolic()) msg("%s| is symbolic\n", loglevel);
453 if(font->isItalic()) msg("%s| is italic\n", loglevel);
454 if(font->isBold()) msg("%s| is bold\n", loglevel);
460 //void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, GBool invert, GBool inlineImg) {printf("void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, GBool invert, GBool inlineImg) \n");}
461 //void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, GBool inlineImg) {printf("void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, GBool inlineImg) \n");}
464 void dump_outline(gfxline_t*line)
467 if(line->type == gfx_moveTo) {
468 msg("<debug> | moveTo %.2f %.2f", line->x,line->y);
469 } else if(line->type == gfx_lineTo) {
470 msg("<debug> | lineTo %.2f %.2f", line->x,line->y);
471 } else if(line->type == gfx_splineTo) {
472 msg("<debug> | splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y);
478 gfxline_t* gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user_movex, int user_movey)
480 int num = path->getNumSubpaths();
483 double lastx=0,lasty=0,posx=0,posy=0;
486 msg("<warning> empty path");
490 gfxdrawer_target_gfxline(&draw);
492 for(t = 0; t < num; t++) {
493 GfxSubpath *subpath = path->getSubpath(t);
494 int subnum = subpath->getNumPoints();
495 double bx=0,by=0,cx=0,cy=0;
497 for(s=0;s<subnum;s++) {
500 state->transform(subpath->getX(s),subpath->getY(s),&x,&y);
505 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
506 draw.lineTo(&draw, lastx, lasty);
508 draw.moveTo(&draw, x,y);
513 } else if(subpath->getCurve(s) && cpos==0) {
517 } else if(subpath->getCurve(s) && cpos==1) {
525 draw.lineTo(&draw, x,y);
527 gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y, 0.05);
534 /* fix non-closed lines */
535 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
536 draw.lineTo(&draw, lastx, lasty);
538 gfxline_t*result = (gfxline_t*)draw.result(&draw);
542 /*----------------------------------------------------------------------------
543 * Primitive Graphic routines
544 *----------------------------------------------------------------------------*/
546 void GFXOutputDev::stroke(GfxState *state)
548 GfxPath * path = state->getPath();
549 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex, user_movey);
550 strokeGfxline(state, line);
554 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line)
556 int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
557 int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
558 double miterLimit = state->getMiterLimit();
559 double width = state->getTransformedLineWidth();
562 double opaq = state->getStrokeOpacity();
564 state->getFillRGB(&rgb);
566 state->getStrokeRGB(&rgb);
568 col.r = colToByte(rgb.r);
569 col.g = colToByte(rgb.g);
570 col.b = colToByte(rgb.b);
571 col.a = (unsigned char)(opaq*255);
573 gfx_capType capType = gfx_capRound;
574 if(lineCap == 0) capType = gfx_capButt;
575 else if(lineCap == 1) capType = gfx_capRound;
576 else if(lineCap == 2) capType = gfx_capSquare;
578 gfx_joinType joinType = gfx_joinRound;
579 if(lineJoin == 0) joinType = gfx_joinMiter;
580 else if(lineJoin == 1) joinType = gfx_joinRound;
581 else if(lineJoin == 2) joinType = gfx_joinBevel;
584 double dashphase = 0;
586 state->getLineDash(&ldash, &dashnum, &dashphase);
590 if(dashnum && ldash) {
591 float * dash = (float*)malloc(sizeof(float)*(dashnum+1));
595 msg("<trace> %d dashes", dashnum);
596 msg("<trace> | phase: %f", dashphase);
597 for(t=0;t<dashnum;t++) {
599 msg("<trace> | d%-3d: %f", t, ldash[t]);
602 if(getLogLevel() >= LOGLEVEL_TRACE) {
606 line2 = gfxtool_dash_line(line, dash, dashphase);
609 msg("<trace> After dashing:");
612 if(getLogLevel() >= LOGLEVEL_TRACE) {
613 msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x\n",
615 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
616 lineCap==0?"butt": (lineJoin==1?"round":"square"),
618 col.r,col.g,col.b,col.a
623 //swfoutput_drawgfxline(output, line, width, &col, capType, joinType, miterLimit);
624 device->stroke(device, line, width, &col, capType, joinType, miterLimit);
634 gfxcolor_t getFillColor(GfxState * state)
637 double opaq = state->getFillOpacity();
638 state->getFillRGB(&rgb);
640 col.r = colToByte(rgb.r);
641 col.g = colToByte(rgb.g);
642 col.b = colToByte(rgb.b);
643 col.a = (unsigned char)(opaq*255);
647 void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line)
649 gfxcolor_t col = getFillColor(state);
651 if(getLogLevel() >= LOGLEVEL_TRACE) {
652 msg("<trace> fill %02x%02x%02x%02x\n", col.r, col.g, col.b, col.a);
655 device->fill(device, line, &col);
657 void GFXOutputDev::fill(GfxState *state)
659 GfxPath * path = state->getPath();
660 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex, user_movey);
661 fillGfxLine(state, line);
664 void GFXOutputDev::eoFill(GfxState *state)
666 GfxPath * path = state->getPath();
667 gfxcolor_t col = getFillColor(state);
669 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex, user_movey);
671 if(getLogLevel() >= LOGLEVEL_TRACE) {
672 msg("<trace> eofill\n");
676 device->fill(device, line, &col);
680 void GFXOutputDev::clip(GfxState *state)
682 GfxPath * path = state->getPath();
683 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex, user_movey);
684 clipToGfxLine(state, line);
688 void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line)
690 if(getLogLevel() >= LOGLEVEL_TRACE) {
691 msg("<trace> clip\n");
695 device->startclip(device, line);
696 states[statepos].clipping++;
698 void GFXOutputDev::eoClip(GfxState *state)
700 GfxPath * path = state->getPath();
701 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex, user_movey);
703 if(getLogLevel() >= LOGLEVEL_TRACE) {
704 msg("<trace> eoclip\n");
708 device->startclip(device, line);
709 states[statepos].clipping++;
713 void GFXOutputDev::endframe()
716 device->endclip(device);
720 device->endpage(device);
723 void GFXOutputDev::finish()
727 device->endclip(device);
733 GFXOutputDev::~GFXOutputDev()
738 free(this->pages); this->pages = 0;
741 fontlist_t*l = this->fontlist;
743 fontlist_t*next = l->next;
745 gfxfont_free(l->font);
747 free(l->filename);l->filename=0;
753 GBool GFXOutputDev::upsideDown()
757 GBool GFXOutputDev::useDrawChar()
762 char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
763 "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
765 #define RENDER_FILL 0
766 #define RENDER_STROKE 1
767 #define RENDER_FILLSTROKE 2
768 #define RENDER_INVISIBLE 3
769 #define RENDER_CLIP 4
771 static char tmp_printstr[4096];
772 char* makeStringPrintable(char*str)
774 int len = strlen(str);
789 tmp_printstr[len++] = '.';
790 tmp_printstr[len++] = '.';
791 tmp_printstr[len++] = '.';
793 tmp_printstr[len] = 0;
798 int getGfxCharID(gfxfont_t*font, int charnr, char *charname, int u)
803 /* find out char name from unicode index
804 TODO: should be precomputed
806 for(t=0;t<sizeof(nameToUnicodeTab)/sizeof(nameToUnicodeTab[0]);t++) {
807 if(nameToUnicodeTab[t].u == u) {
808 uniname = nameToUnicodeTab[t].name;
816 for(t=0;t<font->num_glyphs;t++) {
817 if(font->glyphs[t].name && !strcmp(font->glyphs[t].name,charname)) {
818 msg("<debug> Char [%d,>%s<,%d] maps to %d\n", charnr, charname, u, t);
822 /* if we didn't find the character, maybe
823 we can find the capitalized version */
824 for(t=0;t<font->num_glyphs;t++) {
825 if(font->glyphs[t].name && !strcasecmp(font->glyphs[t].name,charname)) {
826 msg("<debug> Char [%d,>>%s<<,%d] maps to %d\n", charnr, charname, u, t);
834 for(t=0;t<font->num_glyphs;t++) {
835 if(font->glyphs[t].name && !strcmp(font->glyphs[t].name,uniname)) {
836 msg("<debug> Char [%d,%s,>%d(%s)<] maps to %d\n", charnr, charname, u, uniname, t);
840 /* if we didn't find the character, maybe
841 we can find the capitalized version */
842 for(t=0;t<font->num_glyphs;t++) {
843 if(font->glyphs[t].name && !strcasecmp(font->glyphs[t].name,uniname)) {
844 msg("<debug> Char [%d,%s,>>%d(%s)<<] maps to %d\n", charnr, charname, u, uniname, t);
850 /* try to use the unicode id */
851 if(u>=0 && u<font->max_unicode && font->unicode2glyph[u]>=0) {
852 msg("<debug> Char [%d,%s,>%d<] maps to %d\n", charnr, charname, u, font->unicode2glyph[u]);
853 return font->unicode2glyph[u];
856 if(charnr>=0 && charnr<font->num_glyphs) {
857 msg("<debug> Char [>%d<,%s,%d] maps to %d\n", charnr, charname, u, charnr);
865 void GFXOutputDev::beginString(GfxState *state, GString *s)
867 int render = state->getRender();
868 if(current_text_stroke) {
869 msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
872 msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
873 double m11,m21,m12,m22;
874 // msg("<debug> %s beginstring \"%s\"\n", gfxstate2str(state), s->getCString());
875 state->getFontTransMat(&m11, &m12, &m21, &m22);
876 m11 *= state->getHorizScaling();
877 m21 *= state->getHorizScaling();
879 this->current_font_matrix.m00 = m11 / 1024.0;
880 this->current_font_matrix.m01 = m12 / 1024.0;
881 this->current_font_matrix.m10 = -m21 / 1024.0;
882 this->current_font_matrix.m11 = -m22 / 1024.0;
883 this->current_font_matrix.tx = 0;
884 this->current_font_matrix.ty = 0;
886 gfxmatrix_t m = this->current_font_matrix;
888 /*if(render != 3 && render != 0)
889 msg("<warning> Text rendering mode %d (%s) not fully supported yet (for text \"%s\")", render, renderModeDesc[render&7], makeStringPrintable(s->getCString()));*/
890 states[statepos].textRender = render;
893 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
894 double dx, double dy,
895 double originX, double originY,
896 CharCode c, int nBytes, Unicode *_u, int uLen)
898 int render = state->getRender();
899 // check for invisible text -- this is used by Acrobat Capture
901 msg("<debug> Ignoring invisible text: char %d at %f,%f", c, x, y);
905 if(states[statepos].textRender != render)
906 msg("<error> Internal error: drawChar.render!=beginString.render");
908 gfxcolor_t col = getFillColor(state);
910 Gushort *CIDToGIDMap = 0;
911 GfxFont*font = state->getFont();
913 if(font->getType() == fontType3) {
914 /* type 3 chars are passed as graphics */
915 msg("<debug> type3 char at %f/%f", x, y);
925 if(font->isCIDFont()) {
926 GfxCIDFont*cfont = (GfxCIDFont*)font;
928 if(font->getType() == fontCIDType2)
929 CIDToGIDMap = cfont->getCIDToGID();
932 font8 = (Gfx8BitFont*)font;
933 char**enc=font8->getEncoding();
937 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);
940 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);
946 charid = getGfxCharID(current_gfxfont, c, name, u);
948 charid = getGfxCharID(current_gfxfont, c, name, -1);
951 /* multiple unicodes- should usually map to a ligature.
952 if the ligature doesn't exist, we need to draw
953 the characters one-by-one. */
955 msg("<warning> ligature %d missing in font %s\n", c, current_font_id);
956 for(t=0;t<uLen;t++) {
957 drawChar(state, x, y, dx, dy, originX, originY, c, nBytes, _u+t, 1);
963 msg("<warning> Didn't find character '%s' (c=%d,u=%d) in current charset (%s, %d characters)",
964 FIXNULL(name),c, u, FIXNULL((char*)current_font_id), current_gfxfont->num_glyphs);
968 gfxmatrix_t m = this->current_font_matrix;
969 state->transform(x, y, &m.tx, &m.ty);
973 if(render == RENDER_FILL) {
974 device->drawchar(device, current_font_id, charid, &col, &m);
976 msg("<debug> Drawing glyph %d as shape", charid);
978 msg("<notice> Some texts will be rendered as shape");
981 gfxline_t*glyph = current_gfxfont->glyphs[charid].line;
982 gfxline_t*tglyph = gfxline_clone(glyph);
983 gfxline_transform(tglyph, &m);
984 if((render&3) != RENDER_INVISIBLE) {
985 gfxline_t*add = gfxline_clone(tglyph);
986 current_text_stroke = gfxline_append(current_text_stroke, add);
988 if(render&RENDER_CLIP) {
989 gfxline_t*add = gfxline_clone(tglyph);
990 current_text_clip = gfxline_append(current_text_clip, add);
992 gfxline_free(tglyph);
996 void GFXOutputDev::endString(GfxState *state)
998 int render = state->getRender();
999 msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
1000 if(states[statepos].textRender != render)
1001 msg("<error> Internal error: drawChar.render!=beginString.render");
1003 if(current_text_stroke) {
1004 /* fillstroke and stroke text rendering objects we can process right
1005 now (as there may be texts of other rendering modes in this
1006 text object)- clipping objects have to wait until endTextObject,
1008 device->setparameter(device, "mark","TXT");
1009 if((render&3) == RENDER_FILL) {
1010 fillGfxLine(state, current_text_stroke);
1011 gfxline_free(current_text_stroke);
1012 current_text_stroke = 0;
1013 } else if((render&3) == RENDER_FILLSTROKE) {
1014 fillGfxLine(state, current_text_stroke);
1015 strokeGfxline(state, current_text_stroke);
1016 gfxline_free(current_text_stroke);
1017 current_text_stroke = 0;
1018 } else if((render&3) == RENDER_STROKE) {
1019 strokeGfxline(state, current_text_stroke);
1020 gfxline_free(current_text_stroke);
1021 current_text_stroke = 0;
1023 device->setparameter(device, "mark","");
1027 void GFXOutputDev::endTextObject(GfxState *state)
1029 int render = state->getRender();
1030 msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1031 if(states[statepos].textRender != render)
1032 msg("<error> Internal error: drawChar.render!=beginString.render");
1034 if(current_text_clip) {
1035 device->setparameter(device, "mark","TXT");
1036 clipToGfxLine(state, current_text_clip);
1037 device->setparameter(device, "mark","");
1038 gfxline_free(current_text_clip);
1039 current_text_clip = 0;
1043 /* the logic seems to be as following:
1044 first, beginType3Char is called, with the charcode and the coordinates.
1045 if this function returns true, it already knew about the char and has now drawn it.
1046 if the function returns false, it's a new char, and type3D1 is called with some parameters-
1047 the all draw operations until endType3Char are part of the char (which in this moment is
1048 at the position first passed to beginType3Char). the char ends with endType3Char.
1050 The drawing operations between beginType3Char and endType3Char are somewhat different to
1051 the normal ones. For example, the fillcolor equals the stroke color.
1054 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode code, Unicode *u, int uLen)
1056 msg("<debug> beginType3Char %d, %08x, %d", code, *u, uLen);
1058 /* the character itself is going to be passed using the draw functions */
1059 return gFalse; /* gTrue= is_in_cache? */
1062 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1063 msg("<debug> type3D0 width=%f height=%f", wx, wy);
1065 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1066 msg("<debug> type3D1 width=%f height=%f bbox=(%f,%f,%f,%f)", wx, wy,
1070 void GFXOutputDev::endType3Char(GfxState *state)
1073 msg("<debug> endType3Char");
1076 void GFXOutputDev::startFrame(int width, int height)
1078 device->startpage(device, width, height);
1081 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
1083 this->currentpage = pageNum;
1085 int rot = doc->getPageRotate(1);
1088 gfxline_t clippath[5];
1090 white.r = white.g = white.b = white.a = 255;
1092 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1093 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1094 Use CropBox, not MediaBox, as page size
1101 state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1102 state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1104 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1105 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1108 /* apply user clip box */
1109 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1110 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1111 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1112 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1113 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1116 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1118 if(outer_clip_box) {
1119 device->endclip(device);
1123 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);
1125 msg("<verbose> page is rotated %d degrees\n", rot);
1127 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1128 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1129 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1130 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1131 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1132 device->startclip(device, clippath); outer_clip_box = 1;
1133 device->fill(device, clippath, &white);
1136 void GFXOutputDev::drawLink(Link *link, Catalog *catalog)
1138 double x1, y1, x2, y2, w;
1139 gfxline_t points[5];
1142 msg("<debug> drawlink\n");
1144 link->getRect(&x1, &y1, &x2, &y2);
1145 cvtUserToDev(x1, y1, &x, &y);
1146 points[0].type = gfx_moveTo;
1147 points[0].x = points[4].x = x + user_movex;
1148 points[0].y = points[4].y = y + user_movey;
1149 points[0].next = &points[1];
1150 cvtUserToDev(x2, y1, &x, &y);
1151 points[1].type = gfx_lineTo;
1152 points[1].x = x + user_movex;
1153 points[1].y = y + user_movey;
1154 points[1].next = &points[2];
1155 cvtUserToDev(x2, y2, &x, &y);
1156 points[2].type = gfx_lineTo;
1157 points[2].x = x + user_movex;
1158 points[2].y = y + user_movey;
1159 points[2].next = &points[3];
1160 cvtUserToDev(x1, y2, &x, &y);
1161 points[3].type = gfx_lineTo;
1162 points[3].x = x + user_movex;
1163 points[3].y = y + user_movey;
1164 points[3].next = &points[4];
1165 cvtUserToDev(x1, y1, &x, &y);
1166 points[4].type = gfx_lineTo;
1167 points[4].x = x + user_movex;
1168 points[4].y = y + user_movey;
1171 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f\n",
1172 points[0].x, points[0].y,
1173 points[1].x, points[1].y,
1174 points[2].x, points[2].y,
1175 points[3].x, points[3].y);
1177 LinkAction*action=link->getAction();
1183 msg("<trace> drawlink action=%d\n", action->getKind());
1184 switch(action->getKind())
1188 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1189 LinkDest *dest=NULL;
1190 if (ha->getDest()==NULL)
1191 dest=catalog->findDest(ha->getNamedDest());
1192 else dest=ha->getDest();
1194 if (dest->isPageRef()){
1195 Ref pageref=dest->getPageRef();
1196 page=catalog->findPage(pageref.num,pageref.gen);
1198 else page=dest->getPageNum();
1199 sprintf(buf, "%d", page);
1206 LinkGoToR*l = (LinkGoToR*)action;
1207 GString*g = l->getNamedDest();
1209 s = strdup(g->getCString());
1214 LinkNamed*l = (LinkNamed*)action;
1215 GString*name = l->getName();
1217 s = strdup(name->lowerCase()->getCString());
1218 named = name->getCString();
1221 if(strstr(s, "next") || strstr(s, "forward"))
1223 page = currentpage + 1;
1225 else if(strstr(s, "prev") || strstr(s, "back"))
1227 page = currentpage - 1;
1229 else if(strstr(s, "last") || strstr(s, "end"))
1231 if(pages && pagepos>0)
1232 page = pages[pagepos-1];
1234 else if(strstr(s, "first") || strstr(s, "top"))
1242 case actionLaunch: {
1244 LinkLaunch*l = (LinkLaunch*)action;
1245 GString * str = new GString(l->getFileName());
1246 GString * params = l->getParams();
1248 str->append(params);
1249 s = strdup(str->getCString());
1256 LinkURI*l = (LinkURI*)action;
1257 GString*g = l->getURI();
1259 url = g->getCString();
1264 case actionUnknown: {
1266 LinkUnknown*l = (LinkUnknown*)action;
1271 msg("<error> Unknown link type!\n");
1276 if(!s) s = strdup("-?-");
1278 msg("<trace> drawlink s=%s\n", s);
1280 if(!linkinfo && (page || s))
1282 msg("<notice> File contains links");
1290 for(t=1;t<=pagepos;t++) {
1291 if(pages[t]==page) {
1300 sprintf(buf, "page%d", lpage);
1301 device->drawlink(device, points, buf);
1305 device->drawlink(device, points, s);
1308 msg("<verbose> \"%s\" link to \"%s\" (%d)\n", type, FIXNULL(s), page);
1312 void GFXOutputDev::saveState(GfxState *state) {
1313 msg("<trace> saveState\n");
1316 msg("<error> Too many nested states in pdf.");
1320 states[statepos].clipping = 0; //? shouldn't this be the current value?
1321 states[statepos].textRender = states[statepos-1].textRender;
1324 void GFXOutputDev::restoreState(GfxState *state) {
1325 msg("<trace> restoreState\n");
1327 while(states[statepos].clipping) {
1328 device->endclip(device);
1329 states[statepos].clipping--;
1334 char* GFXOutputDev::searchFont(char*name)
1338 int is_standard_font = 0;
1340 msg("<verbose> SearchFont(%s)", name);
1342 /* see if it is a pdf standard font */
1343 for(i=0;i<sizeof(pdf2t1map)/sizeof(mapping);i++)
1345 if(!strcmp(name, pdf2t1map[i].pdffont))
1347 name = pdf2t1map[i].filename;
1348 is_standard_font = 1;
1352 /* look in all font files */
1353 for(i=0;i<fontnum;i++)
1355 if(strstr(fonts[i].filename, name))
1357 if(!fonts[i].used) {
1360 if(!is_standard_font)
1361 msg("<notice> Using %s for %s", fonts[i].filename, name);
1363 return strdup(fonts[i].filename);
1369 void GFXOutputDev::updateLineWidth(GfxState *state)
1371 double width = state->getTransformedLineWidth();
1372 //swfoutput_setlinewidth(&device, width);
1375 void GFXOutputDev::updateLineCap(GfxState *state)
1377 int c = state->getLineCap();
1380 void GFXOutputDev::updateLineJoin(GfxState *state)
1382 int j = state->getLineJoin();
1385 void GFXOutputDev::updateFillColor(GfxState *state)
1388 double opaq = state->getFillOpacity();
1389 state->getFillRGB(&rgb);
1391 //swfoutput_setfillcolor(&device, (char)(rgb.r*255), (char)(rgb.g*255), (char)(rgb.b*255), (char)(opaq*255));
1394 void GFXOutputDev::updateStrokeColor(GfxState *state)
1397 double opaq = state->getStrokeOpacity();
1398 state->getStrokeRGB(&rgb);
1399 //swfoutput_setstrokecolor(&device, (char)(rgb.r*255), (char)(rgb.g*255), (char)(rgb.b*255), (char)(opaq*255));
1402 void FoFiWrite(void *stream, char *data, int len)
1404 fwrite(data, len, 1, (FILE*)stream);
1407 char*GFXOutputDev::writeEmbeddedFontToFile(XRef*ref, GfxFont*font)
1409 char*tmpFileName = NULL;
1415 Object refObj, strObj;
1417 tmpFileName = mktmpname(namebuf);
1420 ret = font->getEmbeddedFontID(&embRef);
1422 msg("<verbose> Didn't get embedded font id");
1423 /* not embedded- the caller should now search the font
1424 directories for this font */
1428 f = fopen(tmpFileName, "wb");
1430 msg("<error> Couldn't create temporary Type 1 font file");
1434 /*if(font->isCIDFont()) {
1435 GfxCIDFont* cidFont = (GfxCIDFont *)font;
1436 GString c = cidFont->getCollection();
1437 msg("<notice> Collection: %s", c.getCString());
1440 //if (font->getType() == fontType1C) {
1441 if (0) { //font->getType() == fontType1C) {
1442 if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
1444 msg("<error> Couldn't read embedded font file");
1447 FoFiType1C *cvt = FoFiType1C::make(fontBuf, fontLen);
1449 cvt->convertToType1(NULL, gTrue, FoFiWrite, f);
1450 //cvt->convertToCIDType0("test", f);
1451 //cvt->convertToType0("test", f);
1454 } else if(font->getType() == fontTrueType) {
1455 msg("<verbose> writing font using TrueTypeFontFile::writeTTF");
1456 if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
1458 msg("<error> Couldn't read embedded font file");
1461 FoFiTrueType *cvt = FoFiTrueType::make(fontBuf, fontLen);
1462 cvt->writeTTF(FoFiWrite, f);
1466 font->getEmbeddedFontID(&embRef);
1467 refObj.initRef(embRef.num, embRef.gen);
1468 refObj.fetch(ref, &strObj);
1470 strObj.streamReset();
1475 f4[t] = strObj.streamGetChar();
1476 f4c[t] = (char)f4[t];
1481 if(!strncmp(f4c, "true", 4)) {
1482 /* some weird TTF fonts don't start with 0,1,0,0 but with "true".
1483 Change this on the fly */
1484 f4[0] = f4[2] = f4[3] = 0;
1492 while ((c = strObj.streamGetChar()) != EOF) {
1496 strObj.streamClose();
1501 return strdup(tmpFileName);
1504 char* GFXOutputDev::searchForSuitableFont(GfxFont*gfxFont)
1506 char*name = getFontName(gfxFont);
1510 if(!this->config_use_fontconfig)
1513 #ifdef HAVE_FONTCONFIG
1514 FcPattern *pattern, *match;
1518 static int fcinitcalled = false;
1520 msg("<debug> searchForSuitableFont(%s)", name);
1522 // call init ony once
1523 if (!fcinitcalled) {
1524 msg("<debug> Initializing FontConfig...");
1525 fcinitcalled = true;
1527 msg("<debug> FontConfig Initialization failed. Disabling.");
1528 config_use_fontconfig = 0;
1531 msg("<debug> ...initialized FontConfig");
1534 msg("<debug> FontConfig: Create \"%s\" Family Pattern", name);
1535 pattern = FcPatternBuild(NULL, FC_FAMILY, FcTypeString, name, NULL);
1536 if (gfxFont->isItalic()) // check for italic
1537 msg("<debug> FontConfig: Adding Italic Slant");
1538 FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
1539 if (gfxFont->isBold()) // check for bold
1540 msg("<debug> FontConfig: Adding Bold Weight");
1541 FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
1543 msg("<debug> FontConfig: Try to match...");
1544 // configure and match using the original font name
1545 FcConfigSubstitute(0, pattern, FcMatchPattern);
1546 FcDefaultSubstitute(pattern);
1547 match = FcFontMatch(0, pattern, &result);
1549 if (FcPatternGetString(match, "family", 0, &v) == FcResultMatch) {
1550 msg("<debug> FontConfig: family=%s", (char*)v);
1551 // if we get an exact match
1552 if (strcmp((char *)v, name) == 0) {
1553 if (FcPatternGetString(match, "file", 0, &v) == FcResultMatch) {
1554 filename = strdup((char*)v); // mem leak
1555 char *nfn = strrchr(filename, '/');
1556 if(nfn) fontname = strdup(nfn+1);
1557 else fontname = filename;
1559 msg("<debug> FontConfig: Returning \"%s\"", fontname);
1561 // initialize patterns
1562 FcPatternDestroy(pattern);
1563 FcPatternDestroy(match);
1565 // now match against serif etc.
1566 if (gfxFont->isSerif()) {
1567 msg("<debug> FontConfig: Create Serif Family Pattern");
1568 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "serif", NULL);
1569 } else if (gfxFont->isFixedWidth()) {
1570 msg("<debug> FontConfig: Create Monospace Family Pattern");
1571 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "monospace", NULL);
1573 msg("<debug> FontConfig: Create Sans Family Pattern");
1574 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "sans", NULL);
1578 if (gfxFont->isItalic()) {
1579 msg("<debug> FontConfig: Adding Italic Slant");
1580 int bb = FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
1583 if (gfxFont->isBold()) {
1584 msg("<debug> FontConfig: Adding Bold Weight");
1585 int bb = FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
1588 msg("<debug> FontConfig: Try to match... (2)");
1589 // configure and match using serif etc
1590 FcConfigSubstitute (0, pattern, FcMatchPattern);
1591 FcDefaultSubstitute (pattern);
1592 match = FcFontMatch (0, pattern, &result);
1594 if (FcPatternGetString(match, "file", 0, &v) == FcResultMatch) {
1595 filename = strdup((char*)v); // mem leak
1596 char *nfn = strrchr(filename, '/');
1597 if(nfn) fontname = strdup(nfn+1);
1598 else fontname = filename;
1600 msg("<debug> FontConfig: Returning \"%s\"", fontname);
1604 //printf("FONTCONFIG: pattern");
1605 //FcPatternPrint(pattern);
1606 //printf("FONTCONFIG: match");
1607 //FcPatternPrint(match);
1609 FcPatternDestroy(pattern);
1610 FcPatternDestroy(match);
1612 pdfswf_addfont(filename);
1619 char* GFXOutputDev::substituteFont(GfxFont*gfxFont, char* oldname)
1621 char*fontname = 0, *filename = 0;
1622 msg("<notice> substituteFont(%s)", oldname);
1624 if(!(fontname = searchForSuitableFont(gfxFont))) {
1625 fontname = "Times-Roman";
1627 filename = searchFont(fontname);
1629 msg("<error> Couldn't find font %s- did you install the default fonts?", fontname);
1633 if(substitutepos>=sizeof(substitutesource)/sizeof(char*)) {
1634 msg("<fatal> Too many fonts in file.");
1638 substitutesource[substitutepos] = strdup(oldname); //mem leak
1639 substitutetarget[substitutepos] = fontname;
1640 msg("<notice> substituting %s -> %s", FIXNULL(oldname), FIXNULL(fontname));
1643 return strdup(filename); //mem leak
1646 void unlinkfont(char* filename)
1653 if(!strncmp(&filename[l-4],".afm",4)) {
1654 memcpy(&filename[l-4],".pfb",4);
1656 memcpy(&filename[l-4],".pfa",4);
1658 memcpy(&filename[l-4],".afm",4);
1661 if(!strncmp(&filename[l-4],".pfa",4)) {
1662 memcpy(&filename[l-4],".afm",4);
1664 memcpy(&filename[l-4],".pfa",4);
1667 if(!strncmp(&filename[l-4],".pfb",4)) {
1668 memcpy(&filename[l-4],".afm",4);
1670 memcpy(&filename[l-4],".pfb",4);
1675 void GFXOutputDev::setXRef(PDFDoc*doc, XRef *xref)
1681 int GFXOutputDev::setGfxFont(char*id, char*name, char*filename, double maxSize)
1684 fontlist_t*last=0,*l = this->fontlist;
1686 /* TODO: should this be part of the state? */
1689 if(!strcmp(l->id, id)) {
1690 current_font_id = l->id;
1691 current_gfxfont = l->font;
1693 device->addfont(device, id, current_gfxfont);
1698 if(!filename) return 0;
1700 /* A font size of e.g. 9 means the font will be scaled down by
1701 1024 and scaled up by 9. So to have a maximum error of 1/20px,
1702 we have to divide 0.05 by (fontsize/1024)
1704 double quality = (1024 * 0.05) / maxSize;
1706 msg("<verbose> Loading %s...", filename);
1707 font = gfxfont_load(filename, quality);
1708 msg("<verbose> Font %s loaded successfully", filename);
1712 l->filename = strdup(filename);
1715 current_font_id = l->id;
1716 current_gfxfont = l->font;
1722 device->addfont(device, id, current_gfxfont);
1726 void GFXOutputDev::updateFont(GfxState *state)
1728 GfxFont*gfxFont = state->getFont();
1734 char * fontid = getFontID(gfxFont);
1735 char * fontname = getFontName(gfxFont);
1737 double maxSize = 1.0;
1740 maxSize = this->info->getMaximumFontSize(fontid);
1744 /* first, look if we substituted this font before-
1745 this way, we don't initialize the T1 Fonts
1747 for(t=0;t<substitutepos;t++) {
1748 if(!strcmp(fontid, substitutesource[t])) {
1749 free(fontid);fontid=0;
1750 fontid = strdup(substitutetarget[t]);
1755 /* second, see if this is a font which was used before-
1756 if so, we are done */
1757 if(setGfxFont(fontid, fontname, 0, 0)) {
1762 /* if(swfoutput_queryfont(&device, fontid))
1763 swfoutput_setfont(&device, fontid, 0);
1765 msg("<debug> updateFont(%s) [cached]", fontid);
1769 // look for Type 3 font
1770 if (gfxFont->getType() == fontType3) {
1772 type3Warning = gTrue;
1773 showFontError(gfxFont, 2);
1780 /* now either load the font, or find a substitution */
1783 GBool embedded = gfxFont->getEmbeddedFontID(&embRef);
1788 (gfxFont->getType() == fontType1 ||
1789 gfxFont->getType() == fontType1C ||
1790 (gfxFont->getType() == fontCIDType0C && forceType0Fonts) ||
1791 gfxFont->getType() == fontTrueType ||
1792 gfxFont->getType() == fontCIDType2
1795 fileName = writeEmbeddedFontToFile(xref, gfxFont);
1796 if(!fileName) showFontError(gfxFont,0);
1799 fileName = searchFont(fontname);
1800 if(!fileName) showFontError(gfxFont,0);
1803 char * fontname = getFontName(gfxFont);
1804 msg("<warning> Font %s %scould not be loaded.", fontname, embedded?"":"(not embedded) ");
1807 msg("<warning> Try putting a TTF version of that font (named \"%s.ttf\") into %s", fontname, lastfontdir);
1809 msg("<warning> Try specifying one or more font directories");
1811 fileName = substituteFont(gfxFont, fontid);
1814 if(fontid) { free(fontid);fontid = strdup(substitutetarget[substitutepos-1]); /*ugly hack*/};
1815 msg("<notice> Font is now %s (%s)", fontid, fileName);
1819 msg("<error> Couldn't set font %s\n", fontid);
1825 msg("<verbose> updateFont(%s) -> %s (max size: %f)", fontid, fileName, maxSize);
1826 dumpFontInfo("<verbose>", gfxFont);
1828 //swfoutput_setfont(&device, fontid, fileName);
1830 if(!setGfxFont(fontid, fontname, 0, 0)) {
1831 setGfxFont(fontid, fontname, fileName, maxSize);
1835 unlinkfont(fileName);
1845 #define SQR(x) ((x)*(x))
1847 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
1849 if((newwidth<2 || newheight<2) ||
1850 (width<=newwidth || height<=newheight))
1852 unsigned char*newdata;
1854 newdata= (unsigned char*)malloc(newwidth*newheight);
1856 double fx = (double)(width)/newwidth;
1857 double fy = (double)(height)/newheight;
1859 int blocksize = (int)(8192/(fx*fy));
1860 int r = 8192*256/palettesize;
1861 for(x=0;x<newwidth;x++) {
1862 double ex = px + fx;
1863 int fromx = (int)px;
1865 int xweight1 = (int)(((fromx+1)-px)*256);
1866 int xweight2 = (int)((ex-tox)*256);
1868 for(y=0;y<newheight;y++) {
1869 double ey = py + fy;
1870 int fromy = (int)py;
1872 int yweight1 = (int)(((fromy+1)-py)*256);
1873 int yweight2 = (int)((ey-toy)*256);
1876 for(xx=fromx;xx<=tox;xx++)
1877 for(yy=fromy;yy<=toy;yy++) {
1878 int b = 1-data[width*yy+xx];
1880 if(xx==fromx) weight = (weight*xweight1)/256;
1881 if(xx==tox) weight = (weight*xweight2)/256;
1882 if(yy==fromy) weight = (weight*yweight1)/256;
1883 if(yy==toy) weight = (weight*yweight2)/256;
1886 //if(a) a=(palettesize-1)*r/blocksize;
1887 newdata[y*newwidth+x] = (a*blocksize)/r;
1895 #define IMAGE_TYPE_JPEG 0
1896 #define IMAGE_TYPE_LOSSLESS 1
1898 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
1899 double x1,double y1,
1900 double x2,double y2,
1901 double x3,double y3,
1902 double x4,double y4, int type)
1904 gfxcolor_t*newpic=0;
1906 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
1907 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
1909 gfxline_t p1,p2,p3,p4,p5;
1910 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
1911 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
1912 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
1913 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
1914 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
1916 {p1.x = (int)(p1.x*20)/20.0;
1917 p1.y = (int)(p1.y*20)/20.0;
1918 p2.x = (int)(p2.x*20)/20.0;
1919 p2.y = (int)(p2.y*20)/20.0;
1920 p3.x = (int)(p3.x*20)/20.0;
1921 p3.y = (int)(p3.y*20)/20.0;
1922 p4.x = (int)(p4.x*20)/20.0;
1923 p4.y = (int)(p4.y*20)/20.0;
1924 p5.x = (int)(p5.x*20)/20.0;
1925 p5.y = (int)(p5.y*20)/20.0;
1932 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
1933 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
1938 img.data = (gfxcolor_t*)data;
1942 if(type == IMAGE_TYPE_JPEG)
1943 /* TODO: pass image_dpi to device instead */
1944 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
1946 dev->fillbitmap(dev, &p1, &img, &m, 0);
1949 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
1950 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
1952 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG);
1955 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
1956 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
1958 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS);
1962 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
1963 int width, int height, GfxImageColorMap*colorMap, GBool invert,
1964 GBool inlineImg, int mask, int*maskColors,
1965 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
1967 double x1,y1,x2,y2,x3,y3,x4,y4;
1968 ImageStream *imgStr;
1973 unsigned char* maskbitmap = 0;
1976 ncomps = colorMap->getNumPixelComps();
1977 bits = colorMap->getBits();
1982 unsigned char buf[8];
1983 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
1985 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
1986 imgMaskStr->reset();
1987 unsigned char pal[256];
1988 int n = 1 << colorMap->getBits();
1993 maskColorMap->getGray(pixBuf, &gray);
1994 pal[t] = colToByte(gray);
1996 for (y = 0; y < maskHeight; y++) {
1997 for (x = 0; x < maskWidth; x++) {
1998 imgMaskStr->getPixel(buf);
1999 maskbitmap[y*maskWidth+x] = pal[buf[0]];
2004 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2005 imgMaskStr->reset();
2006 for (y = 0; y < maskHeight; y++) {
2007 for (x = 0; x < maskWidth; x++) {
2008 imgMaskStr->getPixel(buf);
2010 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2018 imgStr = new ImageStream(str, width, ncomps,bits);
2021 if(!width || !height || (height<=1 && width<=1))
2023 msg("<verbose> Ignoring %d by %d image", width, height);
2024 unsigned char buf[8];
2026 for (y = 0; y < height; ++y)
2027 for (x = 0; x < width; ++x) {
2028 imgStr->getPixel(buf);
2036 state->transform(0, 1, &x1, &y1); x1 += user_movex; y1 += user_movey;
2037 state->transform(0, 0, &x2, &y2); x2 += user_movex; y2 += user_movey;
2038 state->transform(1, 0, &x3, &y3); x3 += user_movex; y3 += user_movey;
2039 state->transform(1, 1, &x4, &y4); x4 += user_movex; y4 += user_movey;
2042 if(!pbminfo && !(str->getKind()==strDCT)) {
2044 msg("<notice> file contains pbm pictures %s",mask?"(masked)":"");
2048 msg("<verbose> drawing %d by %d masked picture\n", width, height);
2050 if(!jpeginfo && (str->getKind()==strDCT)) {
2051 msg("<notice> file contains jpeg pictures");
2057 unsigned char buf[8];
2059 unsigned char*pic = new unsigned char[width*height];
2060 gfxcolor_t pal[256];
2062 state->getFillRGB(&rgb);
2064 memset(pal,255,sizeof(pal));
2065 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2066 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2067 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2068 pal[0].a = 255; pal[1].a = 0;
2071 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2072 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2073 for (y = 0; y < height; ++y)
2074 for (x = 0; x < width; ++x)
2076 imgStr->getPixel(buf);
2079 pic[width*y+x] = buf[0];
2082 /* the size of the drawn image is added to the identifier
2083 as the same image may require different bitmaps if displayed
2084 at different sizes (due to antialiasing): */
2087 unsigned char*pic2 = 0;
2090 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2099 height = realheight;
2103 /* make a black/white palette */
2105 float r = 255/(numpalette-1);
2107 for(t=0;t<numpalette;t++) {
2108 pal[t].r = colToByte(rgb.r);
2109 pal[t].g = colToByte(rgb.g);
2110 pal[t].b = colToByte(rgb.b);
2111 pal[t].a = (unsigned char)(t*r);
2115 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2116 for (y = 0; y < height; ++y) {
2117 for (x = 0; x < width; ++x) {
2118 pic2[width*y+x] = pal[pic[y*width+x]];
2121 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2125 if(maskbitmap) free(maskbitmap);
2131 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2132 gfxcolor_t*pic=new gfxcolor_t[width*height];
2133 for (y = 0; y < height; ++y) {
2134 for (x = 0; x < width; ++x) {
2135 imgStr->getPixel(pixBuf);
2136 colorMap->getRGB(pixBuf, &rgb);
2137 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2138 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2139 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2140 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2142 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2146 if(str->getKind()==strDCT)
2147 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2149 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2152 if(maskbitmap) free(maskbitmap);
2155 gfxcolor_t*pic=new gfxcolor_t[width*height];
2156 gfxcolor_t pal[256];
2157 int n = 1 << colorMap->getBits();
2159 for(t=0;t<256;t++) {
2161 colorMap->getRGB(pixBuf, &rgb);
2163 {/*if(maskColors && *maskColors==t) {
2164 msg("<notice> Color %d is transparent", t);
2165 if (imgData->maskColors) {
2167 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2168 if (pix[i] < imgData->maskColors[2*i] ||
2169 pix[i] > imgData->maskColors[2*i+1]) {
2184 pal[t].r = (unsigned char)(colToByte(rgb.r));
2185 pal[t].g = (unsigned char)(colToByte(rgb.g));
2186 pal[t].b = (unsigned char)(colToByte(rgb.b));
2187 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2190 for (y = 0; y < height; ++y) {
2191 for (x = 0; x < width; ++x) {
2192 imgStr->getPixel(pixBuf);
2193 pic[width*y+x] = pal[pixBuf[0]];
2195 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2199 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2203 if(maskbitmap) free(maskbitmap);
2208 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2209 int width, int height, GBool invert,
2212 if(states[statepos].textRender & 4) //clipped
2214 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2215 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2218 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2219 int width, int height, GfxImageColorMap *colorMap,
2220 int *maskColors, GBool inlineImg)
2222 if(states[statepos].textRender & 4) //clipped
2225 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
2226 colorMap?"colorMap":"no colorMap",
2227 maskColors?"maskColors":"no maskColors",
2230 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2231 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2232 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2235 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2236 int width, int height,
2237 GfxImageColorMap *colorMap,
2238 Stream *maskStr, int maskWidth, int maskHeight,
2241 if(states[statepos].textRender & 4) //clipped
2244 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2245 colorMap?"colorMap":"no colorMap",
2246 maskWidth, maskHeight);
2248 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2249 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2250 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2253 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2254 int width, int height,
2255 GfxImageColorMap *colorMap,
2257 int maskWidth, int maskHeight,
2258 GfxImageColorMap *maskColorMap)
2260 if(states[statepos].textRender & 4) //clipped
2263 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2264 colorMap?"colorMap":"no colorMap",
2265 maskWidth, maskHeight);
2267 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2268 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2269 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2272 static char* dirseparator()
2281 void addGlobalFont(char*filename)
2284 memset(&f, 0, sizeof(fontfile_t));
2285 f.filename = filename;
2286 if(fontnum < sizeof(fonts)/sizeof(fonts[0])) {
2287 msg("<verbose> Adding font \"%s\".", filename);
2288 fonts[fontnum++] = f;
2290 msg("<error> Too many external fonts. Not adding font file \"%s\".", filename);
2294 void addGlobalLanguageDir(char*dir)
2297 globalParams = new GlobalParams("");
2299 msg("<notice> Adding %s to language pack directories", dir);
2303 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2304 strcpy(config_file, dir);
2305 strcat(config_file, dirseparator());
2306 strcat(config_file, "add-to-xpdfrc");
2308 fi = fopen(config_file, "rb");
2310 msg("<error> Could not open %s", config_file);
2313 globalParams->parseFile(new GString(config_file), fi);
2317 void addGlobalFontDir(char*dirname)
2319 #ifdef HAVE_DIRENT_H
2320 msg("<notice> Adding %s to font directories", dirname);
2321 lastfontdir = strdup(dirname);
2322 DIR*dir = opendir(dirname);
2324 msg("<warning> Couldn't open directory %s\n", dirname);
2329 ent = readdir (dir);
2333 char*name = ent->d_name;
2339 if(!strncasecmp(&name[l-4], ".pfa", 4))
2341 if(!strncasecmp(&name[l-4], ".pfb", 4))
2343 if(!strncasecmp(&name[l-4], ".ttf", 4))
2347 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2348 strcpy(fontname, dirname);
2349 strcat(fontname, dirseparator());
2350 strcat(fontname, name);
2351 addGlobalFont(fontname);
2356 msg("<warning> No dirent.h- unable to add font dir %s", dir);
2360 void GFXOutputDev::preparePage(int pdfpage, int outputpage)
2366 this->pagebuflen = 1024;
2367 this->pages = (int*)malloc(this->pagebuflen*sizeof(int));
2368 memset(this->pages, -1, this->pagebuflen*sizeof(int));
2370 while(pdfpage >= this->pagebuflen)
2372 int oldlen = this->pagebuflen;
2373 this->pagebuflen+=1024;
2374 this->pages = (int*)realloc(this->pages, this->pagebuflen*sizeof(int));
2375 memset(&this->pages[oldlen], -1, (this->pagebuflen-oldlen)*sizeof(int));
2378 this->pages[pdfpage] = outputpage;
2379 if(pdfpage>this->pagepos)
2380 this->pagepos = pdfpage;
2387 delete globalParams;globalParams=0;
2388 Object::memCheck(stderr);