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 */
26 #include "../../config.h"
31 #ifdef HAVE_SYS_STAT_H
34 #ifdef HAVE_FONTCONFIG
35 #include <fontconfig.h>
52 #include "OutputDev.h"
55 #include "CharCodeToUnicode.h"
56 #include "NameToUnicodeTable.h"
57 #include "GlobalParams.h"
58 #include "FoFiType1C.h"
59 #include "FoFiTrueType.h"
61 #include "GFXOutputDev.h"
63 //swftools header files
65 #include "../gfxdevice.h"
66 #include "../gfxtools.h"
67 #include "../gfxfont.h"
68 #include "../devices/record.h"
69 #include "../devices/ops.h"
70 #include "../devices/arts.h"
71 #include "../devices/render.h"
77 typedef struct _fontfile
84 static fontfile_t fonts[2048];
85 static int fontnum = 0;
89 static char* lastfontdir = 0;
100 {"Times-Roman", "n021003l", n021003l_afm, n021003l_afm_len, n021003l_pfb, n021003l_pfb_len},
101 {"Times-Italic", "n021023l", n021023l_afm, n021023l_afm_len, n021023l_pfb, n021023l_pfb_len},
102 {"Times-Bold", "n021004l", n021004l_afm, n021004l_afm_len, n021004l_pfb, n021004l_pfb_len},
103 {"Times-BoldItalic", "n021024l", n021024l_afm, n021024l_afm_len, n021024l_pfb, n021024l_pfb_len},
104 {"Helvetica", "n019003l", n019003l_afm, n019003l_afm_len, n019003l_pfb, n019003l_pfb_len},
105 {"Helvetica-Oblique", "n019023l", n019023l_afm, n019023l_afm_len, n019023l_pfb, n019023l_pfb_len},
106 {"Helvetica-Bold", "n019004l", n019004l_afm, n019004l_afm_len, n019004l_pfb, n019004l_pfb_len},
107 {"Helvetica-BoldOblique", "n019024l", n019024l_afm, n019024l_afm_len, n019024l_pfb, n019024l_pfb_len},
108 {"Courier", "n022003l", n022003l_afm, n022003l_afm_len, n022003l_pfb, n022003l_pfb_len},
109 {"Courier-Oblique", "n022023l", n022023l_afm, n022023l_afm_len, n022023l_pfb, n022023l_pfb_len},
110 {"Courier-Bold", "n022004l", n022004l_afm, n022004l_afm_len, n022004l_pfb, n022004l_pfb_len},
111 {"Courier-BoldOblique", "n022024l", n022024l_afm, n022024l_afm_len, n022024l_pfb, n022024l_pfb_len},
112 {"Symbol", "s050000l", s050000l_afm, s050000l_afm_len, s050000l_pfb, s050000l_pfb_len},
113 {"ZapfDingbats", "d050000l", d050000l_afm, d050000l_afm_len, d050000l_pfb, d050000l_pfb_len}};
116 static int verbose = 0;
117 static int dbgindent = 0;
118 static void dbg(char*format, ...)
125 va_start(arglist, format);
126 vsprintf(buf, format, arglist);
129 while(l && buf[l-1]=='\n') {
134 int indent = dbgindent;
144 typedef struct _feature
147 struct _feature*next;
149 feature_t*featurewarnings = 0;
151 void GFXOutputDev::showfeature(char*feature,char fully, char warn)
153 feature_t*f = featurewarnings;
155 if(!strcmp(feature, f->string))
159 f = (feature_t*)malloc(sizeof(feature_t));
160 f->string = strdup(feature);
161 f->next = featurewarnings;
164 msg("<warning> %s not yet %ssupported!",feature,fully?"fully ":"");
165 if(this->config_break_on_warning) {
166 msg("<fatal> Aborting conversion due to unsupported feature");
170 msg("<notice> File contains %s",feature);
173 void GFXOutputDev::warnfeature(char*feature,char fully)
175 showfeature(feature,fully,1);
177 void GFXOutputDev::infofeature(char*feature)
179 showfeature(feature,0,0);
182 GFXOutputState::GFXOutputState() {
184 this->textRender = 0;
185 this->createsoftmask = 0;
186 this->transparencygroup = 0;
188 this->grouprecording = 0;
192 GBool GFXOutputDev::interpretType3Chars()
194 return this->do_interpretType3Chars;
197 typedef struct _drawnchar
215 chars = (drawnchar_t*)malloc(sizeof(drawnchar_t)*buf_size);
216 memset(chars, 0, sizeof(drawnchar_t)*buf_size);
221 free(chars);chars = 0;
228 chars = (drawnchar_t*)realloc(chars, sizeof(drawnchar_t)*buf_size);
232 void addChar(int charid, gfxcoord_t x, gfxcoord_t y, gfxcolor_t color)
235 chars[num_chars].x = x;
236 chars[num_chars].y = y;
237 chars[num_chars].color = color;
238 chars[num_chars].charid = charid;
242 static char*getFontID(GfxFont*font);
244 GFXOutputDev::GFXOutputDev(parameter_t*p)
247 this->textmodeinfo = 0;
250 this->type3active = 0;
253 this->substitutepos = 0;
254 this->type3Warning = 0;
255 this->user_movex = 0;
256 this->user_movey = 0;
259 this->user_clipx1 = 0;
260 this->user_clipy1 = 0;
261 this->user_clipx2 = 0;
262 this->user_clipy2 = 0;
263 this->current_text_stroke = 0;
264 this->current_text_clip = 0;
266 this->outer_clip_box = 0;
268 this->pagebuflen = 0;
270 this->config_use_fontconfig=1;
271 this->config_break_on_warning=0;
272 this->do_interpretType3Chars = gTrue;
274 this->parameters = p;
276 memset(states, 0, sizeof(states));
278 /* configure device */
280 setParameter(p->name, p->value);
285 void GFXOutputDev::setParameter(char*key, char*value)
287 if(!strcmp(key,"rawtext")) {
288 this->do_interpretType3Chars = atoi(value)^1;
289 } else if(!strcmp(key,"breakonwarning")) {
290 this->config_break_on_warning = atoi(value);
291 } else if(!strcmp(key,"fontconfig")) {
292 this->config_use_fontconfig = atoi(value);
294 msg("<warning> Ignored parameter: %s=%s", key, value);
298 void GFXOutputDev::setDevice(gfxdevice_t*dev)
300 parameter_t*p = this->parameters;
302 /* pass parameters to output device */
306 this->device->setparameter(this->device, p->name, p->value);
312 void GFXOutputDev::setMove(int x,int y)
314 this->user_movex = x;
315 this->user_movey = y;
318 void GFXOutputDev::setClip(int x1,int y1,int x2,int y2)
320 if(x2<x1) {int x3=x1;x1=x2;x2=x3;}
321 if(y2<y1) {int y3=y1;y1=y2;y2=y3;}
323 this->user_clipx1 = x1;
324 this->user_clipy1 = y1;
325 this->user_clipx2 = x2;
326 this->user_clipy2 = y2;
329 static char*getFontID(GfxFont*font)
331 Ref*ref = font->getID();
332 GString*gstr = font->getName();
333 char* fname = gstr==0?0:gstr->getCString();
336 sprintf(buf, "font-%d-%d", ref->num, ref->gen);
338 sprintf(buf, "%s-%d-%d", fname, ref->num, ref->gen);
343 static char*getFontName(GfxFont*font)
346 GString*gstr = font->getName();
347 char* fname = gstr==0?0:gstr->getCString();
351 sprintf(buf, "UFONT%d", r->num);
352 fontid = strdup(buf);
354 fontid = strdup(fname);
358 char* plus = strchr(fontid, '+');
359 if(plus && plus < &fontid[strlen(fontid)-1]) {
360 fontname = strdup(plus+1);
362 fontname = strdup(fontid);
368 static char mybuf[1024];
369 static char* gfxstate2str(GfxState *state)
373 bufpos+=sprintf(bufpos,"CTM[%.3f/%.3f/%.3f/%.3f/%.3f/%.3f] ",
380 if(state->getX1()!=0.0)
381 bufpos+=sprintf(bufpos,"X1-%.1f ",state->getX1());
382 if(state->getY1()!=0.0)
383 bufpos+=sprintf(bufpos,"Y1-%.1f ",state->getY1());
384 bufpos+=sprintf(bufpos,"X2-%.1f ",state->getX2());
385 bufpos+=sprintf(bufpos,"Y2-%.1f ",state->getY2());
386 bufpos+=sprintf(bufpos,"PW%.1f ",state->getPageWidth());
387 bufpos+=sprintf(bufpos,"PH%.1f ",state->getPageHeight());
388 /*bufpos+=sprintf(bufpos,"FC[%.1f/%.1f] ",
389 state->getFillColor()->c[0], state->getFillColor()->c[1]);
390 bufpos+=sprintf(bufpos,"SC[%.1f/%.1f] ",
391 state->getStrokeColor()->c[0], state->getFillColor()->c[1]);*/
392 /* bufpos+=sprintf(bufpos,"FC[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f]",
393 state->getFillColor()->c[0], state->getFillColor()->c[1],
394 state->getFillColor()->c[2], state->getFillColor()->c[3],
395 state->getFillColor()->c[4], state->getFillColor()->c[5],
396 state->getFillColor()->c[6], state->getFillColor()->c[7]);
397 bufpos+=sprintf(bufpos,"SC[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f]",
398 state->getStrokeColor()->c[0], state->getFillColor()->c[1],
399 state->getStrokeColor()->c[2], state->getFillColor()->c[3],
400 state->getStrokeColor()->c[4], state->getFillColor()->c[5],
401 state->getStrokeColor()->c[6], state->getFillColor()->c[7]);*/
402 state->getFillRGB(&rgb);
403 if(rgb.r || rgb.g || rgb.b)
404 bufpos+=sprintf(bufpos,"FR[%.1f/%.1f/%.1f] ", rgb.r,rgb.g,rgb.b);
405 state->getStrokeRGB(&rgb);
406 if(rgb.r || rgb.g || rgb.b)
407 bufpos+=sprintf(bufpos,"SR[%.1f/%.1f/%.1f] ", rgb.r,rgb.g,rgb.b);
408 if(state->getFillColorSpace()->getNComps()>1)
409 bufpos+=sprintf(bufpos,"CS[[%d]] ",state->getFillColorSpace()->getNComps());
410 if(state->getStrokeColorSpace()->getNComps()>1)
411 bufpos+=sprintf(bufpos,"SS[[%d]] ",state->getStrokeColorSpace()->getNComps());
412 if(state->getFillPattern())
413 bufpos+=sprintf(bufpos,"FP%08x ", state->getFillPattern());
414 if(state->getStrokePattern())
415 bufpos+=sprintf(bufpos,"SP%08x ", state->getStrokePattern());
417 if(state->getFillOpacity()!=1.0)
418 bufpos+=sprintf(bufpos,"FO%.1f ", state->getFillOpacity());
419 if(state->getStrokeOpacity()!=1.0)
420 bufpos+=sprintf(bufpos,"SO%.1f ", state->getStrokeOpacity());
422 bufpos+=sprintf(bufpos,"LW%.1f ", state->getLineWidth());
427 state->getLineDash(&dash, &length, &start);
431 bufpos+=sprintf(bufpos,"DASH%.1f[",start);
432 for(t=0;t<length;t++) {
433 bufpos+=sprintf(bufpos,"D%.1f",dash[t]);
435 bufpos+=sprintf(bufpos,"]");
438 if(state->getFlatness()!=1)
439 bufpos+=sprintf(bufpos,"F%d ", state->getFlatness());
440 if(state->getLineJoin()!=0)
441 bufpos+=sprintf(bufpos,"J%d ", state->getLineJoin());
442 if(state->getLineJoin()!=0)
443 bufpos+=sprintf(bufpos,"C%d ", state->getLineCap());
444 if(state->getLineJoin()!=0)
445 bufpos+=sprintf(bufpos,"ML%d ", state->getMiterLimit());
447 if(state->getFont() && getFontID(state->getFont()))
448 bufpos+=sprintf(bufpos,"F\"%s\" ",getFontID(state->getFont()));
449 bufpos+=sprintf(bufpos,"FS%.1f ", state->getFontSize());
450 bufpos+=sprintf(bufpos,"MAT[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f] ", state->getTextMat()[0],state->getTextMat()[1],state->getTextMat()[2],
451 state->getTextMat()[3],state->getTextMat()[4],state->getTextMat()[5]);
452 if(state->getCharSpace())
453 bufpos+=sprintf(bufpos,"CS%.5f ", state->getCharSpace());
454 if(state->getWordSpace())
455 bufpos+=sprintf(bufpos,"WS%.5f ", state->getWordSpace());
456 if(state->getHorizScaling()!=1.0)
457 bufpos+=sprintf(bufpos,"SC%.1f ", state->getHorizScaling());
458 if(state->getLeading())
459 bufpos+=sprintf(bufpos,"L%.1f ", state->getLeading());
461 bufpos+=sprintf(bufpos,"R%.1f ", state->getRise());
462 if(state->getRender())
463 bufpos+=sprintf(bufpos,"R%d ", state->getRender());
464 bufpos+=sprintf(bufpos,"P%08x ", state->getPath());
465 bufpos+=sprintf(bufpos,"CX%.1f ", state->getCurX());
466 bufpos+=sprintf(bufpos,"CY%.1f ", state->getCurY());
467 if(state->getLineX())
468 bufpos+=sprintf(bufpos,"LX%.1f ", state->getLineX());
469 if(state->getLineY())
470 bufpos+=sprintf(bufpos,"LY%.1f ", state->getLineY());
471 bufpos+=sprintf(bufpos," ");
475 static void dumpFontInfo(char*loglevel, GfxFont*font);
476 static int lastdumps[1024];
477 static int lastdumppos = 0;
482 static void showFontError(GfxFont*font, int nr)
486 for(t=0;t<lastdumppos;t++)
487 if(lastdumps[t] == r->num)
491 if(lastdumppos<sizeof(lastdumps)/sizeof(int))
492 lastdumps[lastdumppos++] = r->num;
494 msg("<warning> The following font caused problems:");
496 msg("<warning> The following font caused problems (substituting):");
498 msg("<warning> The following Type 3 Font will be rendered as bitmap:");
499 dumpFontInfo("<warning>", font);
502 static void dumpFontInfo(char*loglevel, GfxFont*font)
504 char* id = getFontID(font);
505 char* name = getFontName(font);
506 Ref* r=font->getID();
507 msg("%s=========== %s (ID:%d,%d) ==========\n", loglevel, name, r->num,r->gen);
509 GString*gstr = font->getTag();
511 msg("%s| Tag: %s\n", loglevel, id);
513 if(font->isCIDFont()) msg("%s| is CID font\n", loglevel);
515 GfxFontType type=font->getType();
517 case fontUnknownType:
518 msg("%s| Type: unknown\n",loglevel);
521 msg("%s| Type: 1\n",loglevel);
524 msg("%s| Type: 1C\n",loglevel);
527 msg("%s| Type: 3\n",loglevel);
530 msg("%s| Type: TrueType\n",loglevel);
533 msg("%s| Type: CIDType0\n",loglevel);
536 msg("%s| Type: CIDType0C\n",loglevel);
539 msg("%s| Type: CIDType2\n",loglevel);
544 GBool embedded = font->getEmbeddedFontID(&embRef);
546 if(font->getEmbeddedFontName()) {
547 embeddedName = font->getEmbeddedFontName()->getCString();
550 msg("%s| Embedded id: %s id: %d\n",loglevel, FIXNULL(embeddedName), embRef.num);
552 gstr = font->getExtFontFile();
554 msg("%s| External Font file: %s\n", loglevel, FIXNULL(gstr->getCString()));
556 // Get font descriptor flags.
557 if(font->isFixedWidth()) msg("%s| is fixed width\n", loglevel);
558 if(font->isSerif()) msg("%s| is serif\n", loglevel);
559 if(font->isSymbolic()) msg("%s| is symbolic\n", loglevel);
560 if(font->isItalic()) msg("%s| is italic\n", loglevel);
561 if(font->isBold()) msg("%s| is bold\n", loglevel);
567 //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");}
568 //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");}
571 void dump_outline(gfxline_t*line)
574 if(line->type == gfx_moveTo) {
575 msg("<debug> | moveTo %.2f %.2f", line->x,line->y);
576 } else if(line->type == gfx_lineTo) {
577 msg("<debug> | lineTo %.2f %.2f", line->x,line->y);
578 } else if(line->type == gfx_splineTo) {
579 msg("<debug> | splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y);
585 gfxline_t* gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user_movex, int user_movey)
587 int num = path->getNumSubpaths();
590 double lastx=0,lasty=0,posx=0,posy=0;
593 msg("<warning> empty path");
597 gfxdrawer_target_gfxline(&draw);
599 for(t = 0; t < num; t++) {
600 GfxSubpath *subpath = path->getSubpath(t);
601 int subnum = subpath->getNumPoints();
602 double bx=0,by=0,cx=0,cy=0;
604 for(s=0;s<subnum;s++) {
607 state->transform(subpath->getX(s),subpath->getY(s),&x,&y);
612 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
613 draw.lineTo(&draw, lastx, lasty);
615 draw.moveTo(&draw, x,y);
620 } else if(subpath->getCurve(s) && cpos==0) {
624 } else if(subpath->getCurve(s) && cpos==1) {
632 draw.lineTo(&draw, x,y);
634 gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y, 0.05);
641 /* fix non-closed lines */
642 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
643 draw.lineTo(&draw, lastx, lasty);
645 gfxline_t*result = (gfxline_t*)draw.result(&draw);
647 gfxline_optimize(result);
652 GBool GFXOutputDev::useTilingPatternFill()
654 infofeature("tiled patterns");
658 GBool GFXOutputDev::useShadedFills()
660 infofeature("shaded fills");
664 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line)
666 int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
667 int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
668 double miterLimit = state->getMiterLimit();
669 double width = state->getTransformedLineWidth();
672 double opaq = state->getStrokeOpacity();
674 state->getFillRGB(&rgb);
676 state->getStrokeRGB(&rgb);
678 col.r = colToByte(rgb.r);
679 col.g = colToByte(rgb.g);
680 col.b = colToByte(rgb.b);
681 col.a = (unsigned char)(opaq*255);
683 gfx_capType capType = gfx_capRound;
684 if(lineCap == 0) capType = gfx_capButt;
685 else if(lineCap == 1) capType = gfx_capRound;
686 else if(lineCap == 2) capType = gfx_capSquare;
688 gfx_joinType joinType = gfx_joinRound;
689 if(lineJoin == 0) joinType = gfx_joinMiter;
690 else if(lineJoin == 1) joinType = gfx_joinRound;
691 else if(lineJoin == 2) joinType = gfx_joinBevel;
694 double dashphase = 0;
696 state->getLineDash(&ldash, &dashnum, &dashphase);
700 if(dashnum && ldash) {
701 float * dash = (float*)malloc(sizeof(float)*(dashnum+1));
705 msg("<trace> %d dashes", dashnum);
706 msg("<trace> | phase: %f", dashphase);
707 for(t=0;t<dashnum;t++) {
709 msg("<trace> | d%-3d: %f", t, ldash[t]);
712 if(getLogLevel() >= LOGLEVEL_TRACE) {
716 line2 = gfxtool_dash_line(line, dash, dashphase);
719 msg("<trace> After dashing:");
722 if(getLogLevel() >= LOGLEVEL_TRACE) {
723 msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x\n",
725 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
726 lineCap==0?"butt": (lineJoin==1?"round":"square"),
728 col.r,col.g,col.b,col.a
733 //swfoutput_drawgfxline(output, line, width, &col, capType, joinType, miterLimit);
734 device->stroke(device, line, width, &col, capType, joinType, miterLimit);
740 gfxcolor_t getFillColor(GfxState * state)
743 double opaq = state->getFillOpacity();
744 state->getFillRGB(&rgb);
746 col.r = colToByte(rgb.r);
747 col.g = colToByte(rgb.g);
748 col.b = colToByte(rgb.b);
749 col.a = (unsigned char)(opaq*255);
753 void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line)
755 gfxcolor_t col = getFillColor(state);
757 if(getLogLevel() >= LOGLEVEL_TRACE) {
758 msg("<trace> fill %02x%02x%02x%02x\n", col.r, col.g, col.b, col.a);
761 device->fill(device, line, &col);
764 void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line)
766 if(getLogLevel() >= LOGLEVEL_TRACE) {
767 msg("<trace> clip\n");
771 device->startclip(device, line);
772 states[statepos].clipping++;
775 void GFXOutputDev::clip(GfxState *state)
777 GfxPath * path = state->getPath();
778 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
779 clipToGfxLine(state, line);
783 void GFXOutputDev::eoClip(GfxState *state)
785 GfxPath * path = state->getPath();
786 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
788 if(getLogLevel() >= LOGLEVEL_TRACE) {
789 msg("<trace> eoclip\n");
793 device->startclip(device, line);
794 states[statepos].clipping++;
798 void GFXOutputDev::endframe()
801 device->endclip(device);
806 void GFXOutputDev::finish()
810 device->endclip(device);
816 GFXOutputDev::~GFXOutputDev()
821 free(this->pages); this->pages = 0;
824 fontlist_t*l = this->fontlist;
826 fontlist_t*next = l->next;
828 gfxfont_free(l->font);
829 free(l->filename);l->filename=0;
835 GBool GFXOutputDev::upsideDown()
839 GBool GFXOutputDev::useDrawChar()
844 char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
845 "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
847 #define RENDER_FILL 0
848 #define RENDER_STROKE 1
849 #define RENDER_FILLSTROKE 2
850 #define RENDER_INVISIBLE 3
851 #define RENDER_CLIP 4
853 static char tmp_printstr[4096];
854 char* makeStringPrintable(char*str)
856 int len = strlen(str);
871 tmp_printstr[len++] = '.';
872 tmp_printstr[len++] = '.';
873 tmp_printstr[len++] = '.';
875 tmp_printstr[len] = 0;
880 int getGfxCharID(gfxfont_t*font, int charnr, char *charname, int u)
887 /* find out char name from unicode index
888 TODO: should be precomputed
890 for(t=0;t<sizeof(nameToUnicodeTab)/sizeof(nameToUnicodeTab[0]);t++) {
891 if(nameToUnicodeTab[t].u == u) {
892 uniname = nameToUnicodeTab[t].name;
900 for(t=0;t<font->num_glyphs;t++) {
901 if(font->glyphs[t].name && !strcmp(font->glyphs[t].name,charname)) {
902 msg("<debug> Char [%d,>%s<,%d] maps to %d\n", charnr, charname, u, t);
906 /* if we didn't find the character, maybe
907 we can find the capitalized version */
908 for(t=0;t<font->num_glyphs;t++) {
909 if(font->glyphs[t].name && !strcasecmp(font->glyphs[t].name,charname)) {
910 msg("<debug> Char [%d,>>%s<<,%d] maps to %d\n", charnr, charname, u, t);
918 for(t=0;t<font->num_glyphs;t++) {
919 if(font->glyphs[t].name && !strcmp(font->glyphs[t].name,uniname)) {
920 msg("<debug> Char [%d,%s,>%d(%s)<] maps to %d\n", charnr, charname, u, uniname, t);
924 /* if we didn't find the character, maybe
925 we can find the capitalized version */
926 for(t=0;t<font->num_glyphs;t++) {
927 if(font->glyphs[t].name && !strcasecmp(font->glyphs[t].name,uniname)) {
928 msg("<debug> Char [%d,%s,>>%d(%s)<<] maps to %d\n", charnr, charname, u, uniname, t);
934 /* try to use the unicode id */
935 if(u>=0 && u<font->max_unicode && font->unicode2glyph[u]>=0) {
936 msg("<debug> Char [%d,%s,>%d<] maps to %d\n", charnr, charname, u, font->unicode2glyph[u]);
937 return font->unicode2glyph[u];
940 if(charnr>=0 && charnr<font->num_glyphs) {
941 msg("<debug> Char [>%d<,%s,%d] maps to %d\n", charnr, charname, u, charnr);
949 void GFXOutputDev::beginString(GfxState *state, GString *s)
951 int render = state->getRender();
952 if(current_text_stroke) {
953 msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
956 msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
957 double m11,m21,m12,m22;
958 // msg("<debug> %s beginstring \"%s\"\n", gfxstate2str(state), s->getCString());
959 state->getFontTransMat(&m11, &m12, &m21, &m22);
960 m11 *= state->getHorizScaling();
961 m21 *= state->getHorizScaling();
963 this->current_font_matrix.m00 = m11 / 1024.0;
964 this->current_font_matrix.m01 = m12 / 1024.0;
965 this->current_font_matrix.m10 = -m21 / 1024.0;
966 this->current_font_matrix.m11 = -m22 / 1024.0;
967 this->current_font_matrix.tx = 0;
968 this->current_font_matrix.ty = 0;
970 gfxmatrix_t m = this->current_font_matrix;
972 states[statepos].textRender = render;
975 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
976 double dx, double dy,
977 double originX, double originY,
978 CharCode c, int nBytes, Unicode *_u, int uLen)
980 int render = state->getRender();
981 // check for invisible text -- this is used by Acrobat Capture
983 msg("<debug> Ignoring invisible text: char %d at %f,%f", c, x, y);
987 if(states[statepos].textRender != render)
988 msg("<error> Internal error: drawChar.render!=beginString.render");
990 gfxcolor_t col = getFillColor(state);
992 Gushort *CIDToGIDMap = 0;
993 GfxFont*font = state->getFont();
995 if(font->getType() == fontType3 && do_interpretType3Chars) {
996 /* type 3 chars are passed as graphics */
997 msg("<debug> type3 char at %f/%f", x, y);
1007 if(font->isCIDFont()) {
1008 GfxCIDFont*cfont = (GfxCIDFont*)font;
1010 if(font->getType() == fontCIDType2)
1011 CIDToGIDMap = cfont->getCIDToGID();
1014 font8 = (Gfx8BitFont*)font;
1015 char**enc=font8->getEncoding();
1019 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);
1022 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);
1028 charid = getGfxCharID(current_gfxfont, c, name, u);
1030 charid = getGfxCharID(current_gfxfont, c, name, -1);
1033 /* multiple unicodes- should usually map to a ligature.
1034 if the ligature doesn't exist, we need to draw
1035 the characters one-by-one. */
1037 msg("<warning> ligature %d missing in font %s\n", c, current_gfxfont->id);
1038 for(t=0;t<uLen;t++) {
1039 drawChar(state, x, y, dx, dy, originX, originY, c, nBytes, _u+t, 1);
1045 msg("<warning> Didn't find character '%s' (c=%d,u=%d) in current charset (%s, %d characters)",
1046 FIXNULL(name),c, u, FIXNULL((char*)current_gfxfont->id), current_gfxfont->num_glyphs);
1050 gfxmatrix_t m = this->current_font_matrix;
1051 state->transform(x, y, &m.tx, &m.ty);
1052 m.tx += user_movex + clipmovex;
1053 m.ty += user_movey + clipmovey;
1055 if(render == RENDER_FILL) {
1056 device->drawchar(device, current_gfxfont, charid, &col, &m);
1058 msg("<debug> Drawing glyph %d as shape", charid);
1060 msg("<notice> Some texts will be rendered as shape");
1063 gfxline_t*glyph = current_gfxfont->glyphs[charid].line;
1064 gfxline_t*tglyph = gfxline_clone(glyph);
1065 gfxline_transform(tglyph, &m);
1066 if((render&3) != RENDER_INVISIBLE) {
1067 gfxline_t*add = gfxline_clone(tglyph);
1068 current_text_stroke = gfxline_append(current_text_stroke, add);
1070 if(render&RENDER_CLIP) {
1071 gfxline_t*add = gfxline_clone(tglyph);
1072 current_text_clip = gfxline_append(current_text_clip, add);
1074 gfxline_free(tglyph);
1078 void GFXOutputDev::endString(GfxState *state)
1080 int render = state->getRender();
1081 msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
1082 if(states[statepos].textRender != render)
1083 msg("<error> Internal error: drawChar.render!=beginString.render");
1085 if(current_text_stroke) {
1086 /* fillstroke and stroke text rendering objects we can process right
1087 now (as there may be texts of other rendering modes in this
1088 text object)- clipping objects have to wait until endTextObject,
1090 device->setparameter(device, "mark","TXT");
1091 if((render&3) == RENDER_FILL) {
1092 fillGfxLine(state, current_text_stroke);
1093 gfxline_free(current_text_stroke);
1094 current_text_stroke = 0;
1095 } else if((render&3) == RENDER_FILLSTROKE) {
1096 fillGfxLine(state, current_text_stroke);
1097 strokeGfxline(state, current_text_stroke);
1098 gfxline_free(current_text_stroke);
1099 current_text_stroke = 0;
1100 } else if((render&3) == RENDER_STROKE) {
1101 strokeGfxline(state, current_text_stroke);
1102 gfxline_free(current_text_stroke);
1103 current_text_stroke = 0;
1105 device->setparameter(device, "mark","");
1109 void GFXOutputDev::endTextObject(GfxState *state)
1111 int render = state->getRender();
1112 msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1113 if(states[statepos].textRender != render)
1114 msg("<error> Internal error: drawChar.render!=beginString.render");
1116 if(current_text_clip) {
1117 device->setparameter(device, "mark","TXT");
1118 clipToGfxLine(state, current_text_clip);
1119 device->setparameter(device, "mark","");
1120 gfxline_free(current_text_clip);
1121 current_text_clip = 0;
1125 /* the logic seems to be as following:
1126 first, beginType3Char is called, with the charcode and the coordinates.
1127 if this function returns true, it already knew about the char and has now drawn it.
1128 if the function returns false, it's a new char, and type3D1 is called with some parameters-
1129 the all draw operations until endType3Char are part of the char (which in this moment is
1130 at the position first passed to beginType3Char). the char ends with endType3Char.
1132 The drawing operations between beginType3Char and endType3Char are somewhat different to
1133 the normal ones. For example, the fillcolor equals the stroke color.
1136 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode code, Unicode *u, int uLen)
1138 msg("<debug> beginType3Char %d, %08x, %d", code, *u, uLen);
1143 gfxcolor_t col={255,0,0,0};
1144 gfxmatrix_t m = {1,0,0, 0,1,0};
1146 for(t=0;t<uLen;t++) {
1147 device->drawchar(device, 0, u[t], &col, &m);
1150 /* the character itself is going to be passed using the draw functions */
1151 return gFalse; /* gTrue= is_in_cache? */
1154 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1155 msg("<debug> type3D0 width=%f height=%f", wx, wy);
1157 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1158 msg("<debug> type3D1 width=%f height=%f bbox=(%f,%f,%f,%f)", wx, wy,
1162 void GFXOutputDev::endType3Char(GfxState *state)
1165 msg("<debug> endType3Char");
1168 void GFXOutputDev::startFrame(int width, int height)
1170 if(outer_clip_box) {
1171 device->endclip(device);
1175 device->startpage(device, width, height);
1176 this->width = width;
1177 this->height = height;
1180 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
1182 this->currentpage = pageNum;
1184 int rot = doc->getPageRotate(1);
1187 gfxline_t clippath[5];
1189 white.r = white.g = white.b = white.a = 255;
1191 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1192 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1193 Use CropBox, not MediaBox, as page size
1200 state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1201 state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1203 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1204 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1206 this->clipmovex = -(int)x1;
1207 this->clipmovey = -(int)y1;
1209 /* apply user clip box */
1210 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1211 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1212 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1213 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1214 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1215 msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1218 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1220 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);
1222 msg("<verbose> page is rotated %d degrees\n", rot);
1224 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1225 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1226 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1227 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1228 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1229 device->startclip(device, clippath); outer_clip_box = 1;
1230 device->fill(device, clippath, &white);
1234 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1236 double x1, y1, x2, y2, w;
1237 gfxline_t points[5];
1240 msg("<debug> drawlink\n");
1242 link->getRect(&x1, &y1, &x2, &y2);
1243 cvtUserToDev(x1, y1, &x, &y);
1244 points[0].type = gfx_moveTo;
1245 points[0].x = points[4].x = x + user_movex + clipmovex;
1246 points[0].y = points[4].y = y + user_movey + clipmovey;
1247 points[0].next = &points[1];
1248 cvtUserToDev(x2, y1, &x, &y);
1249 points[1].type = gfx_lineTo;
1250 points[1].x = x + user_movex + clipmovex;
1251 points[1].y = y + user_movey + clipmovey;
1252 points[1].next = &points[2];
1253 cvtUserToDev(x2, y2, &x, &y);
1254 points[2].type = gfx_lineTo;
1255 points[2].x = x + user_movex + clipmovex;
1256 points[2].y = y + user_movey + clipmovey;
1257 points[2].next = &points[3];
1258 cvtUserToDev(x1, y2, &x, &y);
1259 points[3].type = gfx_lineTo;
1260 points[3].x = x + user_movex + clipmovex;
1261 points[3].y = y + user_movey + clipmovey;
1262 points[3].next = &points[4];
1263 cvtUserToDev(x1, y1, &x, &y);
1264 points[4].type = gfx_lineTo;
1265 points[4].x = x + user_movex + clipmovex;
1266 points[4].y = y + user_movey + clipmovey;
1269 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f\n",
1270 points[0].x, points[0].y,
1271 points[1].x, points[1].y,
1272 points[2].x, points[2].y,
1273 points[3].x, points[3].y);
1275 LinkAction*action=link->getAction();
1281 msg("<trace> drawlink action=%d\n", action->getKind());
1282 switch(action->getKind())
1286 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1287 LinkDest *dest=NULL;
1288 if (ha->getDest()==NULL)
1289 dest=catalog->findDest(ha->getNamedDest());
1290 else dest=ha->getDest();
1292 if (dest->isPageRef()){
1293 Ref pageref=dest->getPageRef();
1294 page=catalog->findPage(pageref.num,pageref.gen);
1296 else page=dest->getPageNum();
1297 sprintf(buf, "%d", page);
1304 LinkGoToR*l = (LinkGoToR*)action;
1305 GString*g = l->getFileName();
1307 s = strdup(g->getCString());
1309 /* if the GoToR link has no filename, then
1310 try to find a refernce in the *local*
1312 GString*g = l->getNamedDest();
1314 s = strdup(g->getCString());
1320 LinkNamed*l = (LinkNamed*)action;
1321 GString*name = l->getName();
1323 s = strdup(name->lowerCase()->getCString());
1324 named = name->getCString();
1327 if(strstr(s, "next") || strstr(s, "forward"))
1329 page = currentpage + 1;
1331 else if(strstr(s, "prev") || strstr(s, "back"))
1333 page = currentpage - 1;
1335 else if(strstr(s, "last") || strstr(s, "end"))
1337 if(pages && pagepos>0)
1338 page = pages[pagepos-1];
1340 else if(strstr(s, "first") || strstr(s, "top"))
1348 case actionLaunch: {
1350 LinkLaunch*l = (LinkLaunch*)action;
1351 GString * str = new GString(l->getFileName());
1352 GString * params = l->getParams();
1354 str->append(params);
1355 s = strdup(str->getCString());
1362 LinkURI*l = (LinkURI*)action;
1363 GString*g = l->getURI();
1365 url = g->getCString();
1370 case actionUnknown: {
1372 LinkUnknown*l = (LinkUnknown*)action;
1377 msg("<error> Unknown link type!\n");
1382 if(!s) s = strdup("-?-");
1384 msg("<trace> drawlink s=%s\n", s);
1386 if(!linkinfo && (page || s))
1388 msg("<notice> File contains links");
1396 for(t=1;t<=pagepos;t++) {
1397 if(pages[t]==page) {
1406 sprintf(buf, "page%d", lpage);
1407 device->drawlink(device, points, buf);
1411 device->drawlink(device, points, s);
1414 msg("<verbose> \"%s\" link to \"%s\" (%d)\n", type, FIXNULL(s), page);
1418 void GFXOutputDev::saveState(GfxState *state) {
1419 dbg("saveState");dbgindent+=2;
1421 msg("<trace> saveState\n");
1424 msg("<error> Too many nested states in pdf.");
1428 states[statepos].textRender = states[statepos-1].textRender;
1429 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1430 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1431 states[statepos].clipping = 0;
1434 void GFXOutputDev::restoreState(GfxState *state) {
1435 dbgindent-=2; dbg("restoreState");
1438 msg("<error> Invalid restoreState");
1441 msg("<trace> restoreState");
1442 if(states[statepos].softmask) {
1443 clearSoftMask(state);
1446 while(states[statepos].clipping) {
1447 device->endclip(device);
1448 states[statepos].clipping--;
1453 char* writeOutStdFont(fontentry* f)
1458 char* tmpFileName = mktmpname(namebuf1);
1460 sprintf(namebuf2, "%s.afm", tmpFileName);
1461 fi = fopen(namebuf2, "wb");
1464 fwrite(f->afm, 1, f->afmlen, fi);
1467 sprintf(namebuf2, "%s.pfb", tmpFileName);
1468 fi = fopen(namebuf2, "wb");
1471 fwrite(f->pfb, 1, f->pfblen, fi);
1474 return strdup(namebuf2);
1477 char* GFXOutputDev::searchFont(char*name)
1482 msg("<verbose> SearchFont(%s)", name);
1484 /* see if it is a pdf standard font */
1485 for(i=0;i<sizeof(pdf2t1map)/sizeof(fontentry);i++)
1487 if(!strcmp(name, pdf2t1map[i].pdffont))
1489 if(!pdf2t1map[i].fullfilename) {
1490 pdf2t1map[i].fullfilename = writeOutStdFont(&pdf2t1map[i]);
1491 if(!pdf2t1map[i].fullfilename) {
1492 msg("<error> Couldn't save default font- is the Temp Directory writable?");
1494 msg("<verbose> Storing standard PDF font %s at %s", name, pdf2t1map[i].fullfilename);
1497 return strdup(pdf2t1map[i].fullfilename);
1500 /* else look in all font files */
1501 for(i=0;i<fontnum;i++)
1503 if(strstr(fonts[i].filename, name)) {
1504 return strdup(fonts[i].filename);
1510 void GFXOutputDev::updateLineWidth(GfxState *state)
1512 double width = state->getTransformedLineWidth();
1513 //swfoutput_setlinewidth(&device, width);
1516 void GFXOutputDev::updateLineCap(GfxState *state)
1518 int c = state->getLineCap();
1521 void GFXOutputDev::updateLineJoin(GfxState *state)
1523 int j = state->getLineJoin();
1526 void GFXOutputDev::updateFillColor(GfxState *state)
1529 double opaq = state->getFillOpacity();
1530 state->getFillRGB(&rgb);
1532 void GFXOutputDev::updateFillOpacity(GfxState *state)
1535 double opaq = state->getFillOpacity();
1536 state->getFillRGB(&rgb);
1537 dbg("update fillopaq %f", opaq);
1539 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1541 double opaq = state->getFillOpacity();
1542 dbg("update strokeopaq %f", opaq);
1544 void GFXOutputDev::updateFillOverprint(GfxState *state)
1546 double opaq = state->getFillOverprint();
1547 dbg("update filloverprint %f", opaq);
1549 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1551 double opaq = state->getStrokeOverprint();
1552 dbg("update strokeoverprint %f", opaq);
1554 void GFXOutputDev::updateTransfer(GfxState *state)
1556 dbg("update transfer");
1560 void GFXOutputDev::updateStrokeColor(GfxState *state)
1563 double opaq = state->getStrokeOpacity();
1564 state->getStrokeRGB(&rgb);
1567 void FoFiWrite(void *stream, char *data, int len)
1569 int ret = fwrite(data, len, 1, (FILE*)stream);
1572 char*GFXOutputDev::writeEmbeddedFontToFile(XRef*ref, GfxFont*font)
1574 char*tmpFileName = NULL;
1580 Object refObj, strObj;
1582 tmpFileName = mktmpname(namebuf);
1585 ret = font->getEmbeddedFontID(&embRef);
1587 msg("<verbose> Didn't get embedded font id");
1588 /* not embedded- the caller should now search the font
1589 directories for this font */
1593 f = fopen(tmpFileName, "wb");
1595 msg("<error> Couldn't create temporary Type 1 font file");
1599 /*if(font->isCIDFont()) {
1600 GfxCIDFont* cidFont = (GfxCIDFont *)font;
1601 GString c = cidFont->getCollection();
1602 msg("<notice> Collection: %s", c.getCString());
1605 //if (font->getType() == fontType1C) {
1606 if (0) { //font->getType() == fontType1C) {
1607 if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
1609 msg("<error> Couldn't read embedded font file");
1612 FoFiType1C *cvt = FoFiType1C::make(fontBuf, fontLen);
1614 cvt->convertToType1(0, NULL, gTrue, FoFiWrite, f);
1615 //cvt->convertToCIDType0("test", f);
1616 //cvt->convertToType0("test", f);
1619 } else if(font->getType() == fontTrueType) {
1620 msg("<verbose> writing font using TrueTypeFontFile::writeTTF");
1621 if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
1623 msg("<error> Couldn't read embedded font file");
1626 FoFiTrueType *cvt = FoFiTrueType::make(fontBuf, fontLen);
1627 cvt->writeTTF(FoFiWrite, f);
1631 font->getEmbeddedFontID(&embRef);
1632 refObj.initRef(embRef.num, embRef.gen);
1633 refObj.fetch(ref, &strObj);
1635 strObj.streamReset();
1640 f4[t] = strObj.streamGetChar();
1641 f4c[t] = (char)f4[t];
1646 if(!strncmp(f4c, "true", 4)) {
1647 /* some weird TTF fonts don't start with 0,1,0,0 but with "true".
1648 Change this on the fly */
1649 f4[0] = f4[2] = f4[3] = 0;
1657 while ((c = strObj.streamGetChar()) != EOF) {
1661 strObj.streamClose();
1666 return strdup(tmpFileName);
1669 char* GFXOutputDev::searchForSuitableFont(GfxFont*gfxFont)
1671 char*name = getFontName(gfxFont);
1675 if(!this->config_use_fontconfig)
1678 #ifdef HAVE_FONTCONFIG
1679 FcPattern *pattern, *match;
1683 static int fcinitcalled = false;
1685 msg("<debug> searchForSuitableFont(%s)", name);
1687 // call init ony once
1688 if (!fcinitcalled) {
1689 msg("<debug> Initializing FontConfig...");
1690 fcinitcalled = true;
1692 msg("<debug> FontConfig Initialization failed. Disabling.");
1693 config_use_fontconfig = 0;
1696 msg("<debug> ...initialized FontConfig");
1699 msg("<debug> FontConfig: Create \"%s\" Family Pattern", name);
1700 pattern = FcPatternBuild(NULL, FC_FAMILY, FcTypeString, name, NULL);
1701 if (gfxFont->isItalic()) // check for italic
1702 msg("<debug> FontConfig: Adding Italic Slant");
1703 FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
1704 if (gfxFont->isBold()) // check for bold
1705 msg("<debug> FontConfig: Adding Bold Weight");
1706 FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
1708 msg("<debug> FontConfig: Try to match...");
1709 // configure and match using the original font name
1710 FcConfigSubstitute(0, pattern, FcMatchPattern);
1711 FcDefaultSubstitute(pattern);
1712 match = FcFontMatch(0, pattern, &result);
1714 if (FcPatternGetString(match, "family", 0, &v) == FcResultMatch) {
1715 msg("<debug> FontConfig: family=%s", (char*)v);
1716 // if we get an exact match
1717 if (strcmp((char *)v, name) == 0) {
1718 if (FcPatternGetString(match, "file", 0, &v) == FcResultMatch) {
1719 filename = strdup((char*)v); // mem leak
1720 char *nfn = strrchr(filename, '/');
1721 if(nfn) fontname = strdup(nfn+1);
1722 else fontname = filename;
1724 msg("<debug> FontConfig: Returning \"%s\"", fontname);
1726 // initialize patterns
1727 FcPatternDestroy(pattern);
1728 FcPatternDestroy(match);
1730 // now match against serif etc.
1731 if (gfxFont->isSerif()) {
1732 msg("<debug> FontConfig: Create Serif Family Pattern");
1733 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "serif", NULL);
1734 } else if (gfxFont->isFixedWidth()) {
1735 msg("<debug> FontConfig: Create Monospace Family Pattern");
1736 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "monospace", NULL);
1738 msg("<debug> FontConfig: Create Sans Family Pattern");
1739 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "sans", NULL);
1743 if (gfxFont->isItalic()) {
1744 msg("<debug> FontConfig: Adding Italic Slant");
1745 int bb = FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
1748 if (gfxFont->isBold()) {
1749 msg("<debug> FontConfig: Adding Bold Weight");
1750 int bb = FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
1753 msg("<debug> FontConfig: Try to match... (2)");
1754 // configure and match using serif etc
1755 FcConfigSubstitute (0, pattern, FcMatchPattern);
1756 FcDefaultSubstitute (pattern);
1757 match = FcFontMatch (0, pattern, &result);
1759 if (FcPatternGetString(match, "file", 0, &v) == FcResultMatch) {
1760 filename = strdup((char*)v); // mem leak
1761 char *nfn = strrchr(filename, '/');
1762 if(nfn) fontname = strdup(nfn+1);
1763 else fontname = filename;
1765 msg("<debug> FontConfig: Returning \"%s\"", fontname);
1769 //printf("FONTCONFIG: pattern");
1770 //FcPatternPrint(pattern);
1771 //printf("FONTCONFIG: match");
1772 //FcPatternPrint(match);
1774 FcPatternDestroy(pattern);
1775 FcPatternDestroy(match);
1777 pdfswf_addfont(filename);
1784 char* GFXOutputDev::substituteFont(GfxFont*gfxFont, char* oldname)
1786 char*fontname = 0, *filename = 0;
1787 msg("<notice> substituteFont(%s)", oldname);
1789 if(!(fontname = searchForSuitableFont(gfxFont))) {
1790 fontname = "Times-Roman";
1792 filename = searchFont(fontname);
1794 msg("<error> Couldn't find font %s- did you install the default fonts?", fontname);
1798 if(substitutepos>=sizeof(substitutesource)/sizeof(char*)) {
1799 msg("<fatal> Too many fonts in file.");
1803 substitutesource[substitutepos] = strdup(oldname); //mem leak
1804 substitutetarget[substitutepos] = fontname;
1805 msg("<notice> substituting %s -> %s", FIXNULL(oldname), FIXNULL(fontname));
1808 return strdup(filename); //mem leak
1811 void unlinkfont(char* filename)
1818 if(!strncmp(&filename[l-4],".afm",4)) {
1819 memcpy(&filename[l-4],".pfb",4);
1821 memcpy(&filename[l-4],".pfa",4);
1823 memcpy(&filename[l-4],".afm",4);
1826 if(!strncmp(&filename[l-4],".pfa",4)) {
1827 memcpy(&filename[l-4],".afm",4);
1829 memcpy(&filename[l-4],".pfa",4);
1832 if(!strncmp(&filename[l-4],".pfb",4)) {
1833 memcpy(&filename[l-4],".afm",4);
1835 memcpy(&filename[l-4],".pfb",4);
1840 void GFXOutputDev::setXRef(PDFDoc*doc, XRef *xref)
1846 int GFXOutputDev::setGfxFont(char*id, char*name, char*filename, double maxSize)
1849 fontlist_t*last=0,*l = this->fontlist;
1852 msg("<error> Internal Error: FontID is null");
1854 /* TODO: should this be part of the state? */
1857 if(!strcmp(l->font->id, id)) {
1858 current_gfxfont = l->font;
1860 device->addfont(device, current_gfxfont);
1865 if(!filename) return 0;
1867 /* A font size of e.g. 9 means the font will be scaled down by
1868 1024 and scaled up by 9. So to have a maximum error of 1/20px,
1869 we have to divide 0.05 by (fontsize/1024)
1871 double quality = (1024 * 0.05) / maxSize;
1873 msg("<verbose> Loading %s...", filename);
1874 font = gfxfont_load(id, filename, quality);
1876 msg("<verbose> Couldn't load Font %s (%s)", filename, id);
1879 msg("<verbose> Font %s (%s) loaded successfully", filename, id);
1883 l->filename = strdup(filename);
1885 current_gfxfont = l->font;
1891 device->addfont(device, current_gfxfont);
1895 void GFXOutputDev::updateFont(GfxState *state)
1897 GfxFont*gfxFont = state->getFont();
1903 char * fontid = getFontID(gfxFont);
1904 char * fontname = getFontName(gfxFont);
1906 double maxSize = 1.0;
1909 maxSize = this->info->getMaximumFontSize(fontid);
1913 /* first, look if we substituted this font before-
1914 this way, we don't initialize the T1 Fonts
1916 for(t=0;t<substitutepos;t++) {
1917 if(!strcmp(fontid, substitutesource[t])) {
1918 free(fontid);fontid=0;
1919 fontid = strdup(substitutetarget[t]);
1924 /* second, see if this is a font which was used before-
1925 if so, we are done */
1926 if(setGfxFont(fontid, fontname, 0, 0)) {
1931 /* if(swfoutput_queryfont(&device, fontid))
1932 swfoutput_setfont(&device, fontid, 0);
1934 msg("<debug> updateFont(%s) [cached]", fontid);
1938 // look for Type 3 font
1939 if (gfxFont->getType() == fontType3) {
1941 type3Warning = gTrue;
1942 showFontError(gfxFont, 2);
1949 /* now either load the font, or find a substitution */
1952 GBool embedded = gfxFont->getEmbeddedFontID(&embRef);
1957 (gfxFont->getType() == fontType1 ||
1958 gfxFont->getType() == fontType1C ||
1959 gfxFont->getType() == fontCIDType0C ||
1960 gfxFont->getType() == fontTrueType ||
1961 gfxFont->getType() == fontCIDType2
1964 fileName = writeEmbeddedFontToFile(xref, gfxFont);
1965 if(!fileName) showFontError(gfxFont,0);
1968 fileName = searchFont(fontname);
1969 if(!fileName) showFontError(gfxFont,0);
1972 char * fontname = getFontName(gfxFont);
1973 msg("<warning> Font %s %scould not be loaded.", fontname, embedded?"":"(not embedded) ");
1976 msg("<warning> Try putting a TTF version of that font (named \"%s.ttf\") into %s", fontname, lastfontdir);
1978 msg("<warning> Try specifying one or more font directories");
1980 fileName = substituteFont(gfxFont, fontid);
1983 if(fontid) { free(fontid);fontid = strdup(substitutetarget[substitutepos-1]); /*ugly hack*/};
1984 msg("<notice> Font is now %s (%s)", fontid, fileName);
1988 msg("<error> Couldn't set font %s\n", fontid);
1994 msg("<verbose> updateFont(%s) -> %s (max size: %f)", fontid, fileName, maxSize);
1995 dumpFontInfo("<verbose>", gfxFont);
1997 //swfoutput_setfont(&device, fontid, fileName);
1999 if(!setGfxFont(fontid, fontname, 0, 0)) {
2000 setGfxFont(fontid, fontname, fileName, maxSize);
2004 unlinkfont(fileName);
2014 #define SQR(x) ((x)*(x))
2016 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
2018 if((newwidth<2 || newheight<2) ||
2019 (width<=newwidth || height<=newheight))
2021 unsigned char*newdata;
2023 newdata= (unsigned char*)malloc(newwidth*newheight);
2025 double fx = (double)(width)/newwidth;
2026 double fy = (double)(height)/newheight;
2028 int blocksize = (int)(8192/(fx*fy));
2029 int r = 8192*256/palettesize;
2030 for(x=0;x<newwidth;x++) {
2031 double ex = px + fx;
2032 int fromx = (int)px;
2034 int xweight1 = (int)(((fromx+1)-px)*256);
2035 int xweight2 = (int)((ex-tox)*256);
2037 for(y=0;y<newheight;y++) {
2038 double ey = py + fy;
2039 int fromy = (int)py;
2041 int yweight1 = (int)(((fromy+1)-py)*256);
2042 int yweight2 = (int)((ey-toy)*256);
2045 for(xx=fromx;xx<=tox;xx++)
2046 for(yy=fromy;yy<=toy;yy++) {
2047 int b = 1-data[width*yy+xx];
2049 if(xx==fromx) weight = (weight*xweight1)/256;
2050 if(xx==tox) weight = (weight*xweight2)/256;
2051 if(yy==fromy) weight = (weight*yweight1)/256;
2052 if(yy==toy) weight = (weight*yweight2)/256;
2055 //if(a) a=(palettesize-1)*r/blocksize;
2056 newdata[y*newwidth+x] = (a*blocksize)/r;
2064 #define IMAGE_TYPE_JPEG 0
2065 #define IMAGE_TYPE_LOSSLESS 1
2067 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
2068 double x1,double y1,
2069 double x2,double y2,
2070 double x3,double y3,
2071 double x4,double y4, int type)
2073 gfxcolor_t*newpic=0;
2075 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
2076 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
2078 gfxline_t p1,p2,p3,p4,p5;
2079 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
2080 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
2081 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
2082 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
2083 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
2085 {p1.x = (int)(p1.x*20)/20.0;
2086 p1.y = (int)(p1.y*20)/20.0;
2087 p2.x = (int)(p2.x*20)/20.0;
2088 p2.y = (int)(p2.y*20)/20.0;
2089 p3.x = (int)(p3.x*20)/20.0;
2090 p3.y = (int)(p3.y*20)/20.0;
2091 p4.x = (int)(p4.x*20)/20.0;
2092 p4.y = (int)(p4.y*20)/20.0;
2093 p5.x = (int)(p5.x*20)/20.0;
2094 p5.y = (int)(p5.y*20)/20.0;
2101 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2102 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2107 img.data = (gfxcolor_t*)data;
2111 if(type == IMAGE_TYPE_JPEG)
2112 /* TODO: pass image_dpi to device instead */
2113 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2115 dev->fillbitmap(dev, &p1, &img, &m, 0);
2118 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2119 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
2121 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG);
2124 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2125 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
2127 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS);
2131 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2132 int width, int height, GfxImageColorMap*colorMap, GBool invert,
2133 GBool inlineImg, int mask, int*maskColors,
2134 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2136 double x1,y1,x2,y2,x3,y3,x4,y4;
2137 ImageStream *imgStr;
2142 unsigned char* maskbitmap = 0;
2145 ncomps = colorMap->getNumPixelComps();
2146 bits = colorMap->getBits();
2151 unsigned char buf[8];
2152 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2154 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2155 imgMaskStr->reset();
2156 unsigned char pal[256];
2157 int n = 1 << colorMap->getBits();
2162 maskColorMap->getGray(pixBuf, &gray);
2163 pal[t] = colToByte(gray);
2165 for (y = 0; y < maskHeight; y++) {
2166 for (x = 0; x < maskWidth; x++) {
2167 imgMaskStr->getPixel(buf);
2168 maskbitmap[y*maskWidth+x] = pal[buf[0]];
2173 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2174 imgMaskStr->reset();
2175 for (y = 0; y < maskHeight; y++) {
2176 for (x = 0; x < maskWidth; x++) {
2177 imgMaskStr->getPixel(buf);
2179 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2187 imgStr = new ImageStream(str, width, ncomps,bits);
2190 if(!width || !height || (height<=1 && width<=1))
2192 msg("<verbose> Ignoring %d by %d image", width, height);
2193 unsigned char buf[8];
2195 for (y = 0; y < height; ++y)
2196 for (x = 0; x < width; ++x) {
2197 imgStr->getPixel(buf);
2205 state->transform(0, 1, &x1, &y1); x1 += user_movex + clipmovex; y1 += user_movey + clipmovey;
2206 state->transform(0, 0, &x2, &y2); x2 += user_movex + clipmovex; y2 += user_movey + clipmovey;
2207 state->transform(1, 0, &x3, &y3); x3 += user_movex + clipmovex; y3 += user_movey + clipmovey;
2208 state->transform(1, 1, &x4, &y4); x4 += user_movex + clipmovex; y4 += user_movey + clipmovey;
2210 if(!pbminfo && !(str->getKind()==strDCT)) {
2212 msg("<notice> file contains pbm pictures %s",mask?"(masked)":"");
2216 msg("<verbose> drawing %d by %d masked picture\n", width, height);
2218 if(!jpeginfo && (str->getKind()==strDCT)) {
2219 msg("<notice> file contains jpeg pictures");
2225 unsigned char buf[8];
2227 unsigned char*pic = new unsigned char[width*height];
2228 gfxcolor_t pal[256];
2230 state->getFillRGB(&rgb);
2232 memset(pal,255,sizeof(pal));
2233 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2234 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2235 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2236 pal[0].a = 255; pal[1].a = 0;
2239 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2240 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2241 for (y = 0; y < height; ++y)
2242 for (x = 0; x < width; ++x)
2244 imgStr->getPixel(buf);
2247 pic[width*y+x] = buf[0];
2250 /* the size of the drawn image is added to the identifier
2251 as the same image may require different bitmaps if displayed
2252 at different sizes (due to antialiasing): */
2255 unsigned char*pic2 = 0;
2258 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2267 height = realheight;
2271 /* make a black/white palette */
2273 float r = 255/(numpalette-1);
2275 for(t=0;t<numpalette;t++) {
2276 pal[t].r = colToByte(rgb.r);
2277 pal[t].g = colToByte(rgb.g);
2278 pal[t].b = colToByte(rgb.b);
2279 pal[t].a = (unsigned char)(t*r);
2283 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2284 for (y = 0; y < height; ++y) {
2285 for (x = 0; x < width; ++x) {
2286 pic2[width*y+x] = pal[pic[y*width+x]];
2289 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2293 if(maskbitmap) free(maskbitmap);
2299 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2300 gfxcolor_t*pic=new gfxcolor_t[width*height];
2301 for (y = 0; y < height; ++y) {
2302 for (x = 0; x < width; ++x) {
2303 imgStr->getPixel(pixBuf);
2304 colorMap->getRGB(pixBuf, &rgb);
2305 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2306 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2307 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2308 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2310 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2314 if(str->getKind()==strDCT)
2315 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2317 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2320 if(maskbitmap) free(maskbitmap);
2323 gfxcolor_t*pic=new gfxcolor_t[width*height];
2324 gfxcolor_t pal[256];
2325 int n = 1 << colorMap->getBits();
2327 for(t=0;t<256;t++) {
2329 colorMap->getRGB(pixBuf, &rgb);
2331 {/*if(maskColors && *maskColors==t) {
2332 msg("<notice> Color %d is transparent", t);
2333 if (imgData->maskColors) {
2335 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2336 if (pix[i] < imgData->maskColors[2*i] ||
2337 pix[i] > imgData->maskColors[2*i+1]) {
2352 pal[t].r = (unsigned char)(colToByte(rgb.r));
2353 pal[t].g = (unsigned char)(colToByte(rgb.g));
2354 pal[t].b = (unsigned char)(colToByte(rgb.b));
2355 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2358 for (y = 0; y < height; ++y) {
2359 for (x = 0; x < width; ++x) {
2360 imgStr->getPixel(pixBuf);
2361 pic[width*y+x] = pal[pixBuf[0]];
2363 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2367 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2371 if(maskbitmap) free(maskbitmap);
2376 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2377 int width, int height, GBool invert,
2380 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2381 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2382 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2385 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2386 int width, int height, GfxImageColorMap *colorMap,
2387 int *maskColors, GBool inlineImg)
2389 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
2390 colorMap?"colorMap":"no colorMap",
2391 maskColors?"maskColors":"no maskColors",
2393 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
2394 colorMap?"colorMap":"no colorMap",
2395 maskColors?"maskColors":"no maskColors",
2398 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2399 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2400 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2403 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2404 int width, int height,
2405 GfxImageColorMap *colorMap,
2406 Stream *maskStr, int maskWidth, int maskHeight,
2409 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2410 colorMap?"colorMap":"no colorMap",
2411 maskWidth, maskHeight);
2412 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2413 colorMap?"colorMap":"no colorMap",
2414 maskWidth, maskHeight);
2416 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2417 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2418 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2421 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2422 int width, int height,
2423 GfxImageColorMap *colorMap,
2425 int maskWidth, int maskHeight,
2426 GfxImageColorMap *maskColorMap)
2428 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2429 colorMap?"colorMap":"no colorMap",
2430 maskWidth, maskHeight);
2431 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2432 colorMap?"colorMap":"no colorMap",
2433 maskWidth, maskHeight);
2435 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2436 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2437 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2440 void GFXOutputDev::stroke(GfxState *state)
2444 GfxPath * path = state->getPath();
2445 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2446 strokeGfxline(state, line);
2450 void GFXOutputDev::fill(GfxState *state)
2452 gfxcolor_t col = getFillColor(state);
2453 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2455 GfxPath * path = state->getPath();
2456 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2457 fillGfxLine(state, line);
2461 void GFXOutputDev::eoFill(GfxState *state)
2463 gfxcolor_t col = getFillColor(state);
2464 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2466 GfxPath * path = state->getPath();
2467 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2468 fillGfxLine(state, line);
2473 static char* dirseparator()
2482 void addGlobalFont(char*filename)
2485 memset(&f, 0, sizeof(fontfile_t));
2486 f.filename = filename;
2487 if(fontnum < sizeof(fonts)/sizeof(fonts[0])) {
2488 msg("<verbose> Adding font \"%s\".", filename);
2489 fonts[fontnum++] = f;
2491 msg("<error> Too many external fonts. Not adding font file \"%s\".", filename);
2495 void addGlobalLanguageDir(char*dir)
2498 globalParams = new GlobalParams("");
2500 msg("<notice> Adding %s to language pack directories", dir);
2504 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2505 strcpy(config_file, dir);
2506 strcat(config_file, dirseparator());
2507 strcat(config_file, "add-to-xpdfrc");
2509 fi = fopen(config_file, "rb");
2511 msg("<error> Could not open %s", config_file);
2514 globalParams->parseFile(new GString(config_file), fi);
2518 void addGlobalFontDir(char*dirname)
2520 #ifdef HAVE_DIRENT_H
2521 msg("<notice> Adding %s to font directories", dirname);
2522 lastfontdir = strdup(dirname);
2523 DIR*dir = opendir(dirname);
2525 msg("<warning> Couldn't open directory %s\n", dirname);
2530 ent = readdir (dir);
2534 char*name = ent->d_name;
2540 if(!strncasecmp(&name[l-4], ".pfa", 4))
2542 if(!strncasecmp(&name[l-4], ".pfb", 4))
2544 if(!strncasecmp(&name[l-4], ".ttf", 4))
2548 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2549 strcpy(fontname, dirname);
2550 strcat(fontname, dirseparator());
2551 strcat(fontname, name);
2552 addGlobalFont(fontname);
2557 msg("<warning> No dirent.h- unable to add font dir %s", dir);
2561 void GFXOutputDev::preparePage(int pdfpage, int outputpage)
2567 this->pagebuflen = 1024;
2568 this->pages = (int*)malloc(this->pagebuflen*sizeof(int));
2569 memset(this->pages, -1, this->pagebuflen*sizeof(int));
2571 while(pdfpage >= this->pagebuflen)
2573 int oldlen = this->pagebuflen;
2574 this->pagebuflen+=1024;
2575 this->pages = (int*)realloc(this->pages, this->pagebuflen*sizeof(int));
2576 memset(&this->pages[oldlen], -1, (this->pagebuflen-oldlen)*sizeof(int));
2579 this->pages[pdfpage] = outputpage;
2580 if(pdfpage>this->pagepos)
2581 this->pagepos = pdfpage;
2587 double width,height;
2590 BBox mkBBox(GfxState*state, double*bbox, double width, double height)
2592 double xMin, yMin, xMax, yMax, x, y;
2593 double tx, ty, w, h;
2594 // transform the bbox
2595 state->transform(bbox[0], bbox[1], &x, &y);
2598 state->transform(bbox[0], bbox[3], &x, &y);
2601 } else if (x > xMax) {
2606 } else if (y > yMax) {
2609 state->transform(bbox[2], bbox[1], &x, &y);
2612 } else if (x > xMax) {
2617 } else if (y > yMax) {
2620 state->transform(bbox[2], bbox[3], &x, &y);
2623 } else if (x > xMax) {
2628 } else if (y > yMax) {
2631 tx = (int)floor(xMin);
2634 } else if (tx > width) {
2637 ty = (int)floor(yMin);
2640 } else if (ty > height) {
2643 w = (int)ceil(xMax) - tx + 1;
2644 if (tx + w > width) {
2650 h = (int)ceil(yMax) - ty + 1;
2651 if (ty + h > height) {
2665 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2666 GfxColorSpace *blendingColorSpace,
2667 GBool isolated, GBool knockout,
2670 char*colormodename = "";
2671 BBox rect = mkBBox(state, bbox, this->width, this->height);
2673 if(blendingColorSpace) {
2674 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2676 dbg("beginTransparencyGroup %.1f/%.1f/%.1f/%.1f %s isolated=%d knockout=%d forsoftmask=%d", bbox[0],bbox[1],bbox[2],bbox[3], colormodename, isolated, knockout, forSoftMask);
2677 dbg("using clipping rect %f/%f/%f/%f\n", rect.posx,rect.posy,rect.width,rect.height);
2678 msg("<verbose> beginTransparencyGroup %.1f/%.1f/%.1f/%.1f %s isolated=%d knockout=%d forsoftmask=%d", bbox[0],bbox[1],bbox[2],bbox[3], colormodename, isolated, knockout, forSoftMask);
2680 states[statepos].createsoftmask |= forSoftMask;
2681 states[statepos].transparencygroup = !forSoftMask;
2682 states[statepos].isolated = isolated;
2684 states[statepos].olddevice = this->device;
2685 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2687 gfxdevice_record_init(this->device);
2689 /*if(!forSoftMask) { ////???
2690 state->setFillOpacity(0.0);
2695 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2698 dbg("endTransparencyGroup");
2699 msg("<verbose> endTransparencyGroup");
2701 gfxdevice_t*r = this->device;
2703 this->device = states[statepos].olddevice;
2705 if(states[statepos].createsoftmask) {
2706 states[statepos-1].softmaskrecording = r->finish(r);
2708 states[statepos-1].grouprecording = r->finish(r);
2711 states[statepos].createsoftmask = 0;
2712 states[statepos].transparencygroup = 0;
2716 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2718 char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2719 "colordodge","colorburn","hardlight","softlight","difference",
2720 "exclusion","hue","saturation","color","luminosity"};
2722 dbg("paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2723 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2725 if(state->getBlendMode() == gfxBlendNormal)
2726 infofeature("transparency groups");
2729 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2730 warnfeature(buffer, 0);
2733 gfxresult_t*grouprecording = states[statepos].grouprecording;
2735 if(state->getBlendMode() == gfxBlendNormal) {
2737 gfxdevice_ops_init(&ops, this->device, (unsigned char)(state->getFillOpacity()*255));
2738 gfxresult_record_replay(grouprecording, &ops);
2741 grouprecording->destroy(grouprecording);
2743 states[statepos].grouprecording = 0;
2746 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2748 /* alpha = 1: retrieve mask values from alpha layer
2749 alpha = 0: retrieve mask values from luminance */
2750 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2751 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2752 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2753 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2755 infofeature("soft masks");
2757 warnfeature("soft masks from alpha channel",0);
2759 states[statepos].olddevice = this->device;
2760 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2761 gfxdevice_record_init(this->device);
2763 dbg("softmaskrecording is %08x at statepos %d\n", states[statepos].softmaskrecording, statepos);
2765 states[statepos].softmask = 1;
2766 states[statepos].softmask_alpha = alpha;
2769 static inline Guchar div255(int x) {
2770 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2773 void GFXOutputDev::clearSoftMask(GfxState *state)
2775 if(!states[statepos].softmask)
2777 states[statepos].softmask = 0;
2778 dbg("clearSoftMask statepos=%d", statepos);
2779 msg("<verbose> clearSoftMask");
2781 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2782 msg("<error> Error in softmask/tgroup ordering");
2786 gfxresult_t*mask = states[statepos].softmaskrecording;
2787 gfxresult_t*below = this->device->finish(this->device);
2788 this->device = states[statepos].olddevice;
2790 /* get outline of all objects below the soft mask */
2791 gfxdevice_t uniondev;
2792 gfxdevice_union_init(&uniondev, 0);
2793 gfxresult_record_replay(below, &uniondev);
2794 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2795 uniondev.finish(&uniondev);
2797 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2799 this->device->startclip(this->device, belowoutline);
2800 gfxresult_record_replay(below, this->device);
2801 gfxresult_record_replay(mask, this->device);
2802 this->device->endclip(this->device);
2803 gfxline_free(belowoutline);
2806 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2807 if(width<=0 || height<=0)
2810 gfxdevice_t belowrender;
2811 gfxdevice_render_init(&belowrender);
2812 if(states[statepos+1].isolated) {
2813 belowrender.setparameter(&belowrender, "fillwhite", "1");
2815 belowrender.setparameter(&belowrender, "antialize", "2");
2816 belowrender.startpage(&belowrender, width, height);
2817 gfxresult_record_replay(below, &belowrender);
2818 belowrender.endpage(&belowrender);
2819 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2820 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2821 //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2823 gfxdevice_t maskrender;
2824 gfxdevice_render_init(&maskrender);
2825 maskrender.startpage(&maskrender, width, height);
2826 gfxresult_record_replay(mask, &maskrender);
2827 maskrender.endpage(&maskrender);
2828 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2829 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2831 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2832 msg("<fatal> Internal error in mask drawing");
2837 for(y=0;y<height;y++) {
2838 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2839 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2840 for(x=0;x<width;x++) {
2842 if(states[statepos].softmask_alpha) {
2845 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2848 /* premultiply alpha */
2849 l2->a = div255(alpha*l2->a);
2850 l2->r = div255(alpha*l2->r);
2851 l2->g = div255(alpha*l2->g);
2852 l2->b = div255(alpha*l2->b);
2858 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2861 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2862 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2864 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2866 mask->destroy(mask);
2867 below->destroy(below);
2868 maskresult->destroy(maskresult);
2869 belowresult->destroy(belowresult);
2870 states[statepos].softmaskrecording = 0;
2877 delete globalParams;globalParams=0;
2878 Object::memCheck(stderr);