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"
30 #ifdef HAVE_SYS_STAT_H
33 #ifdef HAVE_FONTCONFIG
34 #include <fontconfig.h>
51 #include "OutputDev.h"
54 #include "CharCodeToUnicode.h"
55 #include "NameToUnicodeTable.h"
56 #include "GlobalParams.h"
57 #include "FoFiType1C.h"
58 #include "FoFiTrueType.h"
60 #include "GFXOutputDev.h"
62 //swftools header files
64 #include "../gfxdevice.h"
65 #include "../gfxtools.h"
66 #include "../gfxfont.h"
70 typedef struct _fontfile
77 static fontfile_t fonts[2048];
78 static int fontnum = 0;
82 static char* lastfontdir = 0;
88 {"Times-Roman", "n021003l"},
89 {"Times-Italic", "n021023l"},
90 {"Times-Bold", "n021004l"},
91 {"Times-BoldItalic", "n021024l"},
92 {"Helvetica", "n019003l"},
93 {"Helvetica-Oblique", "n019023l"},
94 {"Helvetica-Bold", "n019004l"},
95 {"Helvetica-BoldOblique", "n019024l"},
96 {"Courier", "n022003l"},
97 {"Courier-Oblique", "n022023l"},
98 {"Courier-Bold", "n022004l"},
99 {"Courier-BoldOblique", "n022024l"},
100 {"Symbol", "s050000l"},
101 {"ZapfDingbats", "d050000l"}};
103 GFXOutputState::GFXOutputState() {
105 this->textRender = 0;
108 GBool GFXOutputDev::interpretType3Chars()
113 typedef struct _drawnchar
131 chars = (drawnchar_t*)malloc(sizeof(drawnchar_t)*buf_size);
132 memset(chars, 0, sizeof(drawnchar_t)*buf_size);
137 free(chars);chars = 0;
144 chars = (drawnchar_t*)realloc(chars, sizeof(drawnchar_t)*buf_size);
148 void addChar(int charid, gfxcoord_t x, gfxcoord_t y, gfxcolor_t color)
151 chars[num_chars].x = x;
152 chars[num_chars].y = y;
153 chars[num_chars].color = color;
154 chars[num_chars].charid = charid;
158 static char*getFontID(GfxFont*font);
160 GFXOutputDev::GFXOutputDev(parameter_t*p)
163 this->textmodeinfo = 0;
167 this->type3active = 0;
170 this->substitutepos = 0;
171 this->type3Warning = 0;
172 this->user_movex = 0;
173 this->user_movey = 0;
176 this->user_clipx1 = 0;
177 this->user_clipy1 = 0;
178 this->user_clipx2 = 0;
179 this->user_clipy2 = 0;
180 this->current_text_stroke = 0;
181 this->current_text_clip = 0;
183 this->outer_clip_box = 0;
185 this->pagebuflen = 0;
187 this->transparencyGroup = 0;
189 this->forceType0Fonts=1;
190 this->config_use_fontconfig=1;
192 this->parameters = p;
194 /* configure device */
196 if(!strcmp(p->name,"forceType0Fonts")) {
197 this->forceType0Fonts = atoi(p->value);
198 } else if(!strcmp(p->name,"fontconfig")) {
199 this->config_use_fontconfig = atoi(p->value);
205 void GFXOutputDev::setDevice(gfxdevice_t*dev)
207 parameter_t*p = this->parameters;
209 /* TODO: get rid of this */
213 this->device->setparameter(this->device, p->name, p->value);
219 void GFXOutputDev::setMove(int x,int y)
221 this->user_movex = x;
222 this->user_movey = y;
225 void GFXOutputDev::setClip(int x1,int y1,int x2,int y2)
227 if(x2<x1) {int x3=x1;x1=x2;x2=x3;}
228 if(y2<y1) {int y3=y1;y1=y2;y2=y3;}
230 this->user_clipx1 = x1;
231 this->user_clipy1 = y1;
232 this->user_clipx2 = x2;
233 this->user_clipy2 = y2;
236 static char*getFontID(GfxFont*font)
238 Ref*ref = font->getID();
239 GString*gstr = font->getName();
240 char* fname = gstr==0?0:gstr->getCString();
243 sprintf(buf, "font-%d-%d", ref->num, ref->gen);
245 sprintf(buf, "%s-%d-%d", fname, ref->num, ref->gen);
250 static char*getFontName(GfxFont*font)
253 GString*gstr = font->getName();
254 char* fname = gstr==0?0:gstr->getCString();
258 sprintf(buf, "UFONT%d", r->num);
259 fontid = strdup(buf);
261 fontid = strdup(fname);
265 char* plus = strchr(fontid, '+');
266 if(plus && plus < &fontid[strlen(fontid)-1]) {
267 fontname = strdup(plus+1);
269 fontname = strdup(fontid);
275 static char mybuf[1024];
276 static char* gfxstate2str(GfxState *state)
280 bufpos+=sprintf(bufpos,"CTM[%.3f/%.3f/%.3f/%.3f/%.3f/%.3f] ",
287 if(state->getX1()!=0.0)
288 bufpos+=sprintf(bufpos,"X1-%.1f ",state->getX1());
289 if(state->getY1()!=0.0)
290 bufpos+=sprintf(bufpos,"Y1-%.1f ",state->getY1());
291 bufpos+=sprintf(bufpos,"X2-%.1f ",state->getX2());
292 bufpos+=sprintf(bufpos,"Y2-%.1f ",state->getY2());
293 bufpos+=sprintf(bufpos,"PW%.1f ",state->getPageWidth());
294 bufpos+=sprintf(bufpos,"PH%.1f ",state->getPageHeight());
295 /*bufpos+=sprintf(bufpos,"FC[%.1f/%.1f] ",
296 state->getFillColor()->c[0], state->getFillColor()->c[1]);
297 bufpos+=sprintf(bufpos,"SC[%.1f/%.1f] ",
298 state->getStrokeColor()->c[0], state->getFillColor()->c[1]);*/
299 /* bufpos+=sprintf(bufpos,"FC[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f]",
300 state->getFillColor()->c[0], state->getFillColor()->c[1],
301 state->getFillColor()->c[2], state->getFillColor()->c[3],
302 state->getFillColor()->c[4], state->getFillColor()->c[5],
303 state->getFillColor()->c[6], state->getFillColor()->c[7]);
304 bufpos+=sprintf(bufpos,"SC[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f]",
305 state->getStrokeColor()->c[0], state->getFillColor()->c[1],
306 state->getStrokeColor()->c[2], state->getFillColor()->c[3],
307 state->getStrokeColor()->c[4], state->getFillColor()->c[5],
308 state->getStrokeColor()->c[6], state->getFillColor()->c[7]);*/
309 state->getFillRGB(&rgb);
310 if(rgb.r || rgb.g || rgb.b)
311 bufpos+=sprintf(bufpos,"FR[%.1f/%.1f/%.1f] ", rgb.r,rgb.g,rgb.b);
312 state->getStrokeRGB(&rgb);
313 if(rgb.r || rgb.g || rgb.b)
314 bufpos+=sprintf(bufpos,"SR[%.1f/%.1f/%.1f] ", rgb.r,rgb.g,rgb.b);
315 if(state->getFillColorSpace()->getNComps()>1)
316 bufpos+=sprintf(bufpos,"CS[[%d]] ",state->getFillColorSpace()->getNComps());
317 if(state->getStrokeColorSpace()->getNComps()>1)
318 bufpos+=sprintf(bufpos,"SS[[%d]] ",state->getStrokeColorSpace()->getNComps());
319 if(state->getFillPattern())
320 bufpos+=sprintf(bufpos,"FP%08x ", state->getFillPattern());
321 if(state->getStrokePattern())
322 bufpos+=sprintf(bufpos,"SP%08x ", state->getStrokePattern());
324 if(state->getFillOpacity()!=1.0)
325 bufpos+=sprintf(bufpos,"FO%.1f ", state->getFillOpacity());
326 if(state->getStrokeOpacity()!=1.0)
327 bufpos+=sprintf(bufpos,"SO%.1f ", state->getStrokeOpacity());
329 bufpos+=sprintf(bufpos,"LW%.1f ", state->getLineWidth());
334 state->getLineDash(&dash, &length, &start);
338 bufpos+=sprintf(bufpos,"DASH%.1f[",start);
339 for(t=0;t<length;t++) {
340 bufpos+=sprintf(bufpos,"D%.1f",dash[t]);
342 bufpos+=sprintf(bufpos,"]");
345 if(state->getFlatness()!=1)
346 bufpos+=sprintf(bufpos,"F%d ", state->getFlatness());
347 if(state->getLineJoin()!=0)
348 bufpos+=sprintf(bufpos,"J%d ", state->getLineJoin());
349 if(state->getLineJoin()!=0)
350 bufpos+=sprintf(bufpos,"C%d ", state->getLineCap());
351 if(state->getLineJoin()!=0)
352 bufpos+=sprintf(bufpos,"ML%d ", state->getMiterLimit());
354 if(state->getFont() && getFontID(state->getFont()))
355 bufpos+=sprintf(bufpos,"F\"%s\" ",getFontID(state->getFont()));
356 bufpos+=sprintf(bufpos,"FS%.1f ", state->getFontSize());
357 bufpos+=sprintf(bufpos,"MAT[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f] ", state->getTextMat()[0],state->getTextMat()[1],state->getTextMat()[2],
358 state->getTextMat()[3],state->getTextMat()[4],state->getTextMat()[5]);
359 if(state->getCharSpace())
360 bufpos+=sprintf(bufpos,"CS%.5f ", state->getCharSpace());
361 if(state->getWordSpace())
362 bufpos+=sprintf(bufpos,"WS%.5f ", state->getWordSpace());
363 if(state->getHorizScaling()!=1.0)
364 bufpos+=sprintf(bufpos,"SC%.1f ", state->getHorizScaling());
365 if(state->getLeading())
366 bufpos+=sprintf(bufpos,"L%.1f ", state->getLeading());
368 bufpos+=sprintf(bufpos,"R%.1f ", state->getRise());
369 if(state->getRender())
370 bufpos+=sprintf(bufpos,"R%d ", state->getRender());
371 bufpos+=sprintf(bufpos,"P%08x ", state->getPath());
372 bufpos+=sprintf(bufpos,"CX%.1f ", state->getCurX());
373 bufpos+=sprintf(bufpos,"CY%.1f ", state->getCurY());
374 if(state->getLineX())
375 bufpos+=sprintf(bufpos,"LX%.1f ", state->getLineX());
376 if(state->getLineY())
377 bufpos+=sprintf(bufpos,"LY%.1f ", state->getLineY());
378 bufpos+=sprintf(bufpos," ");
382 static void dumpFontInfo(char*loglevel, GfxFont*font);
383 static int lastdumps[1024];
384 static int lastdumppos = 0;
389 static void showFontError(GfxFont*font, int nr)
393 for(t=0;t<lastdumppos;t++)
394 if(lastdumps[t] == r->num)
398 if(lastdumppos<sizeof(lastdumps)/sizeof(int))
399 lastdumps[lastdumppos++] = r->num;
401 msg("<warning> The following font caused problems:");
403 msg("<warning> The following font caused problems (substituting):");
405 msg("<warning> The following Type 3 Font will be rendered as bitmap:");
406 dumpFontInfo("<warning>", font);
409 static void dumpFontInfo(char*loglevel, GfxFont*font)
411 char* id = getFontID(font);
412 char* name = getFontName(font);
413 Ref* r=font->getID();
414 msg("%s=========== %s (ID:%d,%d) ==========\n", loglevel, name, r->num,r->gen);
416 GString*gstr = font->getTag();
418 msg("%s| Tag: %s\n", loglevel, id);
420 if(font->isCIDFont()) msg("%s| is CID font\n", loglevel);
422 GfxFontType type=font->getType();
424 case fontUnknownType:
425 msg("%s| Type: unknown\n",loglevel);
428 msg("%s| Type: 1\n",loglevel);
431 msg("%s| Type: 1C\n",loglevel);
434 msg("%s| Type: 3\n",loglevel);
437 msg("%s| Type: TrueType\n",loglevel);
440 msg("%s| Type: CIDType0\n",loglevel);
443 msg("%s| Type: CIDType0C\n",loglevel);
446 msg("%s| Type: CIDType2\n",loglevel);
451 GBool embedded = font->getEmbeddedFontID(&embRef);
453 if(font->getEmbeddedFontName()) {
454 embeddedName = font->getEmbeddedFontName()->getCString();
457 msg("%s| Embedded id: %s id: %d\n",loglevel, FIXNULL(embeddedName), embRef.num);
459 gstr = font->getExtFontFile();
461 msg("%s| External Font file: %s\n", loglevel, FIXNULL(gstr->getCString()));
463 // Get font descriptor flags.
464 if(font->isFixedWidth()) msg("%s| is fixed width\n", loglevel);
465 if(font->isSerif()) msg("%s| is serif\n", loglevel);
466 if(font->isSymbolic()) msg("%s| is symbolic\n", loglevel);
467 if(font->isItalic()) msg("%s| is italic\n", loglevel);
468 if(font->isBold()) msg("%s| is bold\n", loglevel);
474 //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");}
475 //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");}
478 void dump_outline(gfxline_t*line)
481 if(line->type == gfx_moveTo) {
482 msg("<debug> | moveTo %.2f %.2f", line->x,line->y);
483 } else if(line->type == gfx_lineTo) {
484 msg("<debug> | lineTo %.2f %.2f", line->x,line->y);
485 } else if(line->type == gfx_splineTo) {
486 msg("<debug> | splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y);
492 gfxline_t* gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user_movex, int user_movey)
494 int num = path->getNumSubpaths();
497 double lastx=0,lasty=0,posx=0,posy=0;
500 msg("<warning> empty path");
504 gfxdrawer_target_gfxline(&draw);
506 for(t = 0; t < num; t++) {
507 GfxSubpath *subpath = path->getSubpath(t);
508 int subnum = subpath->getNumPoints();
509 double bx=0,by=0,cx=0,cy=0;
511 for(s=0;s<subnum;s++) {
514 state->transform(subpath->getX(s),subpath->getY(s),&x,&y);
519 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
520 draw.lineTo(&draw, lastx, lasty);
522 draw.moveTo(&draw, x,y);
527 } else if(subpath->getCurve(s) && cpos==0) {
531 } else if(subpath->getCurve(s) && cpos==1) {
539 draw.lineTo(&draw, x,y);
541 gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y, 0.05);
548 /* fix non-closed lines */
549 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
550 draw.lineTo(&draw, lastx, lasty);
552 gfxline_t*result = (gfxline_t*)draw.result(&draw);
556 /*----------------------------------------------------------------------------
557 * Primitive Graphic routines
558 *----------------------------------------------------------------------------*/
560 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line)
562 int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
563 int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
564 double miterLimit = state->getMiterLimit();
565 double width = state->getTransformedLineWidth();
568 double opaq = state->getStrokeOpacity();
570 state->getFillRGB(&rgb);
572 state->getStrokeRGB(&rgb);
574 col.r = colToByte(rgb.r);
575 col.g = colToByte(rgb.g);
576 col.b = colToByte(rgb.b);
577 col.a = (unsigned char)(opaq*255);
579 gfx_capType capType = gfx_capRound;
580 if(lineCap == 0) capType = gfx_capButt;
581 else if(lineCap == 1) capType = gfx_capRound;
582 else if(lineCap == 2) capType = gfx_capSquare;
584 gfx_joinType joinType = gfx_joinRound;
585 if(lineJoin == 0) joinType = gfx_joinMiter;
586 else if(lineJoin == 1) joinType = gfx_joinRound;
587 else if(lineJoin == 2) joinType = gfx_joinBevel;
590 double dashphase = 0;
592 state->getLineDash(&ldash, &dashnum, &dashphase);
596 if(dashnum && ldash) {
597 float * dash = (float*)malloc(sizeof(float)*(dashnum+1));
601 msg("<trace> %d dashes", dashnum);
602 msg("<trace> | phase: %f", dashphase);
603 for(t=0;t<dashnum;t++) {
605 msg("<trace> | d%-3d: %f", t, ldash[t]);
608 if(getLogLevel() >= LOGLEVEL_TRACE) {
612 line2 = gfxtool_dash_line(line, dash, dashphase);
615 msg("<trace> After dashing:");
618 if(getLogLevel() >= LOGLEVEL_TRACE) {
619 msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x\n",
621 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
622 lineCap==0?"butt": (lineJoin==1?"round":"square"),
624 col.r,col.g,col.b,col.a
629 //swfoutput_drawgfxline(output, line, width, &col, capType, joinType, miterLimit);
630 device->stroke(device, line, width, &col, capType, joinType, miterLimit);
636 gfxcolor_t getFillColor(GfxState * state)
639 double opaq = state->getFillOpacity();
640 state->getFillRGB(&rgb);
642 col.r = colToByte(rgb.r);
643 col.g = colToByte(rgb.g);
644 col.b = colToByte(rgb.b);
645 col.a = (unsigned char)(opaq*255);
649 void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line)
651 gfxcolor_t col = getFillColor(state);
653 if(getLogLevel() >= LOGLEVEL_TRACE) {
654 msg("<trace> fill %02x%02x%02x%02x\n", col.r, col.g, col.b, col.a);
658 device->fill(device, line, &col);
661 void GFXOutputDev::clip(GfxState *state)
663 GfxPath * path = state->getPath();
664 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
665 clipToGfxLine(state, line);
669 void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line)
671 if(getLogLevel() >= LOGLEVEL_TRACE) {
672 msg("<trace> clip\n");
676 device->startclip(device, line);
677 states[statepos].clipping++;
679 void GFXOutputDev::eoClip(GfxState *state)
681 GfxPath * path = state->getPath();
682 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
684 if(getLogLevel() >= LOGLEVEL_TRACE) {
685 msg("<trace> eoclip\n");
689 device->startclip(device, line);
690 states[statepos].clipping++;
694 void GFXOutputDev::endframe()
697 device->endclip(device);
701 device->endpage(device);
704 void GFXOutputDev::finish()
708 device->endclip(device);
714 GFXOutputDev::~GFXOutputDev()
719 free(this->pages); this->pages = 0;
722 fontlist_t*l = this->fontlist;
724 fontlist_t*next = l->next;
726 gfxfont_free(l->font);
727 free(l->filename);l->filename=0;
733 GBool GFXOutputDev::upsideDown()
737 GBool GFXOutputDev::useDrawChar()
742 char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
743 "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
745 #define RENDER_FILL 0
746 #define RENDER_STROKE 1
747 #define RENDER_FILLSTROKE 2
748 #define RENDER_INVISIBLE 3
749 #define RENDER_CLIP 4
751 static char tmp_printstr[4096];
752 char* makeStringPrintable(char*str)
754 int len = strlen(str);
769 tmp_printstr[len++] = '.';
770 tmp_printstr[len++] = '.';
771 tmp_printstr[len++] = '.';
773 tmp_printstr[len] = 0;
778 int getGfxCharID(gfxfont_t*font, int charnr, char *charname, int u)
783 /* find out char name from unicode index
784 TODO: should be precomputed
786 for(t=0;t<sizeof(nameToUnicodeTab)/sizeof(nameToUnicodeTab[0]);t++) {
787 if(nameToUnicodeTab[t].u == u) {
788 uniname = nameToUnicodeTab[t].name;
796 for(t=0;t<font->num_glyphs;t++) {
797 if(font->glyphs[t].name && !strcmp(font->glyphs[t].name,charname)) {
798 msg("<debug> Char [%d,>%s<,%d] maps to %d\n", charnr, charname, u, t);
802 /* if we didn't find the character, maybe
803 we can find the capitalized version */
804 for(t=0;t<font->num_glyphs;t++) {
805 if(font->glyphs[t].name && !strcasecmp(font->glyphs[t].name,charname)) {
806 msg("<debug> Char [%d,>>%s<<,%d] maps to %d\n", charnr, charname, u, t);
814 for(t=0;t<font->num_glyphs;t++) {
815 if(font->glyphs[t].name && !strcmp(font->glyphs[t].name,uniname)) {
816 msg("<debug> Char [%d,%s,>%d(%s)<] maps to %d\n", charnr, charname, u, uniname, t);
820 /* if we didn't find the character, maybe
821 we can find the capitalized version */
822 for(t=0;t<font->num_glyphs;t++) {
823 if(font->glyphs[t].name && !strcasecmp(font->glyphs[t].name,uniname)) {
824 msg("<debug> Char [%d,%s,>>%d(%s)<<] maps to %d\n", charnr, charname, u, uniname, t);
830 /* try to use the unicode id */
831 if(u>=0 && u<font->max_unicode && font->unicode2glyph[u]>=0) {
832 msg("<debug> Char [%d,%s,>%d<] maps to %d\n", charnr, charname, u, font->unicode2glyph[u]);
833 return font->unicode2glyph[u];
836 if(charnr>=0 && charnr<font->num_glyphs) {
837 msg("<debug> Char [>%d<,%s,%d] maps to %d\n", charnr, charname, u, charnr);
845 void GFXOutputDev::beginString(GfxState *state, GString *s)
847 int render = state->getRender();
848 if(current_text_stroke) {
849 msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
852 msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
853 double m11,m21,m12,m22;
854 // msg("<debug> %s beginstring \"%s\"\n", gfxstate2str(state), s->getCString());
855 state->getFontTransMat(&m11, &m12, &m21, &m22);
856 m11 *= state->getHorizScaling();
857 m21 *= state->getHorizScaling();
859 this->current_font_matrix.m00 = m11 / 1024.0;
860 this->current_font_matrix.m01 = m12 / 1024.0;
861 this->current_font_matrix.m10 = -m21 / 1024.0;
862 this->current_font_matrix.m11 = -m22 / 1024.0;
863 this->current_font_matrix.tx = 0;
864 this->current_font_matrix.ty = 0;
866 gfxmatrix_t m = this->current_font_matrix;
868 /*if(render != 3 && render != 0)
869 msg("<warning> Text rendering mode %d (%s) not fully supported yet (for text \"%s\")", render, renderModeDesc[render&7], makeStringPrintable(s->getCString()));*/
870 states[statepos].textRender = render;
873 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
874 double dx, double dy,
875 double originX, double originY,
876 CharCode c, int nBytes, Unicode *_u, int uLen)
880 int render = state->getRender();
881 // check for invisible text -- this is used by Acrobat Capture
883 msg("<debug> Ignoring invisible text: char %d at %f,%f", c, x, y);
887 if(states[statepos].textRender != render)
888 msg("<error> Internal error: drawChar.render!=beginString.render");
890 gfxcolor_t col = getFillColor(state);
892 Gushort *CIDToGIDMap = 0;
893 GfxFont*font = state->getFont();
895 if(font->getType() == fontType3) {
896 /* type 3 chars are passed as graphics */
897 msg("<debug> type3 char at %f/%f", x, y);
907 if(font->isCIDFont()) {
908 GfxCIDFont*cfont = (GfxCIDFont*)font;
910 if(font->getType() == fontCIDType2)
911 CIDToGIDMap = cfont->getCIDToGID();
914 font8 = (Gfx8BitFont*)font;
915 char**enc=font8->getEncoding();
919 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);
922 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);
928 charid = getGfxCharID(current_gfxfont, c, name, u);
930 charid = getGfxCharID(current_gfxfont, c, name, -1);
933 /* multiple unicodes- should usually map to a ligature.
934 if the ligature doesn't exist, we need to draw
935 the characters one-by-one. */
937 msg("<warning> ligature %d missing in font %s\n", c, current_gfxfont->id);
938 for(t=0;t<uLen;t++) {
939 drawChar(state, x, y, dx, dy, originX, originY, c, nBytes, _u+t, 1);
945 msg("<warning> Didn't find character '%s' (c=%d,u=%d) in current charset (%s, %d characters)",
946 FIXNULL(name),c, u, FIXNULL((char*)current_gfxfont->id), current_gfxfont->num_glyphs);
950 gfxmatrix_t m = this->current_font_matrix;
951 state->transform(x, y, &m.tx, &m.ty);
952 m.tx += user_movex + clipmovex;
953 m.ty += user_movey + clipmovey;
955 if(render == RENDER_FILL) {
956 device->drawchar(device, current_gfxfont, charid, &col, &m);
958 msg("<debug> Drawing glyph %d as shape", charid);
960 msg("<notice> Some texts will be rendered as shape");
963 gfxline_t*glyph = current_gfxfont->glyphs[charid].line;
964 gfxline_t*tglyph = gfxline_clone(glyph);
965 gfxline_transform(tglyph, &m);
966 if((render&3) != RENDER_INVISIBLE) {
967 gfxline_t*add = gfxline_clone(tglyph);
968 current_text_stroke = gfxline_append(current_text_stroke, add);
970 if(render&RENDER_CLIP) {
971 gfxline_t*add = gfxline_clone(tglyph);
972 current_text_clip = gfxline_append(current_text_clip, add);
974 gfxline_free(tglyph);
978 void GFXOutputDev::endString(GfxState *state)
980 int render = state->getRender();
981 msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
982 if(states[statepos].textRender != render)
983 msg("<error> Internal error: drawChar.render!=beginString.render");
985 if(current_text_stroke) {
986 /* fillstroke and stroke text rendering objects we can process right
987 now (as there may be texts of other rendering modes in this
988 text object)- clipping objects have to wait until endTextObject,
990 device->setparameter(device, "mark","TXT");
991 if((render&3) == RENDER_FILL) {
992 fillGfxLine(state, current_text_stroke);
993 gfxline_free(current_text_stroke);
994 current_text_stroke = 0;
995 } else if((render&3) == RENDER_FILLSTROKE) {
996 fillGfxLine(state, current_text_stroke);
997 strokeGfxline(state, current_text_stroke);
998 gfxline_free(current_text_stroke);
999 current_text_stroke = 0;
1000 } else if((render&3) == RENDER_STROKE) {
1001 strokeGfxline(state, current_text_stroke);
1002 gfxline_free(current_text_stroke);
1003 current_text_stroke = 0;
1005 device->setparameter(device, "mark","");
1009 void GFXOutputDev::endTextObject(GfxState *state)
1011 int render = state->getRender();
1012 msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1013 if(states[statepos].textRender != render)
1014 msg("<error> Internal error: drawChar.render!=beginString.render");
1016 if(current_text_clip) {
1017 device->setparameter(device, "mark","TXT");
1018 clipToGfxLine(state, current_text_clip);
1019 device->setparameter(device, "mark","");
1020 gfxline_free(current_text_clip);
1021 current_text_clip = 0;
1025 /* the logic seems to be as following:
1026 first, beginType3Char is called, with the charcode and the coordinates.
1027 if this function returns true, it already knew about the char and has now drawn it.
1028 if the function returns false, it's a new char, and type3D1 is called with some parameters-
1029 the all draw operations until endType3Char are part of the char (which in this moment is
1030 at the position first passed to beginType3Char). the char ends with endType3Char.
1032 The drawing operations between beginType3Char and endType3Char are somewhat different to
1033 the normal ones. For example, the fillcolor equals the stroke color.
1036 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode code, Unicode *u, int uLen)
1038 msg("<debug> beginType3Char %d, %08x, %d", code, *u, uLen);
1040 /* the character itself is going to be passed using the draw functions */
1041 return gFalse; /* gTrue= is_in_cache? */
1044 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1045 msg("<debug> type3D0 width=%f height=%f", wx, wy);
1047 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1048 msg("<debug> type3D1 width=%f height=%f bbox=(%f,%f,%f,%f)", wx, wy,
1052 void GFXOutputDev::endType3Char(GfxState *state)
1055 msg("<debug> endType3Char");
1058 void GFXOutputDev::startFrame(int width, int height)
1060 if(outer_clip_box) {
1061 device->endclip(device);
1065 device->startpage(device, width, height);
1068 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
1070 this->currentpage = pageNum;
1072 int rot = doc->getPageRotate(1);
1075 gfxline_t clippath[5];
1077 white.r = white.g = white.b = white.a = 255;
1079 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1080 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1081 Use CropBox, not MediaBox, as page size
1088 state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1089 state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1091 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1092 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1094 this->clipmovex = -(int)x1;
1095 this->clipmovey = -(int)y1;
1097 /* apply user clip box */
1098 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1099 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1100 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1101 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1102 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1103 msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1106 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1108 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 + clipmovex, user_movey + clipmovey);
1110 msg("<verbose> page is rotated %d degrees\n", rot);
1112 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1113 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1114 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1115 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1116 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1117 device->startclip(device, clippath); outer_clip_box = 1;
1118 device->fill(device, clippath, &white);
1121 void GFXOutputDev::drawLink(Link *link, Catalog *catalog)
1123 double x1, y1, x2, y2, w;
1124 gfxline_t points[5];
1127 msg("<debug> drawlink\n");
1129 link->getRect(&x1, &y1, &x2, &y2);
1130 cvtUserToDev(x1, y1, &x, &y);
1131 points[0].type = gfx_moveTo;
1132 points[0].x = points[4].x = x + user_movex + clipmovex;
1133 points[0].y = points[4].y = y + user_movey + clipmovey;
1134 points[0].next = &points[1];
1135 cvtUserToDev(x2, y1, &x, &y);
1136 points[1].type = gfx_lineTo;
1137 points[1].x = x + user_movex + clipmovex;
1138 points[1].y = y + user_movey + clipmovey;
1139 points[1].next = &points[2];
1140 cvtUserToDev(x2, y2, &x, &y);
1141 points[2].type = gfx_lineTo;
1142 points[2].x = x + user_movex + clipmovex;
1143 points[2].y = y + user_movey + clipmovey;
1144 points[2].next = &points[3];
1145 cvtUserToDev(x1, y2, &x, &y);
1146 points[3].type = gfx_lineTo;
1147 points[3].x = x + user_movex + clipmovex;
1148 points[3].y = y + user_movey + clipmovey;
1149 points[3].next = &points[4];
1150 cvtUserToDev(x1, y1, &x, &y);
1151 points[4].type = gfx_lineTo;
1152 points[4].x = x + user_movex + clipmovex;
1153 points[4].y = y + user_movey + clipmovey;
1156 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f\n",
1157 points[0].x, points[0].y,
1158 points[1].x, points[1].y,
1159 points[2].x, points[2].y,
1160 points[3].x, points[3].y);
1162 LinkAction*action=link->getAction();
1168 msg("<trace> drawlink action=%d\n", action->getKind());
1169 switch(action->getKind())
1173 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1174 LinkDest *dest=NULL;
1175 if (ha->getDest()==NULL)
1176 dest=catalog->findDest(ha->getNamedDest());
1177 else dest=ha->getDest();
1179 if (dest->isPageRef()){
1180 Ref pageref=dest->getPageRef();
1181 page=catalog->findPage(pageref.num,pageref.gen);
1183 else page=dest->getPageNum();
1184 sprintf(buf, "%d", page);
1191 LinkGoToR*l = (LinkGoToR*)action;
1192 GString*g = l->getFileName();
1194 s = strdup(g->getCString());
1196 /* if the GoToR link has no filename, then
1197 try to find a refernce in the *local*
1199 GString*g = l->getNamedDest();
1201 s = strdup(g->getCString());
1207 LinkNamed*l = (LinkNamed*)action;
1208 GString*name = l->getName();
1210 s = strdup(name->lowerCase()->getCString());
1211 named = name->getCString();
1214 if(strstr(s, "next") || strstr(s, "forward"))
1216 page = currentpage + 1;
1218 else if(strstr(s, "prev") || strstr(s, "back"))
1220 page = currentpage - 1;
1222 else if(strstr(s, "last") || strstr(s, "end"))
1224 if(pages && pagepos>0)
1225 page = pages[pagepos-1];
1227 else if(strstr(s, "first") || strstr(s, "top"))
1235 case actionLaunch: {
1237 LinkLaunch*l = (LinkLaunch*)action;
1238 GString * str = new GString(l->getFileName());
1239 GString * params = l->getParams();
1241 str->append(params);
1242 s = strdup(str->getCString());
1249 LinkURI*l = (LinkURI*)action;
1250 GString*g = l->getURI();
1252 url = g->getCString();
1257 case actionUnknown: {
1259 LinkUnknown*l = (LinkUnknown*)action;
1264 msg("<error> Unknown link type!\n");
1269 if(!s) s = strdup("-?-");
1271 msg("<trace> drawlink s=%s\n", s);
1273 if(!linkinfo && (page || s))
1275 msg("<notice> File contains links");
1283 for(t=1;t<=pagepos;t++) {
1284 if(pages[t]==page) {
1293 sprintf(buf, "page%d", lpage);
1294 device->drawlink(device, points, buf);
1298 device->drawlink(device, points, s);
1301 msg("<verbose> \"%s\" link to \"%s\" (%d)\n", type, FIXNULL(s), page);
1305 void GFXOutputDev::saveState(GfxState *state) {
1306 msg("<trace> saveState\n");
1309 msg("<error> Too many nested states in pdf.");
1313 states[statepos].clipping = 0; //? shouldn't this be the current value?
1314 states[statepos].textRender = states[statepos-1].textRender;
1317 void GFXOutputDev::restoreState(GfxState *state) {
1318 msg("<trace> restoreState\n");
1320 while(states[statepos].clipping) {
1321 device->endclip(device);
1322 states[statepos].clipping--;
1327 char* GFXOutputDev::searchFont(char*name)
1331 int is_standard_font = 0;
1333 msg("<verbose> SearchFont(%s)", name);
1335 /* see if it is a pdf standard font */
1336 for(i=0;i<sizeof(pdf2t1map)/sizeof(mapping);i++)
1338 if(!strcmp(name, pdf2t1map[i].pdffont))
1340 name = pdf2t1map[i].filename;
1341 is_standard_font = 1;
1345 /* look in all font files */
1346 for(i=0;i<fontnum;i++)
1348 if(strstr(fonts[i].filename, name))
1350 if(!fonts[i].used) {
1353 if(!is_standard_font)
1354 msg("<notice> Using %s for %s", fonts[i].filename, name);
1356 return strdup(fonts[i].filename);
1362 void GFXOutputDev::updateLineWidth(GfxState *state)
1364 double width = state->getTransformedLineWidth();
1365 //swfoutput_setlinewidth(&device, width);
1368 void GFXOutputDev::updateLineCap(GfxState *state)
1370 int c = state->getLineCap();
1373 void GFXOutputDev::updateLineJoin(GfxState *state)
1375 int j = state->getLineJoin();
1378 void GFXOutputDev::updateFillColor(GfxState *state)
1381 double opaq = state->getFillOpacity();
1382 state->getFillRGB(&rgb);
1384 void GFXOutputDev::updateFillOpacity(GfxState *state)
1387 double opaq = state->getFillOpacity();
1388 state->getFillRGB(&rgb);
1391 void GFXOutputDev::updateStrokeColor(GfxState *state)
1394 double opaq = state->getStrokeOpacity();
1395 state->getStrokeRGB(&rgb);
1398 void FoFiWrite(void *stream, char *data, int len)
1400 int ret = fwrite(data, len, 1, (FILE*)stream);
1403 char*GFXOutputDev::writeEmbeddedFontToFile(XRef*ref, GfxFont*font)
1405 char*tmpFileName = NULL;
1411 Object refObj, strObj;
1413 tmpFileName = mktmpname(namebuf);
1416 ret = font->getEmbeddedFontID(&embRef);
1418 msg("<verbose> Didn't get embedded font id");
1419 /* not embedded- the caller should now search the font
1420 directories for this font */
1424 f = fopen(tmpFileName, "wb");
1426 msg("<error> Couldn't create temporary Type 1 font file");
1430 /*if(font->isCIDFont()) {
1431 GfxCIDFont* cidFont = (GfxCIDFont *)font;
1432 GString c = cidFont->getCollection();
1433 msg("<notice> Collection: %s", c.getCString());
1436 //if (font->getType() == fontType1C) {
1437 if (0) { //font->getType() == fontType1C) {
1438 if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
1440 msg("<error> Couldn't read embedded font file");
1443 FoFiType1C *cvt = FoFiType1C::make(fontBuf, fontLen);
1445 cvt->convertToType1(NULL, gTrue, FoFiWrite, f);
1446 //cvt->convertToCIDType0("test", f);
1447 //cvt->convertToType0("test", f);
1450 } else if(font->getType() == fontTrueType) {
1451 msg("<verbose> writing font using TrueTypeFontFile::writeTTF");
1452 if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
1454 msg("<error> Couldn't read embedded font file");
1457 FoFiTrueType *cvt = FoFiTrueType::make(fontBuf, fontLen);
1458 cvt->writeTTF(FoFiWrite, f);
1462 font->getEmbeddedFontID(&embRef);
1463 refObj.initRef(embRef.num, embRef.gen);
1464 refObj.fetch(ref, &strObj);
1466 strObj.streamReset();
1471 f4[t] = strObj.streamGetChar();
1472 f4c[t] = (char)f4[t];
1477 if(!strncmp(f4c, "true", 4)) {
1478 /* some weird TTF fonts don't start with 0,1,0,0 but with "true".
1479 Change this on the fly */
1480 f4[0] = f4[2] = f4[3] = 0;
1488 while ((c = strObj.streamGetChar()) != EOF) {
1492 strObj.streamClose();
1497 return strdup(tmpFileName);
1500 char* GFXOutputDev::searchForSuitableFont(GfxFont*gfxFont)
1502 char*name = getFontName(gfxFont);
1506 if(!this->config_use_fontconfig)
1509 #ifdef HAVE_FONTCONFIG
1510 FcPattern *pattern, *match;
1514 static int fcinitcalled = false;
1516 msg("<debug> searchForSuitableFont(%s)", name);
1518 // call init ony once
1519 if (!fcinitcalled) {
1520 msg("<debug> Initializing FontConfig...");
1521 fcinitcalled = true;
1523 msg("<debug> FontConfig Initialization failed. Disabling.");
1524 config_use_fontconfig = 0;
1527 msg("<debug> ...initialized FontConfig");
1530 msg("<debug> FontConfig: Create \"%s\" Family Pattern", name);
1531 pattern = FcPatternBuild(NULL, FC_FAMILY, FcTypeString, name, NULL);
1532 if (gfxFont->isItalic()) // check for italic
1533 msg("<debug> FontConfig: Adding Italic Slant");
1534 FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
1535 if (gfxFont->isBold()) // check for bold
1536 msg("<debug> FontConfig: Adding Bold Weight");
1537 FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
1539 msg("<debug> FontConfig: Try to match...");
1540 // configure and match using the original font name
1541 FcConfigSubstitute(0, pattern, FcMatchPattern);
1542 FcDefaultSubstitute(pattern);
1543 match = FcFontMatch(0, pattern, &result);
1545 if (FcPatternGetString(match, "family", 0, &v) == FcResultMatch) {
1546 msg("<debug> FontConfig: family=%s", (char*)v);
1547 // if we get an exact match
1548 if (strcmp((char *)v, name) == 0) {
1549 if (FcPatternGetString(match, "file", 0, &v) == FcResultMatch) {
1550 filename = strdup((char*)v); // mem leak
1551 char *nfn = strrchr(filename, '/');
1552 if(nfn) fontname = strdup(nfn+1);
1553 else fontname = filename;
1555 msg("<debug> FontConfig: Returning \"%s\"", fontname);
1557 // initialize patterns
1558 FcPatternDestroy(pattern);
1559 FcPatternDestroy(match);
1561 // now match against serif etc.
1562 if (gfxFont->isSerif()) {
1563 msg("<debug> FontConfig: Create Serif Family Pattern");
1564 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "serif", NULL);
1565 } else if (gfxFont->isFixedWidth()) {
1566 msg("<debug> FontConfig: Create Monospace Family Pattern");
1567 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "monospace", NULL);
1569 msg("<debug> FontConfig: Create Sans Family Pattern");
1570 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "sans", NULL);
1574 if (gfxFont->isItalic()) {
1575 msg("<debug> FontConfig: Adding Italic Slant");
1576 int bb = FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
1579 if (gfxFont->isBold()) {
1580 msg("<debug> FontConfig: Adding Bold Weight");
1581 int bb = FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
1584 msg("<debug> FontConfig: Try to match... (2)");
1585 // configure and match using serif etc
1586 FcConfigSubstitute (0, pattern, FcMatchPattern);
1587 FcDefaultSubstitute (pattern);
1588 match = FcFontMatch (0, pattern, &result);
1590 if (FcPatternGetString(match, "file", 0, &v) == FcResultMatch) {
1591 filename = strdup((char*)v); // mem leak
1592 char *nfn = strrchr(filename, '/');
1593 if(nfn) fontname = strdup(nfn+1);
1594 else fontname = filename;
1596 msg("<debug> FontConfig: Returning \"%s\"", fontname);
1600 //printf("FONTCONFIG: pattern");
1601 //FcPatternPrint(pattern);
1602 //printf("FONTCONFIG: match");
1603 //FcPatternPrint(match);
1605 FcPatternDestroy(pattern);
1606 FcPatternDestroy(match);
1608 pdfswf_addfont(filename);
1615 char* GFXOutputDev::substituteFont(GfxFont*gfxFont, char* oldname)
1617 char*fontname = 0, *filename = 0;
1618 msg("<notice> substituteFont(%s)", oldname);
1620 if(!(fontname = searchForSuitableFont(gfxFont))) {
1621 fontname = "Times-Roman";
1623 filename = searchFont(fontname);
1625 msg("<error> Couldn't find font %s- did you install the default fonts?", fontname);
1629 if(substitutepos>=sizeof(substitutesource)/sizeof(char*)) {
1630 msg("<fatal> Too many fonts in file.");
1634 substitutesource[substitutepos] = strdup(oldname); //mem leak
1635 substitutetarget[substitutepos] = fontname;
1636 msg("<notice> substituting %s -> %s", FIXNULL(oldname), FIXNULL(fontname));
1639 return strdup(filename); //mem leak
1642 void unlinkfont(char* filename)
1649 if(!strncmp(&filename[l-4],".afm",4)) {
1650 memcpy(&filename[l-4],".pfb",4);
1652 memcpy(&filename[l-4],".pfa",4);
1654 memcpy(&filename[l-4],".afm",4);
1657 if(!strncmp(&filename[l-4],".pfa",4)) {
1658 memcpy(&filename[l-4],".afm",4);
1660 memcpy(&filename[l-4],".pfa",4);
1663 if(!strncmp(&filename[l-4],".pfb",4)) {
1664 memcpy(&filename[l-4],".afm",4);
1666 memcpy(&filename[l-4],".pfb",4);
1671 void GFXOutputDev::setXRef(PDFDoc*doc, XRef *xref)
1677 int GFXOutputDev::setGfxFont(char*id, char*name, char*filename, double maxSize)
1680 fontlist_t*last=0,*l = this->fontlist;
1683 msg("<error> Internal Error: FontID is null");
1685 /* TODO: should this be part of the state? */
1688 if(!strcmp(l->font->id, id)) {
1689 current_gfxfont = l->font;
1691 device->addfont(device, current_gfxfont);
1696 if(!filename) return 0;
1698 /* A font size of e.g. 9 means the font will be scaled down by
1699 1024 and scaled up by 9. So to have a maximum error of 1/20px,
1700 we have to divide 0.05 by (fontsize/1024)
1702 double quality = (1024 * 0.05) / maxSize;
1704 msg("<verbose> Loading %s...", filename);
1705 font = gfxfont_load(id, filename, quality);
1707 msg("<verbose> Couldn't load Font %s (%s)", filename, id);
1710 msg("<verbose> Font %s (%s) loaded successfully", filename, id);
1714 l->filename = strdup(filename);
1716 current_gfxfont = l->font;
1722 device->addfont(device, 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 + clipmovex; y1 += user_movey + clipmovey;
2037 state->transform(0, 0, &x2, &y2); x2 += user_movex + clipmovex; y2 += user_movey + clipmovey;
2038 state->transform(1, 0, &x3, &y3); x3 += user_movex + clipmovex; y3 += user_movey + clipmovey;
2039 state->transform(1, 1, &x4, &y4); x4 += user_movex + clipmovex; y4 += user_movey + clipmovey;
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,
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)
2224 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
2225 colorMap?"colorMap":"no colorMap",
2226 maskColors?"maskColors":"no maskColors",
2229 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2230 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2231 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2234 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2235 int width, int height,
2236 GfxImageColorMap *colorMap,
2237 Stream *maskStr, int maskWidth, int maskHeight,
2242 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2243 colorMap?"colorMap":"no colorMap",
2244 maskWidth, maskHeight);
2246 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2247 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2248 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2251 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2252 int width, int height,
2253 GfxImageColorMap *colorMap,
2255 int maskWidth, int maskHeight,
2256 GfxImageColorMap *maskColorMap)
2260 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2261 colorMap?"colorMap":"no colorMap",
2262 maskWidth, maskHeight);
2264 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2265 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2266 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2269 void GFXOutputDev::stroke(GfxState *state)
2274 GfxPath * path = state->getPath();
2275 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2276 strokeGfxline(state, line);
2280 void GFXOutputDev::fill(GfxState *state)
2282 GfxPath * path = state->getPath();
2283 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2284 fillGfxLine(state, line);
2288 void GFXOutputDev::eoFill(GfxState *state)
2290 GfxPath * path = state->getPath();
2291 gfxcolor_t col = getFillColor(state);
2293 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2295 if(getLogLevel() >= LOGLEVEL_TRACE) {
2296 msg("<trace> eofill\n");
2300 device->fill(device, line, &col);
2305 static char* dirseparator()
2314 void addGlobalFont(char*filename)
2317 memset(&f, 0, sizeof(fontfile_t));
2318 f.filename = filename;
2319 if(fontnum < sizeof(fonts)/sizeof(fonts[0])) {
2320 msg("<verbose> Adding font \"%s\".", filename);
2321 fonts[fontnum++] = f;
2323 msg("<error> Too many external fonts. Not adding font file \"%s\".", filename);
2327 void addGlobalLanguageDir(char*dir)
2330 globalParams = new GlobalParams("");
2332 msg("<notice> Adding %s to language pack directories", dir);
2336 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2337 strcpy(config_file, dir);
2338 strcat(config_file, dirseparator());
2339 strcat(config_file, "add-to-xpdfrc");
2341 fi = fopen(config_file, "rb");
2343 msg("<error> Could not open %s", config_file);
2346 globalParams->parseFile(new GString(config_file), fi);
2350 void addGlobalFontDir(char*dirname)
2352 #ifdef HAVE_DIRENT_H
2353 msg("<notice> Adding %s to font directories", dirname);
2354 lastfontdir = strdup(dirname);
2355 DIR*dir = opendir(dirname);
2357 msg("<warning> Couldn't open directory %s\n", dirname);
2362 ent = readdir (dir);
2366 char*name = ent->d_name;
2372 if(!strncasecmp(&name[l-4], ".pfa", 4))
2374 if(!strncasecmp(&name[l-4], ".pfb", 4))
2376 if(!strncasecmp(&name[l-4], ".ttf", 4))
2380 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2381 strcpy(fontname, dirname);
2382 strcat(fontname, dirseparator());
2383 strcat(fontname, name);
2384 addGlobalFont(fontname);
2389 msg("<warning> No dirent.h- unable to add font dir %s", dir);
2393 void GFXOutputDev::preparePage(int pdfpage, int outputpage)
2399 this->pagebuflen = 1024;
2400 this->pages = (int*)malloc(this->pagebuflen*sizeof(int));
2401 memset(this->pages, -1, this->pagebuflen*sizeof(int));
2403 while(pdfpage >= this->pagebuflen)
2405 int oldlen = this->pagebuflen;
2406 this->pagebuflen+=1024;
2407 this->pages = (int*)realloc(this->pages, this->pagebuflen*sizeof(int));
2408 memset(&this->pages[oldlen], -1, (this->pagebuflen-oldlen)*sizeof(int));
2411 this->pages[pdfpage] = outputpage;
2412 if(pdfpage>this->pagepos)
2413 this->pagepos = pdfpage;
2416 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2417 GfxColorSpace *blendingColorSpace,
2418 GBool isolated, GBool knockout,
2421 char*colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2422 msg("<verbose> beginTransparencyGroup %f/%f/%f/%f %s isolated=%d knockout=%d forsoftmask=%d", bbox[0],bbox[1],bbox[2],bbox[3], colormodename, isolated, knockout, forSoftMask);
2423 createsoftmask = forSoftMask;
2424 transparencyGroup = !forSoftMask;
2426 if(transparencyGroup) {
2427 state->setFillOpacity(0.0);
2432 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2434 msg("<verbose> endTransparencyGroup");
2436 transparencyGroup = 0;
2439 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2441 msg("<verbose> paintTransparencyGroup");
2445 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2447 msg("<verbose> setSoftMask %f/%f/%f/%f alpha=%d backdrop=%02x%02x%02x",
2448 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2451 void GFXOutputDev::clearSoftMask(GfxState *state)
2453 msg("<verbose> clearSoftMask");
2460 delete globalParams;globalParams=0;
2461 Object::memCheck(stderr);