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()
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;
273 this->parameters = p;
275 memset(states, 0, sizeof(states));
277 /* configure device */
279 if(!strcmp(p->name,"fontconfig")) {
280 this->config_use_fontconfig = atoi(p->value);
281 } else if(!strcmp(p->name,"breakonwarning")) {
282 this->config_break_on_warning = atoi(p->value);
288 void GFXOutputDev::setDevice(gfxdevice_t*dev)
290 parameter_t*p = this->parameters;
292 /* pass parameters to output device */
296 this->device->setparameter(this->device, p->name, p->value);
302 void GFXOutputDev::setMove(int x,int y)
304 this->user_movex = x;
305 this->user_movey = y;
308 void GFXOutputDev::setClip(int x1,int y1,int x2,int y2)
310 if(x2<x1) {int x3=x1;x1=x2;x2=x3;}
311 if(y2<y1) {int y3=y1;y1=y2;y2=y3;}
313 this->user_clipx1 = x1;
314 this->user_clipy1 = y1;
315 this->user_clipx2 = x2;
316 this->user_clipy2 = y2;
319 static char*getFontID(GfxFont*font)
321 Ref*ref = font->getID();
322 GString*gstr = font->getName();
323 char* fname = gstr==0?0:gstr->getCString();
326 sprintf(buf, "font-%d-%d", ref->num, ref->gen);
328 sprintf(buf, "%s-%d-%d", fname, ref->num, ref->gen);
333 static char*getFontName(GfxFont*font)
336 GString*gstr = font->getName();
337 char* fname = gstr==0?0:gstr->getCString();
341 sprintf(buf, "UFONT%d", r->num);
342 fontid = strdup(buf);
344 fontid = strdup(fname);
348 char* plus = strchr(fontid, '+');
349 if(plus && plus < &fontid[strlen(fontid)-1]) {
350 fontname = strdup(plus+1);
352 fontname = strdup(fontid);
358 static char mybuf[1024];
359 static char* gfxstate2str(GfxState *state)
363 bufpos+=sprintf(bufpos,"CTM[%.3f/%.3f/%.3f/%.3f/%.3f/%.3f] ",
370 if(state->getX1()!=0.0)
371 bufpos+=sprintf(bufpos,"X1-%.1f ",state->getX1());
372 if(state->getY1()!=0.0)
373 bufpos+=sprintf(bufpos,"Y1-%.1f ",state->getY1());
374 bufpos+=sprintf(bufpos,"X2-%.1f ",state->getX2());
375 bufpos+=sprintf(bufpos,"Y2-%.1f ",state->getY2());
376 bufpos+=sprintf(bufpos,"PW%.1f ",state->getPageWidth());
377 bufpos+=sprintf(bufpos,"PH%.1f ",state->getPageHeight());
378 /*bufpos+=sprintf(bufpos,"FC[%.1f/%.1f] ",
379 state->getFillColor()->c[0], state->getFillColor()->c[1]);
380 bufpos+=sprintf(bufpos,"SC[%.1f/%.1f] ",
381 state->getStrokeColor()->c[0], state->getFillColor()->c[1]);*/
382 /* bufpos+=sprintf(bufpos,"FC[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f]",
383 state->getFillColor()->c[0], state->getFillColor()->c[1],
384 state->getFillColor()->c[2], state->getFillColor()->c[3],
385 state->getFillColor()->c[4], state->getFillColor()->c[5],
386 state->getFillColor()->c[6], state->getFillColor()->c[7]);
387 bufpos+=sprintf(bufpos,"SC[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f]",
388 state->getStrokeColor()->c[0], state->getFillColor()->c[1],
389 state->getStrokeColor()->c[2], state->getFillColor()->c[3],
390 state->getStrokeColor()->c[4], state->getFillColor()->c[5],
391 state->getStrokeColor()->c[6], state->getFillColor()->c[7]);*/
392 state->getFillRGB(&rgb);
393 if(rgb.r || rgb.g || rgb.b)
394 bufpos+=sprintf(bufpos,"FR[%.1f/%.1f/%.1f] ", rgb.r,rgb.g,rgb.b);
395 state->getStrokeRGB(&rgb);
396 if(rgb.r || rgb.g || rgb.b)
397 bufpos+=sprintf(bufpos,"SR[%.1f/%.1f/%.1f] ", rgb.r,rgb.g,rgb.b);
398 if(state->getFillColorSpace()->getNComps()>1)
399 bufpos+=sprintf(bufpos,"CS[[%d]] ",state->getFillColorSpace()->getNComps());
400 if(state->getStrokeColorSpace()->getNComps()>1)
401 bufpos+=sprintf(bufpos,"SS[[%d]] ",state->getStrokeColorSpace()->getNComps());
402 if(state->getFillPattern())
403 bufpos+=sprintf(bufpos,"FP%08x ", state->getFillPattern());
404 if(state->getStrokePattern())
405 bufpos+=sprintf(bufpos,"SP%08x ", state->getStrokePattern());
407 if(state->getFillOpacity()!=1.0)
408 bufpos+=sprintf(bufpos,"FO%.1f ", state->getFillOpacity());
409 if(state->getStrokeOpacity()!=1.0)
410 bufpos+=sprintf(bufpos,"SO%.1f ", state->getStrokeOpacity());
412 bufpos+=sprintf(bufpos,"LW%.1f ", state->getLineWidth());
417 state->getLineDash(&dash, &length, &start);
421 bufpos+=sprintf(bufpos,"DASH%.1f[",start);
422 for(t=0;t<length;t++) {
423 bufpos+=sprintf(bufpos,"D%.1f",dash[t]);
425 bufpos+=sprintf(bufpos,"]");
428 if(state->getFlatness()!=1)
429 bufpos+=sprintf(bufpos,"F%d ", state->getFlatness());
430 if(state->getLineJoin()!=0)
431 bufpos+=sprintf(bufpos,"J%d ", state->getLineJoin());
432 if(state->getLineJoin()!=0)
433 bufpos+=sprintf(bufpos,"C%d ", state->getLineCap());
434 if(state->getLineJoin()!=0)
435 bufpos+=sprintf(bufpos,"ML%d ", state->getMiterLimit());
437 if(state->getFont() && getFontID(state->getFont()))
438 bufpos+=sprintf(bufpos,"F\"%s\" ",getFontID(state->getFont()));
439 bufpos+=sprintf(bufpos,"FS%.1f ", state->getFontSize());
440 bufpos+=sprintf(bufpos,"MAT[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f] ", state->getTextMat()[0],state->getTextMat()[1],state->getTextMat()[2],
441 state->getTextMat()[3],state->getTextMat()[4],state->getTextMat()[5]);
442 if(state->getCharSpace())
443 bufpos+=sprintf(bufpos,"CS%.5f ", state->getCharSpace());
444 if(state->getWordSpace())
445 bufpos+=sprintf(bufpos,"WS%.5f ", state->getWordSpace());
446 if(state->getHorizScaling()!=1.0)
447 bufpos+=sprintf(bufpos,"SC%.1f ", state->getHorizScaling());
448 if(state->getLeading())
449 bufpos+=sprintf(bufpos,"L%.1f ", state->getLeading());
451 bufpos+=sprintf(bufpos,"R%.1f ", state->getRise());
452 if(state->getRender())
453 bufpos+=sprintf(bufpos,"R%d ", state->getRender());
454 bufpos+=sprintf(bufpos,"P%08x ", state->getPath());
455 bufpos+=sprintf(bufpos,"CX%.1f ", state->getCurX());
456 bufpos+=sprintf(bufpos,"CY%.1f ", state->getCurY());
457 if(state->getLineX())
458 bufpos+=sprintf(bufpos,"LX%.1f ", state->getLineX());
459 if(state->getLineY())
460 bufpos+=sprintf(bufpos,"LY%.1f ", state->getLineY());
461 bufpos+=sprintf(bufpos," ");
465 static void dumpFontInfo(char*loglevel, GfxFont*font);
466 static int lastdumps[1024];
467 static int lastdumppos = 0;
472 static void showFontError(GfxFont*font, int nr)
476 for(t=0;t<lastdumppos;t++)
477 if(lastdumps[t] == r->num)
481 if(lastdumppos<sizeof(lastdumps)/sizeof(int))
482 lastdumps[lastdumppos++] = r->num;
484 msg("<warning> The following font caused problems:");
486 msg("<warning> The following font caused problems (substituting):");
488 msg("<warning> The following Type 3 Font will be rendered as bitmap:");
489 dumpFontInfo("<warning>", font);
492 static void dumpFontInfo(char*loglevel, GfxFont*font)
494 char* id = getFontID(font);
495 char* name = getFontName(font);
496 Ref* r=font->getID();
497 msg("%s=========== %s (ID:%d,%d) ==========\n", loglevel, name, r->num,r->gen);
499 GString*gstr = font->getTag();
501 msg("%s| Tag: %s\n", loglevel, id);
503 if(font->isCIDFont()) msg("%s| is CID font\n", loglevel);
505 GfxFontType type=font->getType();
507 case fontUnknownType:
508 msg("%s| Type: unknown\n",loglevel);
511 msg("%s| Type: 1\n",loglevel);
514 msg("%s| Type: 1C\n",loglevel);
517 msg("%s| Type: 3\n",loglevel);
520 msg("%s| Type: TrueType\n",loglevel);
523 msg("%s| Type: CIDType0\n",loglevel);
526 msg("%s| Type: CIDType0C\n",loglevel);
529 msg("%s| Type: CIDType2\n",loglevel);
534 GBool embedded = font->getEmbeddedFontID(&embRef);
536 if(font->getEmbeddedFontName()) {
537 embeddedName = font->getEmbeddedFontName()->getCString();
540 msg("%s| Embedded id: %s id: %d\n",loglevel, FIXNULL(embeddedName), embRef.num);
542 gstr = font->getExtFontFile();
544 msg("%s| External Font file: %s\n", loglevel, FIXNULL(gstr->getCString()));
546 // Get font descriptor flags.
547 if(font->isFixedWidth()) msg("%s| is fixed width\n", loglevel);
548 if(font->isSerif()) msg("%s| is serif\n", loglevel);
549 if(font->isSymbolic()) msg("%s| is symbolic\n", loglevel);
550 if(font->isItalic()) msg("%s| is italic\n", loglevel);
551 if(font->isBold()) msg("%s| is bold\n", loglevel);
557 //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");}
558 //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");}
561 void dump_outline(gfxline_t*line)
564 if(line->type == gfx_moveTo) {
565 msg("<debug> | moveTo %.2f %.2f", line->x,line->y);
566 } else if(line->type == gfx_lineTo) {
567 msg("<debug> | lineTo %.2f %.2f", line->x,line->y);
568 } else if(line->type == gfx_splineTo) {
569 msg("<debug> | splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y);
575 gfxline_t* gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user_movex, int user_movey)
577 int num = path->getNumSubpaths();
580 double lastx=0,lasty=0,posx=0,posy=0;
583 msg("<warning> empty path");
587 gfxdrawer_target_gfxline(&draw);
589 for(t = 0; t < num; t++) {
590 GfxSubpath *subpath = path->getSubpath(t);
591 int subnum = subpath->getNumPoints();
592 double bx=0,by=0,cx=0,cy=0;
594 for(s=0;s<subnum;s++) {
597 state->transform(subpath->getX(s),subpath->getY(s),&x,&y);
602 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
603 draw.lineTo(&draw, lastx, lasty);
605 draw.moveTo(&draw, x,y);
610 } else if(subpath->getCurve(s) && cpos==0) {
614 } else if(subpath->getCurve(s) && cpos==1) {
622 draw.lineTo(&draw, x,y);
624 gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y, 0.05);
631 /* fix non-closed lines */
632 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
633 draw.lineTo(&draw, lastx, lasty);
635 gfxline_t*result = (gfxline_t*)draw.result(&draw);
637 gfxline_optimize(result);
642 GBool GFXOutputDev::useTilingPatternFill()
644 infofeature("tiled patterns");
648 GBool GFXOutputDev::useShadedFills()
650 infofeature("shaded fills");
654 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line)
656 int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
657 int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
658 double miterLimit = state->getMiterLimit();
659 double width = state->getTransformedLineWidth();
662 double opaq = state->getStrokeOpacity();
664 state->getFillRGB(&rgb);
666 state->getStrokeRGB(&rgb);
668 col.r = colToByte(rgb.r);
669 col.g = colToByte(rgb.g);
670 col.b = colToByte(rgb.b);
671 col.a = (unsigned char)(opaq*255);
673 gfx_capType capType = gfx_capRound;
674 if(lineCap == 0) capType = gfx_capButt;
675 else if(lineCap == 1) capType = gfx_capRound;
676 else if(lineCap == 2) capType = gfx_capSquare;
678 gfx_joinType joinType = gfx_joinRound;
679 if(lineJoin == 0) joinType = gfx_joinMiter;
680 else if(lineJoin == 1) joinType = gfx_joinRound;
681 else if(lineJoin == 2) joinType = gfx_joinBevel;
684 double dashphase = 0;
686 state->getLineDash(&ldash, &dashnum, &dashphase);
690 if(dashnum && ldash) {
691 float * dash = (float*)malloc(sizeof(float)*(dashnum+1));
695 msg("<trace> %d dashes", dashnum);
696 msg("<trace> | phase: %f", dashphase);
697 for(t=0;t<dashnum;t++) {
699 msg("<trace> | d%-3d: %f", t, ldash[t]);
702 if(getLogLevel() >= LOGLEVEL_TRACE) {
706 line2 = gfxtool_dash_line(line, dash, dashphase);
709 msg("<trace> After dashing:");
712 if(getLogLevel() >= LOGLEVEL_TRACE) {
713 msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x\n",
715 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
716 lineCap==0?"butt": (lineJoin==1?"round":"square"),
718 col.r,col.g,col.b,col.a
723 //swfoutput_drawgfxline(output, line, width, &col, capType, joinType, miterLimit);
724 device->stroke(device, line, width, &col, capType, joinType, miterLimit);
730 gfxcolor_t getFillColor(GfxState * state)
733 double opaq = state->getFillOpacity();
734 state->getFillRGB(&rgb);
736 col.r = colToByte(rgb.r);
737 col.g = colToByte(rgb.g);
738 col.b = colToByte(rgb.b);
739 col.a = (unsigned char)(opaq*255);
743 void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line)
745 gfxcolor_t col = getFillColor(state);
747 if(getLogLevel() >= LOGLEVEL_TRACE) {
748 msg("<trace> fill %02x%02x%02x%02x\n", col.r, col.g, col.b, col.a);
751 device->fill(device, line, &col);
754 void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line)
756 if(getLogLevel() >= LOGLEVEL_TRACE) {
757 msg("<trace> clip\n");
761 device->startclip(device, line);
762 states[statepos].clipping++;
765 void GFXOutputDev::clip(GfxState *state)
767 GfxPath * path = state->getPath();
768 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
769 clipToGfxLine(state, line);
773 void GFXOutputDev::eoClip(GfxState *state)
775 GfxPath * path = state->getPath();
776 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
778 if(getLogLevel() >= LOGLEVEL_TRACE) {
779 msg("<trace> eoclip\n");
783 device->startclip(device, line);
784 states[statepos].clipping++;
788 void GFXOutputDev::endframe()
791 device->endclip(device);
795 device->endpage(device);
798 void GFXOutputDev::finish()
802 device->endclip(device);
808 GFXOutputDev::~GFXOutputDev()
813 free(this->pages); this->pages = 0;
816 fontlist_t*l = this->fontlist;
818 fontlist_t*next = l->next;
820 gfxfont_free(l->font);
821 free(l->filename);l->filename=0;
827 GBool GFXOutputDev::upsideDown()
831 GBool GFXOutputDev::useDrawChar()
836 char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
837 "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
839 #define RENDER_FILL 0
840 #define RENDER_STROKE 1
841 #define RENDER_FILLSTROKE 2
842 #define RENDER_INVISIBLE 3
843 #define RENDER_CLIP 4
845 static char tmp_printstr[4096];
846 char* makeStringPrintable(char*str)
848 int len = strlen(str);
863 tmp_printstr[len++] = '.';
864 tmp_printstr[len++] = '.';
865 tmp_printstr[len++] = '.';
867 tmp_printstr[len] = 0;
872 int getGfxCharID(gfxfont_t*font, int charnr, char *charname, int u)
877 /* find out char name from unicode index
878 TODO: should be precomputed
880 for(t=0;t<sizeof(nameToUnicodeTab)/sizeof(nameToUnicodeTab[0]);t++) {
881 if(nameToUnicodeTab[t].u == u) {
882 uniname = nameToUnicodeTab[t].name;
890 for(t=0;t<font->num_glyphs;t++) {
891 if(font->glyphs[t].name && !strcmp(font->glyphs[t].name,charname)) {
892 msg("<debug> Char [%d,>%s<,%d] maps to %d\n", charnr, charname, u, t);
896 /* if we didn't find the character, maybe
897 we can find the capitalized version */
898 for(t=0;t<font->num_glyphs;t++) {
899 if(font->glyphs[t].name && !strcasecmp(font->glyphs[t].name,charname)) {
900 msg("<debug> Char [%d,>>%s<<,%d] maps to %d\n", charnr, charname, u, t);
908 for(t=0;t<font->num_glyphs;t++) {
909 if(font->glyphs[t].name && !strcmp(font->glyphs[t].name,uniname)) {
910 msg("<debug> Char [%d,%s,>%d(%s)<] maps to %d\n", charnr, charname, u, uniname, t);
914 /* if we didn't find the character, maybe
915 we can find the capitalized version */
916 for(t=0;t<font->num_glyphs;t++) {
917 if(font->glyphs[t].name && !strcasecmp(font->glyphs[t].name,uniname)) {
918 msg("<debug> Char [%d,%s,>>%d(%s)<<] maps to %d\n", charnr, charname, u, uniname, t);
924 /* try to use the unicode id */
925 if(u>=0 && u<font->max_unicode && font->unicode2glyph[u]>=0) {
926 msg("<debug> Char [%d,%s,>%d<] maps to %d\n", charnr, charname, u, font->unicode2glyph[u]);
927 return font->unicode2glyph[u];
930 if(charnr>=0 && charnr<font->num_glyphs) {
931 msg("<debug> Char [>%d<,%s,%d] maps to %d\n", charnr, charname, u, charnr);
939 void GFXOutputDev::beginString(GfxState *state, GString *s)
941 int render = state->getRender();
942 if(current_text_stroke) {
943 msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
946 msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
947 double m11,m21,m12,m22;
948 // msg("<debug> %s beginstring \"%s\"\n", gfxstate2str(state), s->getCString());
949 state->getFontTransMat(&m11, &m12, &m21, &m22);
950 m11 *= state->getHorizScaling();
951 m21 *= state->getHorizScaling();
953 this->current_font_matrix.m00 = m11 / 1024.0;
954 this->current_font_matrix.m01 = m12 / 1024.0;
955 this->current_font_matrix.m10 = -m21 / 1024.0;
956 this->current_font_matrix.m11 = -m22 / 1024.0;
957 this->current_font_matrix.tx = 0;
958 this->current_font_matrix.ty = 0;
960 gfxmatrix_t m = this->current_font_matrix;
962 states[statepos].textRender = render;
965 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
966 double dx, double dy,
967 double originX, double originY,
968 CharCode c, int nBytes, Unicode *_u, int uLen)
970 int render = state->getRender();
971 // check for invisible text -- this is used by Acrobat Capture
973 msg("<debug> Ignoring invisible text: char %d at %f,%f", c, x, y);
977 if(states[statepos].textRender != render)
978 msg("<error> Internal error: drawChar.render!=beginString.render");
980 gfxcolor_t col = getFillColor(state);
982 Gushort *CIDToGIDMap = 0;
983 GfxFont*font = state->getFont();
985 if(font->getType() == fontType3) {
986 /* type 3 chars are passed as graphics */
987 msg("<debug> type3 char at %f/%f", x, y);
997 if(font->isCIDFont()) {
998 GfxCIDFont*cfont = (GfxCIDFont*)font;
1000 if(font->getType() == fontCIDType2)
1001 CIDToGIDMap = cfont->getCIDToGID();
1004 font8 = (Gfx8BitFont*)font;
1005 char**enc=font8->getEncoding();
1009 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);
1012 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);
1018 charid = getGfxCharID(current_gfxfont, c, name, u);
1020 charid = getGfxCharID(current_gfxfont, c, name, -1);
1023 /* multiple unicodes- should usually map to a ligature.
1024 if the ligature doesn't exist, we need to draw
1025 the characters one-by-one. */
1027 msg("<warning> ligature %d missing in font %s\n", c, current_gfxfont->id);
1028 for(t=0;t<uLen;t++) {
1029 drawChar(state, x, y, dx, dy, originX, originY, c, nBytes, _u+t, 1);
1035 msg("<warning> Didn't find character '%s' (c=%d,u=%d) in current charset (%s, %d characters)",
1036 FIXNULL(name),c, u, FIXNULL((char*)current_gfxfont->id), current_gfxfont->num_glyphs);
1040 gfxmatrix_t m = this->current_font_matrix;
1041 state->transform(x, y, &m.tx, &m.ty);
1042 m.tx += user_movex + clipmovex;
1043 m.ty += user_movey + clipmovey;
1045 if(render == RENDER_FILL) {
1046 device->drawchar(device, current_gfxfont, charid, &col, &m);
1048 msg("<debug> Drawing glyph %d as shape", charid);
1050 msg("<notice> Some texts will be rendered as shape");
1053 gfxline_t*glyph = current_gfxfont->glyphs[charid].line;
1054 gfxline_t*tglyph = gfxline_clone(glyph);
1055 gfxline_transform(tglyph, &m);
1056 if((render&3) != RENDER_INVISIBLE) {
1057 gfxline_t*add = gfxline_clone(tglyph);
1058 current_text_stroke = gfxline_append(current_text_stroke, add);
1060 if(render&RENDER_CLIP) {
1061 gfxline_t*add = gfxline_clone(tglyph);
1062 current_text_clip = gfxline_append(current_text_clip, add);
1064 gfxline_free(tglyph);
1068 void GFXOutputDev::endString(GfxState *state)
1070 int render = state->getRender();
1071 msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
1072 if(states[statepos].textRender != render)
1073 msg("<error> Internal error: drawChar.render!=beginString.render");
1075 if(current_text_stroke) {
1076 /* fillstroke and stroke text rendering objects we can process right
1077 now (as there may be texts of other rendering modes in this
1078 text object)- clipping objects have to wait until endTextObject,
1080 device->setparameter(device, "mark","TXT");
1081 if((render&3) == RENDER_FILL) {
1082 fillGfxLine(state, current_text_stroke);
1083 gfxline_free(current_text_stroke);
1084 current_text_stroke = 0;
1085 } else if((render&3) == RENDER_FILLSTROKE) {
1086 fillGfxLine(state, current_text_stroke);
1087 strokeGfxline(state, current_text_stroke);
1088 gfxline_free(current_text_stroke);
1089 current_text_stroke = 0;
1090 } else if((render&3) == RENDER_STROKE) {
1091 strokeGfxline(state, current_text_stroke);
1092 gfxline_free(current_text_stroke);
1093 current_text_stroke = 0;
1095 device->setparameter(device, "mark","");
1099 void GFXOutputDev::endTextObject(GfxState *state)
1101 int render = state->getRender();
1102 msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1103 if(states[statepos].textRender != render)
1104 msg("<error> Internal error: drawChar.render!=beginString.render");
1106 if(current_text_clip) {
1107 device->setparameter(device, "mark","TXT");
1108 clipToGfxLine(state, current_text_clip);
1109 device->setparameter(device, "mark","");
1110 gfxline_free(current_text_clip);
1111 current_text_clip = 0;
1115 /* the logic seems to be as following:
1116 first, beginType3Char is called, with the charcode and the coordinates.
1117 if this function returns true, it already knew about the char and has now drawn it.
1118 if the function returns false, it's a new char, and type3D1 is called with some parameters-
1119 the all draw operations until endType3Char are part of the char (which in this moment is
1120 at the position first passed to beginType3Char). the char ends with endType3Char.
1122 The drawing operations between beginType3Char and endType3Char are somewhat different to
1123 the normal ones. For example, the fillcolor equals the stroke color.
1126 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode code, Unicode *u, int uLen)
1128 msg("<debug> beginType3Char %d, %08x, %d", code, *u, uLen);
1130 /* the character itself is going to be passed using the draw functions */
1131 return gFalse; /* gTrue= is_in_cache? */
1134 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1135 msg("<debug> type3D0 width=%f height=%f", wx, wy);
1137 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1138 msg("<debug> type3D1 width=%f height=%f bbox=(%f,%f,%f,%f)", wx, wy,
1142 void GFXOutputDev::endType3Char(GfxState *state)
1145 msg("<debug> endType3Char");
1148 void GFXOutputDev::startFrame(int width, int height)
1150 if(outer_clip_box) {
1151 device->endclip(device);
1155 device->startpage(device, width, height);
1156 this->width = width;
1157 this->height = height;
1160 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
1162 this->currentpage = pageNum;
1164 int rot = doc->getPageRotate(1);
1167 gfxline_t clippath[5];
1169 white.r = white.g = white.b = white.a = 255;
1171 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1172 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1173 Use CropBox, not MediaBox, as page size
1180 state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1181 state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1183 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1184 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1186 this->clipmovex = -(int)x1;
1187 this->clipmovey = -(int)y1;
1189 /* apply user clip box */
1190 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1191 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1192 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1193 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1194 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1195 msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1198 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1200 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);
1202 msg("<verbose> page is rotated %d degrees\n", rot);
1204 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1205 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1206 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1207 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1208 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1209 device->startclip(device, clippath); outer_clip_box = 1;
1210 device->fill(device, clippath, &white);
1214 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1216 double x1, y1, x2, y2, w;
1217 gfxline_t points[5];
1220 msg("<debug> drawlink\n");
1222 link->getRect(&x1, &y1, &x2, &y2);
1223 cvtUserToDev(x1, y1, &x, &y);
1224 points[0].type = gfx_moveTo;
1225 points[0].x = points[4].x = x + user_movex + clipmovex;
1226 points[0].y = points[4].y = y + user_movey + clipmovey;
1227 points[0].next = &points[1];
1228 cvtUserToDev(x2, y1, &x, &y);
1229 points[1].type = gfx_lineTo;
1230 points[1].x = x + user_movex + clipmovex;
1231 points[1].y = y + user_movey + clipmovey;
1232 points[1].next = &points[2];
1233 cvtUserToDev(x2, y2, &x, &y);
1234 points[2].type = gfx_lineTo;
1235 points[2].x = x + user_movex + clipmovex;
1236 points[2].y = y + user_movey + clipmovey;
1237 points[2].next = &points[3];
1238 cvtUserToDev(x1, y2, &x, &y);
1239 points[3].type = gfx_lineTo;
1240 points[3].x = x + user_movex + clipmovex;
1241 points[3].y = y + user_movey + clipmovey;
1242 points[3].next = &points[4];
1243 cvtUserToDev(x1, y1, &x, &y);
1244 points[4].type = gfx_lineTo;
1245 points[4].x = x + user_movex + clipmovex;
1246 points[4].y = y + user_movey + clipmovey;
1249 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f\n",
1250 points[0].x, points[0].y,
1251 points[1].x, points[1].y,
1252 points[2].x, points[2].y,
1253 points[3].x, points[3].y);
1255 LinkAction*action=link->getAction();
1261 msg("<trace> drawlink action=%d\n", action->getKind());
1262 switch(action->getKind())
1266 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1267 LinkDest *dest=NULL;
1268 if (ha->getDest()==NULL)
1269 dest=catalog->findDest(ha->getNamedDest());
1270 else dest=ha->getDest();
1272 if (dest->isPageRef()){
1273 Ref pageref=dest->getPageRef();
1274 page=catalog->findPage(pageref.num,pageref.gen);
1276 else page=dest->getPageNum();
1277 sprintf(buf, "%d", page);
1284 LinkGoToR*l = (LinkGoToR*)action;
1285 GString*g = l->getFileName();
1287 s = strdup(g->getCString());
1289 /* if the GoToR link has no filename, then
1290 try to find a refernce in the *local*
1292 GString*g = l->getNamedDest();
1294 s = strdup(g->getCString());
1300 LinkNamed*l = (LinkNamed*)action;
1301 GString*name = l->getName();
1303 s = strdup(name->lowerCase()->getCString());
1304 named = name->getCString();
1307 if(strstr(s, "next") || strstr(s, "forward"))
1309 page = currentpage + 1;
1311 else if(strstr(s, "prev") || strstr(s, "back"))
1313 page = currentpage - 1;
1315 else if(strstr(s, "last") || strstr(s, "end"))
1317 if(pages && pagepos>0)
1318 page = pages[pagepos-1];
1320 else if(strstr(s, "first") || strstr(s, "top"))
1328 case actionLaunch: {
1330 LinkLaunch*l = (LinkLaunch*)action;
1331 GString * str = new GString(l->getFileName());
1332 GString * params = l->getParams();
1334 str->append(params);
1335 s = strdup(str->getCString());
1342 LinkURI*l = (LinkURI*)action;
1343 GString*g = l->getURI();
1345 url = g->getCString();
1350 case actionUnknown: {
1352 LinkUnknown*l = (LinkUnknown*)action;
1357 msg("<error> Unknown link type!\n");
1362 if(!s) s = strdup("-?-");
1364 msg("<trace> drawlink s=%s\n", s);
1366 if(!linkinfo && (page || s))
1368 msg("<notice> File contains links");
1376 for(t=1;t<=pagepos;t++) {
1377 if(pages[t]==page) {
1386 sprintf(buf, "page%d", lpage);
1387 device->drawlink(device, points, buf);
1391 device->drawlink(device, points, s);
1394 msg("<verbose> \"%s\" link to \"%s\" (%d)\n", type, FIXNULL(s), page);
1398 void GFXOutputDev::saveState(GfxState *state) {
1399 dbg("saveState");dbgindent+=2;
1401 msg("<trace> saveState\n");
1404 msg("<error> Too many nested states in pdf.");
1408 states[statepos].textRender = states[statepos-1].textRender;
1409 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1410 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1411 states[statepos].clipping = 0;
1414 void GFXOutputDev::restoreState(GfxState *state) {
1415 dbgindent-=2; dbg("restoreState");
1418 msg("<error> Invalid restoreState");
1421 msg("<trace> restoreState");
1422 if(states[statepos].softmask) {
1423 clearSoftMask(state);
1426 while(states[statepos].clipping) {
1427 device->endclip(device);
1428 states[statepos].clipping--;
1433 char* writeOutStdFont(fontentry* f)
1438 char* tmpFileName = mktmpname(namebuf1);
1440 sprintf(namebuf2, "%s.afm", tmpFileName);
1441 fi = fopen(namebuf2, "wb");
1444 fwrite(f->afm, 1, f->afmlen, fi);
1447 sprintf(namebuf2, "%s.pfb", tmpFileName);
1448 fi = fopen(namebuf2, "wb");
1451 fwrite(f->pfb, 1, f->pfblen, fi);
1454 return strdup(namebuf2);
1457 char* GFXOutputDev::searchFont(char*name)
1462 msg("<verbose> SearchFont(%s)", name);
1464 /* see if it is a pdf standard font */
1465 for(i=0;i<sizeof(pdf2t1map)/sizeof(fontentry);i++)
1467 if(!strcmp(name, pdf2t1map[i].pdffont))
1469 if(!pdf2t1map[i].fullfilename) {
1470 pdf2t1map[i].fullfilename = writeOutStdFont(&pdf2t1map[i]);
1471 if(!pdf2t1map[i].fullfilename) {
1472 msg("<error> Couldn't save default font- is the Temp Directory writable?");
1474 msg("<verbose> Storing standard PDF font %s at %s", name, pdf2t1map[i].fullfilename);
1477 return strdup(pdf2t1map[i].fullfilename);
1480 /* else look in all font files */
1481 for(i=0;i<fontnum;i++)
1483 if(strstr(fonts[i].filename, name)) {
1484 return strdup(fonts[i].filename);
1490 void GFXOutputDev::updateLineWidth(GfxState *state)
1492 double width = state->getTransformedLineWidth();
1493 //swfoutput_setlinewidth(&device, width);
1496 void GFXOutputDev::updateLineCap(GfxState *state)
1498 int c = state->getLineCap();
1501 void GFXOutputDev::updateLineJoin(GfxState *state)
1503 int j = state->getLineJoin();
1506 void GFXOutputDev::updateFillColor(GfxState *state)
1509 double opaq = state->getFillOpacity();
1510 state->getFillRGB(&rgb);
1512 void GFXOutputDev::updateFillOpacity(GfxState *state)
1515 double opaq = state->getFillOpacity();
1516 state->getFillRGB(&rgb);
1517 dbg("update fillopaq %f", opaq);
1519 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1521 double opaq = state->getFillOpacity();
1522 dbg("update strokeopaq %f", opaq);
1524 void GFXOutputDev::updateFillOverprint(GfxState *state)
1526 double opaq = state->getFillOverprint();
1527 dbg("update filloverprint %f", opaq);
1529 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1531 double opaq = state->getStrokeOverprint();
1532 dbg("update strokeoverprint %f", opaq);
1534 void GFXOutputDev::updateTransfer(GfxState *state)
1536 dbg("update transfer");
1540 void GFXOutputDev::updateStrokeColor(GfxState *state)
1543 double opaq = state->getStrokeOpacity();
1544 state->getStrokeRGB(&rgb);
1547 void FoFiWrite(void *stream, char *data, int len)
1549 int ret = fwrite(data, len, 1, (FILE*)stream);
1552 char*GFXOutputDev::writeEmbeddedFontToFile(XRef*ref, GfxFont*font)
1554 char*tmpFileName = NULL;
1560 Object refObj, strObj;
1562 tmpFileName = mktmpname(namebuf);
1565 ret = font->getEmbeddedFontID(&embRef);
1567 msg("<verbose> Didn't get embedded font id");
1568 /* not embedded- the caller should now search the font
1569 directories for this font */
1573 f = fopen(tmpFileName, "wb");
1575 msg("<error> Couldn't create temporary Type 1 font file");
1579 /*if(font->isCIDFont()) {
1580 GfxCIDFont* cidFont = (GfxCIDFont *)font;
1581 GString c = cidFont->getCollection();
1582 msg("<notice> Collection: %s", c.getCString());
1585 //if (font->getType() == fontType1C) {
1586 if (0) { //font->getType() == fontType1C) {
1587 if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
1589 msg("<error> Couldn't read embedded font file");
1592 FoFiType1C *cvt = FoFiType1C::make(fontBuf, fontLen);
1594 cvt->convertToType1(0, NULL, gTrue, FoFiWrite, f);
1595 //cvt->convertToCIDType0("test", f);
1596 //cvt->convertToType0("test", f);
1599 } else if(font->getType() == fontTrueType) {
1600 msg("<verbose> writing font using TrueTypeFontFile::writeTTF");
1601 if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
1603 msg("<error> Couldn't read embedded font file");
1606 FoFiTrueType *cvt = FoFiTrueType::make(fontBuf, fontLen);
1607 cvt->writeTTF(FoFiWrite, f);
1611 font->getEmbeddedFontID(&embRef);
1612 refObj.initRef(embRef.num, embRef.gen);
1613 refObj.fetch(ref, &strObj);
1615 strObj.streamReset();
1620 f4[t] = strObj.streamGetChar();
1621 f4c[t] = (char)f4[t];
1626 if(!strncmp(f4c, "true", 4)) {
1627 /* some weird TTF fonts don't start with 0,1,0,0 but with "true".
1628 Change this on the fly */
1629 f4[0] = f4[2] = f4[3] = 0;
1637 while ((c = strObj.streamGetChar()) != EOF) {
1641 strObj.streamClose();
1646 return strdup(tmpFileName);
1649 char* GFXOutputDev::searchForSuitableFont(GfxFont*gfxFont)
1651 char*name = getFontName(gfxFont);
1655 if(!this->config_use_fontconfig)
1658 #ifdef HAVE_FONTCONFIG
1659 FcPattern *pattern, *match;
1663 static int fcinitcalled = false;
1665 msg("<debug> searchForSuitableFont(%s)", name);
1667 // call init ony once
1668 if (!fcinitcalled) {
1669 msg("<debug> Initializing FontConfig...");
1670 fcinitcalled = true;
1672 msg("<debug> FontConfig Initialization failed. Disabling.");
1673 config_use_fontconfig = 0;
1676 msg("<debug> ...initialized FontConfig");
1679 msg("<debug> FontConfig: Create \"%s\" Family Pattern", name);
1680 pattern = FcPatternBuild(NULL, FC_FAMILY, FcTypeString, name, NULL);
1681 if (gfxFont->isItalic()) // check for italic
1682 msg("<debug> FontConfig: Adding Italic Slant");
1683 FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
1684 if (gfxFont->isBold()) // check for bold
1685 msg("<debug> FontConfig: Adding Bold Weight");
1686 FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
1688 msg("<debug> FontConfig: Try to match...");
1689 // configure and match using the original font name
1690 FcConfigSubstitute(0, pattern, FcMatchPattern);
1691 FcDefaultSubstitute(pattern);
1692 match = FcFontMatch(0, pattern, &result);
1694 if (FcPatternGetString(match, "family", 0, &v) == FcResultMatch) {
1695 msg("<debug> FontConfig: family=%s", (char*)v);
1696 // if we get an exact match
1697 if (strcmp((char *)v, name) == 0) {
1698 if (FcPatternGetString(match, "file", 0, &v) == FcResultMatch) {
1699 filename = strdup((char*)v); // mem leak
1700 char *nfn = strrchr(filename, '/');
1701 if(nfn) fontname = strdup(nfn+1);
1702 else fontname = filename;
1704 msg("<debug> FontConfig: Returning \"%s\"", fontname);
1706 // initialize patterns
1707 FcPatternDestroy(pattern);
1708 FcPatternDestroy(match);
1710 // now match against serif etc.
1711 if (gfxFont->isSerif()) {
1712 msg("<debug> FontConfig: Create Serif Family Pattern");
1713 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "serif", NULL);
1714 } else if (gfxFont->isFixedWidth()) {
1715 msg("<debug> FontConfig: Create Monospace Family Pattern");
1716 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "monospace", NULL);
1718 msg("<debug> FontConfig: Create Sans Family Pattern");
1719 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "sans", NULL);
1723 if (gfxFont->isItalic()) {
1724 msg("<debug> FontConfig: Adding Italic Slant");
1725 int bb = FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
1728 if (gfxFont->isBold()) {
1729 msg("<debug> FontConfig: Adding Bold Weight");
1730 int bb = FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
1733 msg("<debug> FontConfig: Try to match... (2)");
1734 // configure and match using serif etc
1735 FcConfigSubstitute (0, pattern, FcMatchPattern);
1736 FcDefaultSubstitute (pattern);
1737 match = FcFontMatch (0, pattern, &result);
1739 if (FcPatternGetString(match, "file", 0, &v) == FcResultMatch) {
1740 filename = strdup((char*)v); // mem leak
1741 char *nfn = strrchr(filename, '/');
1742 if(nfn) fontname = strdup(nfn+1);
1743 else fontname = filename;
1745 msg("<debug> FontConfig: Returning \"%s\"", fontname);
1749 //printf("FONTCONFIG: pattern");
1750 //FcPatternPrint(pattern);
1751 //printf("FONTCONFIG: match");
1752 //FcPatternPrint(match);
1754 FcPatternDestroy(pattern);
1755 FcPatternDestroy(match);
1757 pdfswf_addfont(filename);
1764 char* GFXOutputDev::substituteFont(GfxFont*gfxFont, char* oldname)
1766 char*fontname = 0, *filename = 0;
1767 msg("<notice> substituteFont(%s)", oldname);
1769 if(!(fontname = searchForSuitableFont(gfxFont))) {
1770 fontname = "Times-Roman";
1772 filename = searchFont(fontname);
1774 msg("<error> Couldn't find font %s- did you install the default fonts?", fontname);
1778 if(substitutepos>=sizeof(substitutesource)/sizeof(char*)) {
1779 msg("<fatal> Too many fonts in file.");
1783 substitutesource[substitutepos] = strdup(oldname); //mem leak
1784 substitutetarget[substitutepos] = fontname;
1785 msg("<notice> substituting %s -> %s", FIXNULL(oldname), FIXNULL(fontname));
1788 return strdup(filename); //mem leak
1791 void unlinkfont(char* filename)
1798 if(!strncmp(&filename[l-4],".afm",4)) {
1799 memcpy(&filename[l-4],".pfb",4);
1801 memcpy(&filename[l-4],".pfa",4);
1803 memcpy(&filename[l-4],".afm",4);
1806 if(!strncmp(&filename[l-4],".pfa",4)) {
1807 memcpy(&filename[l-4],".afm",4);
1809 memcpy(&filename[l-4],".pfa",4);
1812 if(!strncmp(&filename[l-4],".pfb",4)) {
1813 memcpy(&filename[l-4],".afm",4);
1815 memcpy(&filename[l-4],".pfb",4);
1820 void GFXOutputDev::setXRef(PDFDoc*doc, XRef *xref)
1826 int GFXOutputDev::setGfxFont(char*id, char*name, char*filename, double maxSize)
1829 fontlist_t*last=0,*l = this->fontlist;
1832 msg("<error> Internal Error: FontID is null");
1834 /* TODO: should this be part of the state? */
1837 if(!strcmp(l->font->id, id)) {
1838 current_gfxfont = l->font;
1840 device->addfont(device, current_gfxfont);
1845 if(!filename) return 0;
1847 /* A font size of e.g. 9 means the font will be scaled down by
1848 1024 and scaled up by 9. So to have a maximum error of 1/20px,
1849 we have to divide 0.05 by (fontsize/1024)
1851 double quality = (1024 * 0.05) / maxSize;
1853 msg("<verbose> Loading %s...", filename);
1854 font = gfxfont_load(id, filename, quality);
1856 msg("<verbose> Couldn't load Font %s (%s)", filename, id);
1859 msg("<verbose> Font %s (%s) loaded successfully", filename, id);
1863 l->filename = strdup(filename);
1865 current_gfxfont = l->font;
1871 device->addfont(device, current_gfxfont);
1875 void GFXOutputDev::updateFont(GfxState *state)
1877 GfxFont*gfxFont = state->getFont();
1883 char * fontid = getFontID(gfxFont);
1884 char * fontname = getFontName(gfxFont);
1886 double maxSize = 1.0;
1889 maxSize = this->info->getMaximumFontSize(fontid);
1893 /* first, look if we substituted this font before-
1894 this way, we don't initialize the T1 Fonts
1896 for(t=0;t<substitutepos;t++) {
1897 if(!strcmp(fontid, substitutesource[t])) {
1898 free(fontid);fontid=0;
1899 fontid = strdup(substitutetarget[t]);
1904 /* second, see if this is a font which was used before-
1905 if so, we are done */
1906 if(setGfxFont(fontid, fontname, 0, 0)) {
1911 /* if(swfoutput_queryfont(&device, fontid))
1912 swfoutput_setfont(&device, fontid, 0);
1914 msg("<debug> updateFont(%s) [cached]", fontid);
1918 // look for Type 3 font
1919 if (gfxFont->getType() == fontType3) {
1921 type3Warning = gTrue;
1922 showFontError(gfxFont, 2);
1929 /* now either load the font, or find a substitution */
1932 GBool embedded = gfxFont->getEmbeddedFontID(&embRef);
1937 (gfxFont->getType() == fontType1 ||
1938 gfxFont->getType() == fontType1C ||
1939 gfxFont->getType() == fontCIDType0C ||
1940 gfxFont->getType() == fontTrueType ||
1941 gfxFont->getType() == fontCIDType2
1944 fileName = writeEmbeddedFontToFile(xref, gfxFont);
1945 if(!fileName) showFontError(gfxFont,0);
1948 fileName = searchFont(fontname);
1949 if(!fileName) showFontError(gfxFont,0);
1952 char * fontname = getFontName(gfxFont);
1953 msg("<warning> Font %s %scould not be loaded.", fontname, embedded?"":"(not embedded) ");
1956 msg("<warning> Try putting a TTF version of that font (named \"%s.ttf\") into %s", fontname, lastfontdir);
1958 msg("<warning> Try specifying one or more font directories");
1960 fileName = substituteFont(gfxFont, fontid);
1963 if(fontid) { free(fontid);fontid = strdup(substitutetarget[substitutepos-1]); /*ugly hack*/};
1964 msg("<notice> Font is now %s (%s)", fontid, fileName);
1968 msg("<error> Couldn't set font %s\n", fontid);
1974 msg("<verbose> updateFont(%s) -> %s (max size: %f)", fontid, fileName, maxSize);
1975 dumpFontInfo("<verbose>", gfxFont);
1977 //swfoutput_setfont(&device, fontid, fileName);
1979 if(!setGfxFont(fontid, fontname, 0, 0)) {
1980 setGfxFont(fontid, fontname, fileName, maxSize);
1984 unlinkfont(fileName);
1994 #define SQR(x) ((x)*(x))
1996 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
1998 if((newwidth<2 || newheight<2) ||
1999 (width<=newwidth || height<=newheight))
2001 unsigned char*newdata;
2003 newdata= (unsigned char*)malloc(newwidth*newheight);
2005 double fx = (double)(width)/newwidth;
2006 double fy = (double)(height)/newheight;
2008 int blocksize = (int)(8192/(fx*fy));
2009 int r = 8192*256/palettesize;
2010 for(x=0;x<newwidth;x++) {
2011 double ex = px + fx;
2012 int fromx = (int)px;
2014 int xweight1 = (int)(((fromx+1)-px)*256);
2015 int xweight2 = (int)((ex-tox)*256);
2017 for(y=0;y<newheight;y++) {
2018 double ey = py + fy;
2019 int fromy = (int)py;
2021 int yweight1 = (int)(((fromy+1)-py)*256);
2022 int yweight2 = (int)((ey-toy)*256);
2025 for(xx=fromx;xx<=tox;xx++)
2026 for(yy=fromy;yy<=toy;yy++) {
2027 int b = 1-data[width*yy+xx];
2029 if(xx==fromx) weight = (weight*xweight1)/256;
2030 if(xx==tox) weight = (weight*xweight2)/256;
2031 if(yy==fromy) weight = (weight*yweight1)/256;
2032 if(yy==toy) weight = (weight*yweight2)/256;
2035 //if(a) a=(palettesize-1)*r/blocksize;
2036 newdata[y*newwidth+x] = (a*blocksize)/r;
2044 #define IMAGE_TYPE_JPEG 0
2045 #define IMAGE_TYPE_LOSSLESS 1
2047 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
2048 double x1,double y1,
2049 double x2,double y2,
2050 double x3,double y3,
2051 double x4,double y4, int type)
2053 gfxcolor_t*newpic=0;
2055 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
2056 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
2058 gfxline_t p1,p2,p3,p4,p5;
2059 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
2060 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
2061 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
2062 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
2063 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
2065 {p1.x = (int)(p1.x*20)/20.0;
2066 p1.y = (int)(p1.y*20)/20.0;
2067 p2.x = (int)(p2.x*20)/20.0;
2068 p2.y = (int)(p2.y*20)/20.0;
2069 p3.x = (int)(p3.x*20)/20.0;
2070 p3.y = (int)(p3.y*20)/20.0;
2071 p4.x = (int)(p4.x*20)/20.0;
2072 p4.y = (int)(p4.y*20)/20.0;
2073 p5.x = (int)(p5.x*20)/20.0;
2074 p5.y = (int)(p5.y*20)/20.0;
2081 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2082 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2087 img.data = (gfxcolor_t*)data;
2091 if(type == IMAGE_TYPE_JPEG)
2092 /* TODO: pass image_dpi to device instead */
2093 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2095 dev->fillbitmap(dev, &p1, &img, &m, 0);
2098 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2099 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
2101 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG);
2104 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2105 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
2107 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS);
2111 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2112 int width, int height, GfxImageColorMap*colorMap, GBool invert,
2113 GBool inlineImg, int mask, int*maskColors,
2114 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2116 double x1,y1,x2,y2,x3,y3,x4,y4;
2117 ImageStream *imgStr;
2122 unsigned char* maskbitmap = 0;
2125 ncomps = colorMap->getNumPixelComps();
2126 bits = colorMap->getBits();
2131 unsigned char buf[8];
2132 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2134 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2135 imgMaskStr->reset();
2136 unsigned char pal[256];
2137 int n = 1 << colorMap->getBits();
2142 maskColorMap->getGray(pixBuf, &gray);
2143 pal[t] = colToByte(gray);
2145 for (y = 0; y < maskHeight; y++) {
2146 for (x = 0; x < maskWidth; x++) {
2147 imgMaskStr->getPixel(buf);
2148 maskbitmap[y*maskWidth+x] = pal[buf[0]];
2153 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2154 imgMaskStr->reset();
2155 for (y = 0; y < maskHeight; y++) {
2156 for (x = 0; x < maskWidth; x++) {
2157 imgMaskStr->getPixel(buf);
2159 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2167 imgStr = new ImageStream(str, width, ncomps,bits);
2170 if(!width || !height || (height<=1 && width<=1))
2172 msg("<verbose> Ignoring %d by %d image", width, height);
2173 unsigned char buf[8];
2175 for (y = 0; y < height; ++y)
2176 for (x = 0; x < width; ++x) {
2177 imgStr->getPixel(buf);
2185 state->transform(0, 1, &x1, &y1); x1 += user_movex + clipmovex; y1 += user_movey + clipmovey;
2186 state->transform(0, 0, &x2, &y2); x2 += user_movex + clipmovex; y2 += user_movey + clipmovey;
2187 state->transform(1, 0, &x3, &y3); x3 += user_movex + clipmovex; y3 += user_movey + clipmovey;
2188 state->transform(1, 1, &x4, &y4); x4 += user_movex + clipmovex; y4 += user_movey + clipmovey;
2190 if(!pbminfo && !(str->getKind()==strDCT)) {
2192 msg("<notice> file contains pbm pictures %s",mask?"(masked)":"");
2196 msg("<verbose> drawing %d by %d masked picture\n", width, height);
2198 if(!jpeginfo && (str->getKind()==strDCT)) {
2199 msg("<notice> file contains jpeg pictures");
2205 unsigned char buf[8];
2207 unsigned char*pic = new unsigned char[width*height];
2208 gfxcolor_t pal[256];
2210 state->getFillRGB(&rgb);
2212 memset(pal,255,sizeof(pal));
2213 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2214 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2215 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2216 pal[0].a = 255; pal[1].a = 0;
2219 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2220 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2221 for (y = 0; y < height; ++y)
2222 for (x = 0; x < width; ++x)
2224 imgStr->getPixel(buf);
2227 pic[width*y+x] = buf[0];
2230 /* the size of the drawn image is added to the identifier
2231 as the same image may require different bitmaps if displayed
2232 at different sizes (due to antialiasing): */
2235 unsigned char*pic2 = 0;
2238 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2247 height = realheight;
2251 /* make a black/white palette */
2253 float r = 255/(numpalette-1);
2255 for(t=0;t<numpalette;t++) {
2256 pal[t].r = colToByte(rgb.r);
2257 pal[t].g = colToByte(rgb.g);
2258 pal[t].b = colToByte(rgb.b);
2259 pal[t].a = (unsigned char)(t*r);
2263 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2264 for (y = 0; y < height; ++y) {
2265 for (x = 0; x < width; ++x) {
2266 pic2[width*y+x] = pal[pic[y*width+x]];
2269 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2273 if(maskbitmap) free(maskbitmap);
2279 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2280 gfxcolor_t*pic=new gfxcolor_t[width*height];
2281 for (y = 0; y < height; ++y) {
2282 for (x = 0; x < width; ++x) {
2283 imgStr->getPixel(pixBuf);
2284 colorMap->getRGB(pixBuf, &rgb);
2285 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2286 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2287 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2288 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2290 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2294 if(str->getKind()==strDCT)
2295 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2297 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2300 if(maskbitmap) free(maskbitmap);
2303 gfxcolor_t*pic=new gfxcolor_t[width*height];
2304 gfxcolor_t pal[256];
2305 int n = 1 << colorMap->getBits();
2307 for(t=0;t<256;t++) {
2309 colorMap->getRGB(pixBuf, &rgb);
2311 {/*if(maskColors && *maskColors==t) {
2312 msg("<notice> Color %d is transparent", t);
2313 if (imgData->maskColors) {
2315 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2316 if (pix[i] < imgData->maskColors[2*i] ||
2317 pix[i] > imgData->maskColors[2*i+1]) {
2332 pal[t].r = (unsigned char)(colToByte(rgb.r));
2333 pal[t].g = (unsigned char)(colToByte(rgb.g));
2334 pal[t].b = (unsigned char)(colToByte(rgb.b));
2335 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2338 for (y = 0; y < height; ++y) {
2339 for (x = 0; x < width; ++x) {
2340 imgStr->getPixel(pixBuf);
2341 pic[width*y+x] = pal[pixBuf[0]];
2343 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2347 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2351 if(maskbitmap) free(maskbitmap);
2356 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2357 int width, int height, GBool invert,
2360 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2361 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2362 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2365 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2366 int width, int height, GfxImageColorMap *colorMap,
2367 int *maskColors, GBool inlineImg)
2369 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
2370 colorMap?"colorMap":"no colorMap",
2371 maskColors?"maskColors":"no maskColors",
2373 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
2374 colorMap?"colorMap":"no colorMap",
2375 maskColors?"maskColors":"no maskColors",
2378 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2379 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2380 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2383 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2384 int width, int height,
2385 GfxImageColorMap *colorMap,
2386 Stream *maskStr, int maskWidth, int maskHeight,
2389 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2390 colorMap?"colorMap":"no colorMap",
2391 maskWidth, maskHeight);
2392 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2393 colorMap?"colorMap":"no colorMap",
2394 maskWidth, maskHeight);
2396 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2397 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2398 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2401 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2402 int width, int height,
2403 GfxImageColorMap *colorMap,
2405 int maskWidth, int maskHeight,
2406 GfxImageColorMap *maskColorMap)
2408 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2409 colorMap?"colorMap":"no colorMap",
2410 maskWidth, maskHeight);
2411 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2412 colorMap?"colorMap":"no colorMap",
2413 maskWidth, maskHeight);
2415 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2416 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2417 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2420 void GFXOutputDev::stroke(GfxState *state)
2424 GfxPath * path = state->getPath();
2425 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2426 strokeGfxline(state, line);
2430 void GFXOutputDev::fill(GfxState *state)
2432 gfxcolor_t col = getFillColor(state);
2433 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2435 GfxPath * path = state->getPath();
2436 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2437 fillGfxLine(state, line);
2441 void GFXOutputDev::eoFill(GfxState *state)
2443 gfxcolor_t col = getFillColor(state);
2444 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2446 GfxPath * path = state->getPath();
2447 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2448 fillGfxLine(state, line);
2453 static char* dirseparator()
2462 void addGlobalFont(char*filename)
2465 memset(&f, 0, sizeof(fontfile_t));
2466 f.filename = filename;
2467 if(fontnum < sizeof(fonts)/sizeof(fonts[0])) {
2468 msg("<verbose> Adding font \"%s\".", filename);
2469 fonts[fontnum++] = f;
2471 msg("<error> Too many external fonts. Not adding font file \"%s\".", filename);
2475 void addGlobalLanguageDir(char*dir)
2478 globalParams = new GlobalParams("");
2480 msg("<notice> Adding %s to language pack directories", dir);
2484 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2485 strcpy(config_file, dir);
2486 strcat(config_file, dirseparator());
2487 strcat(config_file, "add-to-xpdfrc");
2489 fi = fopen(config_file, "rb");
2491 msg("<error> Could not open %s", config_file);
2494 globalParams->parseFile(new GString(config_file), fi);
2498 void addGlobalFontDir(char*dirname)
2500 #ifdef HAVE_DIRENT_H
2501 msg("<notice> Adding %s to font directories", dirname);
2502 lastfontdir = strdup(dirname);
2503 DIR*dir = opendir(dirname);
2505 msg("<warning> Couldn't open directory %s\n", dirname);
2510 ent = readdir (dir);
2514 char*name = ent->d_name;
2520 if(!strncasecmp(&name[l-4], ".pfa", 4))
2522 if(!strncasecmp(&name[l-4], ".pfb", 4))
2524 if(!strncasecmp(&name[l-4], ".ttf", 4))
2528 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2529 strcpy(fontname, dirname);
2530 strcat(fontname, dirseparator());
2531 strcat(fontname, name);
2532 addGlobalFont(fontname);
2537 msg("<warning> No dirent.h- unable to add font dir %s", dir);
2541 void GFXOutputDev::preparePage(int pdfpage, int outputpage)
2547 this->pagebuflen = 1024;
2548 this->pages = (int*)malloc(this->pagebuflen*sizeof(int));
2549 memset(this->pages, -1, this->pagebuflen*sizeof(int));
2551 while(pdfpage >= this->pagebuflen)
2553 int oldlen = this->pagebuflen;
2554 this->pagebuflen+=1024;
2555 this->pages = (int*)realloc(this->pages, this->pagebuflen*sizeof(int));
2556 memset(&this->pages[oldlen], -1, (this->pagebuflen-oldlen)*sizeof(int));
2559 this->pages[pdfpage] = outputpage;
2560 if(pdfpage>this->pagepos)
2561 this->pagepos = pdfpage;
2567 double width,height;
2570 BBox mkBBox(GfxState*state, double*bbox, double width, double height)
2572 double xMin, yMin, xMax, yMax, x, y;
2573 double tx, ty, w, h;
2574 // transform the bbox
2575 state->transform(bbox[0], bbox[1], &x, &y);
2578 state->transform(bbox[0], bbox[3], &x, &y);
2581 } else if (x > xMax) {
2586 } else if (y > yMax) {
2589 state->transform(bbox[2], bbox[1], &x, &y);
2592 } else if (x > xMax) {
2597 } else if (y > yMax) {
2600 state->transform(bbox[2], bbox[3], &x, &y);
2603 } else if (x > xMax) {
2608 } else if (y > yMax) {
2611 tx = (int)floor(xMin);
2614 } else if (tx > width) {
2617 ty = (int)floor(yMin);
2620 } else if (ty > height) {
2623 w = (int)ceil(xMax) - tx + 1;
2624 if (tx + w > width) {
2630 h = (int)ceil(yMax) - ty + 1;
2631 if (ty + h > height) {
2645 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2646 GfxColorSpace *blendingColorSpace,
2647 GBool isolated, GBool knockout,
2650 char*colormodename = "";
2651 BBox rect = mkBBox(state, bbox, this->width, this->height);
2653 if(blendingColorSpace) {
2654 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2656 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);
2657 dbg("using clipping rect %f/%f/%f/%f\n", rect.posx,rect.posy,rect.width,rect.height);
2658 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);
2660 states[statepos].createsoftmask |= forSoftMask;
2661 states[statepos].transparencygroup = !forSoftMask;
2662 states[statepos].isolated = isolated;
2664 states[statepos].olddevice = this->device;
2665 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2667 gfxdevice_record_init(this->device);
2669 /*if(!forSoftMask) { ////???
2670 state->setFillOpacity(0.0);
2675 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2678 dbg("endTransparencyGroup");
2679 msg("<verbose> endTransparencyGroup");
2681 gfxdevice_t*r = this->device;
2683 this->device = states[statepos].olddevice;
2685 if(states[statepos].createsoftmask) {
2686 states[statepos-1].softmaskrecording = r->finish(r);
2688 states[statepos-1].grouprecording = r->finish(r);
2691 states[statepos].createsoftmask = 0;
2692 states[statepos].transparencygroup = 0;
2696 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2698 char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2699 "colordodge","colorburn","hardlight","softlight","difference",
2700 "exclusion","hue","saturation","color","luminosity"};
2702 dbg("paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2703 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2705 if(state->getBlendMode() == gfxBlendNormal)
2706 infofeature("transparency groups");
2709 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2710 warnfeature(buffer, 0);
2713 gfxresult_t*grouprecording = states[statepos].grouprecording;
2715 if(state->getBlendMode() == gfxBlendNormal) {
2717 gfxdevice_ops_init(&ops, this->device, (unsigned char)(state->getFillOpacity()*255));
2718 gfxresult_record_replay(grouprecording, &ops);
2721 grouprecording->destroy(grouprecording);
2723 states[statepos].grouprecording = 0;
2726 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2728 /* alpha = 1: retrieve mask values from alpha layer
2729 alpha = 0: retrieve mask values from luminance */
2730 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2731 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2732 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2733 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2735 infofeature("soft masks");
2737 warnfeature("soft masks from alpha channel",0);
2739 states[statepos].olddevice = this->device;
2740 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2741 gfxdevice_record_init(this->device);
2743 dbg("softmaskrecording is %08x at statepos %d\n", states[statepos].softmaskrecording, statepos);
2745 states[statepos].softmask = 1;
2746 states[statepos].softmask_alpha = alpha;
2749 static inline Guchar div255(int x) {
2750 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2753 void GFXOutputDev::clearSoftMask(GfxState *state)
2755 if(!states[statepos].softmask)
2757 states[statepos].softmask = 0;
2758 dbg("clearSoftMask statepos=%d", statepos);
2759 msg("<verbose> clearSoftMask");
2761 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2762 msg("<error> Error in softmask/tgroup ordering");
2766 gfxresult_t*mask = states[statepos].softmaskrecording;
2767 gfxresult_t*below = this->device->finish(this->device);
2768 this->device = states[statepos].olddevice;
2770 /* get outline of all objects below the soft mask */
2771 gfxdevice_t uniondev;
2772 gfxdevice_union_init(&uniondev, 0);
2773 gfxresult_record_replay(below, &uniondev);
2774 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2775 uniondev.finish(&uniondev);
2777 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2779 this->device->startclip(this->device, belowoutline);
2780 gfxresult_record_replay(below, this->device);
2781 gfxresult_record_replay(mask, this->device);
2782 this->device->endclip(this->device);
2783 gfxline_free(belowoutline);
2786 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2788 gfxdevice_t belowrender;
2789 gfxdevice_render_init(&belowrender);
2790 if(states[statepos+1].isolated) {
2791 belowrender.setparameter(&belowrender, "fillwhite", "1");
2793 belowrender.setparameter(&belowrender, "antialize", "2");
2794 belowrender.startpage(&belowrender, width, height);
2795 gfxresult_record_replay(below, &belowrender);
2796 belowrender.endpage(&belowrender);
2797 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2798 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2799 //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2801 gfxdevice_t maskrender;
2802 gfxdevice_render_init(&maskrender);
2803 maskrender.startpage(&maskrender, width, height);
2804 gfxresult_record_replay(mask, &maskrender);
2805 maskrender.endpage(&maskrender);
2806 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2807 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2809 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2810 msg("<fatal> Internal error in mask drawing");
2815 for(y=0;y<height;y++) {
2816 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2817 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2818 for(x=0;x<width;x++) {
2820 if(states[statepos].softmask_alpha) {
2823 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2826 /* premultiply alpha */
2827 l2->a = div255(alpha*l2->a);
2828 l2->r = div255(alpha*l2->r);
2829 l2->g = div255(alpha*l2->g);
2830 l2->b = div255(alpha*l2->b);
2836 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2839 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2840 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2842 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2844 mask->destroy(mask);
2845 below->destroy(below);
2846 maskresult->destroy(maskresult);
2847 belowresult->destroy(belowresult);
2848 states[statepos].softmaskrecording = 0;
2855 delete globalParams;globalParams=0;
2856 Object::memCheck(stderr);