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);
796 void GFXOutputDev::finish()
800 device->endclip(device);
806 GFXOutputDev::~GFXOutputDev()
811 free(this->pages); this->pages = 0;
814 fontlist_t*l = this->fontlist;
816 fontlist_t*next = l->next;
818 gfxfont_free(l->font);
819 free(l->filename);l->filename=0;
825 GBool GFXOutputDev::upsideDown()
829 GBool GFXOutputDev::useDrawChar()
834 char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
835 "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
837 #define RENDER_FILL 0
838 #define RENDER_STROKE 1
839 #define RENDER_FILLSTROKE 2
840 #define RENDER_INVISIBLE 3
841 #define RENDER_CLIP 4
843 static char tmp_printstr[4096];
844 char* makeStringPrintable(char*str)
846 int len = strlen(str);
861 tmp_printstr[len++] = '.';
862 tmp_printstr[len++] = '.';
863 tmp_printstr[len++] = '.';
865 tmp_printstr[len] = 0;
870 int getGfxCharID(gfxfont_t*font, int charnr, char *charname, int u)
875 /* find out char name from unicode index
876 TODO: should be precomputed
878 for(t=0;t<sizeof(nameToUnicodeTab)/sizeof(nameToUnicodeTab[0]);t++) {
879 if(nameToUnicodeTab[t].u == u) {
880 uniname = nameToUnicodeTab[t].name;
888 for(t=0;t<font->num_glyphs;t++) {
889 if(font->glyphs[t].name && !strcmp(font->glyphs[t].name,charname)) {
890 msg("<debug> Char [%d,>%s<,%d] maps to %d\n", charnr, charname, u, t);
894 /* if we didn't find the character, maybe
895 we can find the capitalized version */
896 for(t=0;t<font->num_glyphs;t++) {
897 if(font->glyphs[t].name && !strcasecmp(font->glyphs[t].name,charname)) {
898 msg("<debug> Char [%d,>>%s<<,%d] maps to %d\n", charnr, charname, u, t);
906 for(t=0;t<font->num_glyphs;t++) {
907 if(font->glyphs[t].name && !strcmp(font->glyphs[t].name,uniname)) {
908 msg("<debug> Char [%d,%s,>%d(%s)<] maps to %d\n", charnr, charname, u, uniname, t);
912 /* if we didn't find the character, maybe
913 we can find the capitalized version */
914 for(t=0;t<font->num_glyphs;t++) {
915 if(font->glyphs[t].name && !strcasecmp(font->glyphs[t].name,uniname)) {
916 msg("<debug> Char [%d,%s,>>%d(%s)<<] maps to %d\n", charnr, charname, u, uniname, t);
922 /* try to use the unicode id */
923 if(u>=0 && u<font->max_unicode && font->unicode2glyph[u]>=0) {
924 msg("<debug> Char [%d,%s,>%d<] maps to %d\n", charnr, charname, u, font->unicode2glyph[u]);
925 return font->unicode2glyph[u];
928 if(charnr>=0 && charnr<font->num_glyphs) {
929 msg("<debug> Char [>%d<,%s,%d] maps to %d\n", charnr, charname, u, charnr);
937 void GFXOutputDev::beginString(GfxState *state, GString *s)
939 int render = state->getRender();
940 if(current_text_stroke) {
941 msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
944 msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
945 double m11,m21,m12,m22;
946 // msg("<debug> %s beginstring \"%s\"\n", gfxstate2str(state), s->getCString());
947 state->getFontTransMat(&m11, &m12, &m21, &m22);
948 m11 *= state->getHorizScaling();
949 m21 *= state->getHorizScaling();
951 this->current_font_matrix.m00 = m11 / 1024.0;
952 this->current_font_matrix.m01 = m12 / 1024.0;
953 this->current_font_matrix.m10 = -m21 / 1024.0;
954 this->current_font_matrix.m11 = -m22 / 1024.0;
955 this->current_font_matrix.tx = 0;
956 this->current_font_matrix.ty = 0;
958 gfxmatrix_t m = this->current_font_matrix;
960 states[statepos].textRender = render;
963 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
964 double dx, double dy,
965 double originX, double originY,
966 CharCode c, int nBytes, Unicode *_u, int uLen)
968 int render = state->getRender();
969 // check for invisible text -- this is used by Acrobat Capture
971 msg("<debug> Ignoring invisible text: char %d at %f,%f", c, x, y);
975 if(states[statepos].textRender != render)
976 msg("<error> Internal error: drawChar.render!=beginString.render");
978 gfxcolor_t col = getFillColor(state);
980 Gushort *CIDToGIDMap = 0;
981 GfxFont*font = state->getFont();
983 if(font->getType() == fontType3) {
984 /* type 3 chars are passed as graphics */
985 msg("<debug> type3 char at %f/%f", x, y);
995 if(font->isCIDFont()) {
996 GfxCIDFont*cfont = (GfxCIDFont*)font;
998 if(font->getType() == fontCIDType2)
999 CIDToGIDMap = cfont->getCIDToGID();
1002 font8 = (Gfx8BitFont*)font;
1003 char**enc=font8->getEncoding();
1007 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);
1010 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);
1016 charid = getGfxCharID(current_gfxfont, c, name, u);
1018 charid = getGfxCharID(current_gfxfont, c, name, -1);
1021 /* multiple unicodes- should usually map to a ligature.
1022 if the ligature doesn't exist, we need to draw
1023 the characters one-by-one. */
1025 msg("<warning> ligature %d missing in font %s\n", c, current_gfxfont->id);
1026 for(t=0;t<uLen;t++) {
1027 drawChar(state, x, y, dx, dy, originX, originY, c, nBytes, _u+t, 1);
1033 msg("<warning> Didn't find character '%s' (c=%d,u=%d) in current charset (%s, %d characters)",
1034 FIXNULL(name),c, u, FIXNULL((char*)current_gfxfont->id), current_gfxfont->num_glyphs);
1038 gfxmatrix_t m = this->current_font_matrix;
1039 state->transform(x, y, &m.tx, &m.ty);
1040 m.tx += user_movex + clipmovex;
1041 m.ty += user_movey + clipmovey;
1043 if(render == RENDER_FILL) {
1044 device->drawchar(device, current_gfxfont, charid, &col, &m);
1046 msg("<debug> Drawing glyph %d as shape", charid);
1048 msg("<notice> Some texts will be rendered as shape");
1051 gfxline_t*glyph = current_gfxfont->glyphs[charid].line;
1052 gfxline_t*tglyph = gfxline_clone(glyph);
1053 gfxline_transform(tglyph, &m);
1054 if((render&3) != RENDER_INVISIBLE) {
1055 gfxline_t*add = gfxline_clone(tglyph);
1056 current_text_stroke = gfxline_append(current_text_stroke, add);
1058 if(render&RENDER_CLIP) {
1059 gfxline_t*add = gfxline_clone(tglyph);
1060 current_text_clip = gfxline_append(current_text_clip, add);
1062 gfxline_free(tglyph);
1066 void GFXOutputDev::endString(GfxState *state)
1068 int render = state->getRender();
1069 msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
1070 if(states[statepos].textRender != render)
1071 msg("<error> Internal error: drawChar.render!=beginString.render");
1073 if(current_text_stroke) {
1074 /* fillstroke and stroke text rendering objects we can process right
1075 now (as there may be texts of other rendering modes in this
1076 text object)- clipping objects have to wait until endTextObject,
1078 device->setparameter(device, "mark","TXT");
1079 if((render&3) == RENDER_FILL) {
1080 fillGfxLine(state, current_text_stroke);
1081 gfxline_free(current_text_stroke);
1082 current_text_stroke = 0;
1083 } else if((render&3) == RENDER_FILLSTROKE) {
1084 fillGfxLine(state, current_text_stroke);
1085 strokeGfxline(state, current_text_stroke);
1086 gfxline_free(current_text_stroke);
1087 current_text_stroke = 0;
1088 } else if((render&3) == RENDER_STROKE) {
1089 strokeGfxline(state, current_text_stroke);
1090 gfxline_free(current_text_stroke);
1091 current_text_stroke = 0;
1093 device->setparameter(device, "mark","");
1097 void GFXOutputDev::endTextObject(GfxState *state)
1099 int render = state->getRender();
1100 msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1101 if(states[statepos].textRender != render)
1102 msg("<error> Internal error: drawChar.render!=beginString.render");
1104 if(current_text_clip) {
1105 device->setparameter(device, "mark","TXT");
1106 clipToGfxLine(state, current_text_clip);
1107 device->setparameter(device, "mark","");
1108 gfxline_free(current_text_clip);
1109 current_text_clip = 0;
1113 /* the logic seems to be as following:
1114 first, beginType3Char is called, with the charcode and the coordinates.
1115 if this function returns true, it already knew about the char and has now drawn it.
1116 if the function returns false, it's a new char, and type3D1 is called with some parameters-
1117 the all draw operations until endType3Char are part of the char (which in this moment is
1118 at the position first passed to beginType3Char). the char ends with endType3Char.
1120 The drawing operations between beginType3Char and endType3Char are somewhat different to
1121 the normal ones. For example, the fillcolor equals the stroke color.
1124 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode code, Unicode *u, int uLen)
1126 msg("<debug> beginType3Char %d, %08x, %d", code, *u, uLen);
1131 gfxcolor_t col={255,0,0,0};
1132 gfxmatrix_t m = {1,0,0, 0,1,0};
1134 for(t=0;t<uLen;t++) {
1135 device->drawchar(device, 0, u[t], &col, &m);
1138 /* the character itself is going to be passed using the draw functions */
1139 return gFalse; /* gTrue= is_in_cache? */
1142 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1143 msg("<debug> type3D0 width=%f height=%f", wx, wy);
1145 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1146 msg("<debug> type3D1 width=%f height=%f bbox=(%f,%f,%f,%f)", wx, wy,
1150 void GFXOutputDev::endType3Char(GfxState *state)
1153 msg("<debug> endType3Char");
1156 void GFXOutputDev::startFrame(int width, int height)
1158 if(outer_clip_box) {
1159 device->endclip(device);
1163 device->startpage(device, width, height);
1164 this->width = width;
1165 this->height = height;
1168 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
1170 this->currentpage = pageNum;
1172 int rot = doc->getPageRotate(1);
1175 gfxline_t clippath[5];
1177 white.r = white.g = white.b = white.a = 255;
1179 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1180 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1181 Use CropBox, not MediaBox, as page size
1188 state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1189 state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1191 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1192 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1194 this->clipmovex = -(int)x1;
1195 this->clipmovey = -(int)y1;
1197 /* apply user clip box */
1198 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1199 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1200 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1201 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1202 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1203 msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1206 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1208 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);
1210 msg("<verbose> page is rotated %d degrees\n", rot);
1212 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1213 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1214 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1215 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1216 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1217 device->startclip(device, clippath); outer_clip_box = 1;
1218 device->fill(device, clippath, &white);
1222 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1224 double x1, y1, x2, y2, w;
1225 gfxline_t points[5];
1228 msg("<debug> drawlink\n");
1230 link->getRect(&x1, &y1, &x2, &y2);
1231 cvtUserToDev(x1, y1, &x, &y);
1232 points[0].type = gfx_moveTo;
1233 points[0].x = points[4].x = x + user_movex + clipmovex;
1234 points[0].y = points[4].y = y + user_movey + clipmovey;
1235 points[0].next = &points[1];
1236 cvtUserToDev(x2, y1, &x, &y);
1237 points[1].type = gfx_lineTo;
1238 points[1].x = x + user_movex + clipmovex;
1239 points[1].y = y + user_movey + clipmovey;
1240 points[1].next = &points[2];
1241 cvtUserToDev(x2, y2, &x, &y);
1242 points[2].type = gfx_lineTo;
1243 points[2].x = x + user_movex + clipmovex;
1244 points[2].y = y + user_movey + clipmovey;
1245 points[2].next = &points[3];
1246 cvtUserToDev(x1, y2, &x, &y);
1247 points[3].type = gfx_lineTo;
1248 points[3].x = x + user_movex + clipmovex;
1249 points[3].y = y + user_movey + clipmovey;
1250 points[3].next = &points[4];
1251 cvtUserToDev(x1, y1, &x, &y);
1252 points[4].type = gfx_lineTo;
1253 points[4].x = x + user_movex + clipmovex;
1254 points[4].y = y + user_movey + clipmovey;
1257 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f\n",
1258 points[0].x, points[0].y,
1259 points[1].x, points[1].y,
1260 points[2].x, points[2].y,
1261 points[3].x, points[3].y);
1263 LinkAction*action=link->getAction();
1269 msg("<trace> drawlink action=%d\n", action->getKind());
1270 switch(action->getKind())
1274 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1275 LinkDest *dest=NULL;
1276 if (ha->getDest()==NULL)
1277 dest=catalog->findDest(ha->getNamedDest());
1278 else dest=ha->getDest();
1280 if (dest->isPageRef()){
1281 Ref pageref=dest->getPageRef();
1282 page=catalog->findPage(pageref.num,pageref.gen);
1284 else page=dest->getPageNum();
1285 sprintf(buf, "%d", page);
1292 LinkGoToR*l = (LinkGoToR*)action;
1293 GString*g = l->getFileName();
1295 s = strdup(g->getCString());
1297 /* if the GoToR link has no filename, then
1298 try to find a refernce in the *local*
1300 GString*g = l->getNamedDest();
1302 s = strdup(g->getCString());
1308 LinkNamed*l = (LinkNamed*)action;
1309 GString*name = l->getName();
1311 s = strdup(name->lowerCase()->getCString());
1312 named = name->getCString();
1315 if(strstr(s, "next") || strstr(s, "forward"))
1317 page = currentpage + 1;
1319 else if(strstr(s, "prev") || strstr(s, "back"))
1321 page = currentpage - 1;
1323 else if(strstr(s, "last") || strstr(s, "end"))
1325 if(pages && pagepos>0)
1326 page = pages[pagepos-1];
1328 else if(strstr(s, "first") || strstr(s, "top"))
1336 case actionLaunch: {
1338 LinkLaunch*l = (LinkLaunch*)action;
1339 GString * str = new GString(l->getFileName());
1340 GString * params = l->getParams();
1342 str->append(params);
1343 s = strdup(str->getCString());
1350 LinkURI*l = (LinkURI*)action;
1351 GString*g = l->getURI();
1353 url = g->getCString();
1358 case actionUnknown: {
1360 LinkUnknown*l = (LinkUnknown*)action;
1365 msg("<error> Unknown link type!\n");
1370 if(!s) s = strdup("-?-");
1372 msg("<trace> drawlink s=%s\n", s);
1374 if(!linkinfo && (page || s))
1376 msg("<notice> File contains links");
1384 for(t=1;t<=pagepos;t++) {
1385 if(pages[t]==page) {
1394 sprintf(buf, "page%d", lpage);
1395 device->drawlink(device, points, buf);
1399 device->drawlink(device, points, s);
1402 msg("<verbose> \"%s\" link to \"%s\" (%d)\n", type, FIXNULL(s), page);
1406 void GFXOutputDev::saveState(GfxState *state) {
1407 dbg("saveState");dbgindent+=2;
1409 msg("<trace> saveState\n");
1412 msg("<error> Too many nested states in pdf.");
1416 states[statepos].textRender = states[statepos-1].textRender;
1417 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1418 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1419 states[statepos].clipping = 0;
1422 void GFXOutputDev::restoreState(GfxState *state) {
1423 dbgindent-=2; dbg("restoreState");
1426 msg("<error> Invalid restoreState");
1429 msg("<trace> restoreState");
1430 if(states[statepos].softmask) {
1431 clearSoftMask(state);
1434 while(states[statepos].clipping) {
1435 device->endclip(device);
1436 states[statepos].clipping--;
1441 char* writeOutStdFont(fontentry* f)
1446 char* tmpFileName = mktmpname(namebuf1);
1448 sprintf(namebuf2, "%s.afm", tmpFileName);
1449 fi = fopen(namebuf2, "wb");
1452 fwrite(f->afm, 1, f->afmlen, fi);
1455 sprintf(namebuf2, "%s.pfb", tmpFileName);
1456 fi = fopen(namebuf2, "wb");
1459 fwrite(f->pfb, 1, f->pfblen, fi);
1462 return strdup(namebuf2);
1465 char* GFXOutputDev::searchFont(char*name)
1470 msg("<verbose> SearchFont(%s)", name);
1472 /* see if it is a pdf standard font */
1473 for(i=0;i<sizeof(pdf2t1map)/sizeof(fontentry);i++)
1475 if(!strcmp(name, pdf2t1map[i].pdffont))
1477 if(!pdf2t1map[i].fullfilename) {
1478 pdf2t1map[i].fullfilename = writeOutStdFont(&pdf2t1map[i]);
1479 if(!pdf2t1map[i].fullfilename) {
1480 msg("<error> Couldn't save default font- is the Temp Directory writable?");
1482 msg("<verbose> Storing standard PDF font %s at %s", name, pdf2t1map[i].fullfilename);
1485 return strdup(pdf2t1map[i].fullfilename);
1488 /* else look in all font files */
1489 for(i=0;i<fontnum;i++)
1491 if(strstr(fonts[i].filename, name)) {
1492 return strdup(fonts[i].filename);
1498 void GFXOutputDev::updateLineWidth(GfxState *state)
1500 double width = state->getTransformedLineWidth();
1501 //swfoutput_setlinewidth(&device, width);
1504 void GFXOutputDev::updateLineCap(GfxState *state)
1506 int c = state->getLineCap();
1509 void GFXOutputDev::updateLineJoin(GfxState *state)
1511 int j = state->getLineJoin();
1514 void GFXOutputDev::updateFillColor(GfxState *state)
1517 double opaq = state->getFillOpacity();
1518 state->getFillRGB(&rgb);
1520 void GFXOutputDev::updateFillOpacity(GfxState *state)
1523 double opaq = state->getFillOpacity();
1524 state->getFillRGB(&rgb);
1525 dbg("update fillopaq %f", opaq);
1527 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1529 double opaq = state->getFillOpacity();
1530 dbg("update strokeopaq %f", opaq);
1532 void GFXOutputDev::updateFillOverprint(GfxState *state)
1534 double opaq = state->getFillOverprint();
1535 dbg("update filloverprint %f", opaq);
1537 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1539 double opaq = state->getStrokeOverprint();
1540 dbg("update strokeoverprint %f", opaq);
1542 void GFXOutputDev::updateTransfer(GfxState *state)
1544 dbg("update transfer");
1548 void GFXOutputDev::updateStrokeColor(GfxState *state)
1551 double opaq = state->getStrokeOpacity();
1552 state->getStrokeRGB(&rgb);
1555 void FoFiWrite(void *stream, char *data, int len)
1557 int ret = fwrite(data, len, 1, (FILE*)stream);
1560 char*GFXOutputDev::writeEmbeddedFontToFile(XRef*ref, GfxFont*font)
1562 char*tmpFileName = NULL;
1568 Object refObj, strObj;
1570 tmpFileName = mktmpname(namebuf);
1573 ret = font->getEmbeddedFontID(&embRef);
1575 msg("<verbose> Didn't get embedded font id");
1576 /* not embedded- the caller should now search the font
1577 directories for this font */
1581 f = fopen(tmpFileName, "wb");
1583 msg("<error> Couldn't create temporary Type 1 font file");
1587 /*if(font->isCIDFont()) {
1588 GfxCIDFont* cidFont = (GfxCIDFont *)font;
1589 GString c = cidFont->getCollection();
1590 msg("<notice> Collection: %s", c.getCString());
1593 //if (font->getType() == fontType1C) {
1594 if (0) { //font->getType() == fontType1C) {
1595 if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
1597 msg("<error> Couldn't read embedded font file");
1600 FoFiType1C *cvt = FoFiType1C::make(fontBuf, fontLen);
1602 cvt->convertToType1(0, NULL, gTrue, FoFiWrite, f);
1603 //cvt->convertToCIDType0("test", f);
1604 //cvt->convertToType0("test", f);
1607 } else if(font->getType() == fontTrueType) {
1608 msg("<verbose> writing font using TrueTypeFontFile::writeTTF");
1609 if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
1611 msg("<error> Couldn't read embedded font file");
1614 FoFiTrueType *cvt = FoFiTrueType::make(fontBuf, fontLen);
1615 cvt->writeTTF(FoFiWrite, f);
1619 font->getEmbeddedFontID(&embRef);
1620 refObj.initRef(embRef.num, embRef.gen);
1621 refObj.fetch(ref, &strObj);
1623 strObj.streamReset();
1628 f4[t] = strObj.streamGetChar();
1629 f4c[t] = (char)f4[t];
1634 if(!strncmp(f4c, "true", 4)) {
1635 /* some weird TTF fonts don't start with 0,1,0,0 but with "true".
1636 Change this on the fly */
1637 f4[0] = f4[2] = f4[3] = 0;
1645 while ((c = strObj.streamGetChar()) != EOF) {
1649 strObj.streamClose();
1654 return strdup(tmpFileName);
1657 char* GFXOutputDev::searchForSuitableFont(GfxFont*gfxFont)
1659 char*name = getFontName(gfxFont);
1663 if(!this->config_use_fontconfig)
1666 #ifdef HAVE_FONTCONFIG
1667 FcPattern *pattern, *match;
1671 static int fcinitcalled = false;
1673 msg("<debug> searchForSuitableFont(%s)", name);
1675 // call init ony once
1676 if (!fcinitcalled) {
1677 msg("<debug> Initializing FontConfig...");
1678 fcinitcalled = true;
1680 msg("<debug> FontConfig Initialization failed. Disabling.");
1681 config_use_fontconfig = 0;
1684 msg("<debug> ...initialized FontConfig");
1687 msg("<debug> FontConfig: Create \"%s\" Family Pattern", name);
1688 pattern = FcPatternBuild(NULL, FC_FAMILY, FcTypeString, name, NULL);
1689 if (gfxFont->isItalic()) // check for italic
1690 msg("<debug> FontConfig: Adding Italic Slant");
1691 FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
1692 if (gfxFont->isBold()) // check for bold
1693 msg("<debug> FontConfig: Adding Bold Weight");
1694 FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
1696 msg("<debug> FontConfig: Try to match...");
1697 // configure and match using the original font name
1698 FcConfigSubstitute(0, pattern, FcMatchPattern);
1699 FcDefaultSubstitute(pattern);
1700 match = FcFontMatch(0, pattern, &result);
1702 if (FcPatternGetString(match, "family", 0, &v) == FcResultMatch) {
1703 msg("<debug> FontConfig: family=%s", (char*)v);
1704 // if we get an exact match
1705 if (strcmp((char *)v, name) == 0) {
1706 if (FcPatternGetString(match, "file", 0, &v) == FcResultMatch) {
1707 filename = strdup((char*)v); // mem leak
1708 char *nfn = strrchr(filename, '/');
1709 if(nfn) fontname = strdup(nfn+1);
1710 else fontname = filename;
1712 msg("<debug> FontConfig: Returning \"%s\"", fontname);
1714 // initialize patterns
1715 FcPatternDestroy(pattern);
1716 FcPatternDestroy(match);
1718 // now match against serif etc.
1719 if (gfxFont->isSerif()) {
1720 msg("<debug> FontConfig: Create Serif Family Pattern");
1721 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "serif", NULL);
1722 } else if (gfxFont->isFixedWidth()) {
1723 msg("<debug> FontConfig: Create Monospace Family Pattern");
1724 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "monospace", NULL);
1726 msg("<debug> FontConfig: Create Sans Family Pattern");
1727 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "sans", NULL);
1731 if (gfxFont->isItalic()) {
1732 msg("<debug> FontConfig: Adding Italic Slant");
1733 int bb = FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
1736 if (gfxFont->isBold()) {
1737 msg("<debug> FontConfig: Adding Bold Weight");
1738 int bb = FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
1741 msg("<debug> FontConfig: Try to match... (2)");
1742 // configure and match using serif etc
1743 FcConfigSubstitute (0, pattern, FcMatchPattern);
1744 FcDefaultSubstitute (pattern);
1745 match = FcFontMatch (0, pattern, &result);
1747 if (FcPatternGetString(match, "file", 0, &v) == FcResultMatch) {
1748 filename = strdup((char*)v); // mem leak
1749 char *nfn = strrchr(filename, '/');
1750 if(nfn) fontname = strdup(nfn+1);
1751 else fontname = filename;
1753 msg("<debug> FontConfig: Returning \"%s\"", fontname);
1757 //printf("FONTCONFIG: pattern");
1758 //FcPatternPrint(pattern);
1759 //printf("FONTCONFIG: match");
1760 //FcPatternPrint(match);
1762 FcPatternDestroy(pattern);
1763 FcPatternDestroy(match);
1765 pdfswf_addfont(filename);
1772 char* GFXOutputDev::substituteFont(GfxFont*gfxFont, char* oldname)
1774 char*fontname = 0, *filename = 0;
1775 msg("<notice> substituteFont(%s)", oldname);
1777 if(!(fontname = searchForSuitableFont(gfxFont))) {
1778 fontname = "Times-Roman";
1780 filename = searchFont(fontname);
1782 msg("<error> Couldn't find font %s- did you install the default fonts?", fontname);
1786 if(substitutepos>=sizeof(substitutesource)/sizeof(char*)) {
1787 msg("<fatal> Too many fonts in file.");
1791 substitutesource[substitutepos] = strdup(oldname); //mem leak
1792 substitutetarget[substitutepos] = fontname;
1793 msg("<notice> substituting %s -> %s", FIXNULL(oldname), FIXNULL(fontname));
1796 return strdup(filename); //mem leak
1799 void unlinkfont(char* filename)
1806 if(!strncmp(&filename[l-4],".afm",4)) {
1807 memcpy(&filename[l-4],".pfb",4);
1809 memcpy(&filename[l-4],".pfa",4);
1811 memcpy(&filename[l-4],".afm",4);
1814 if(!strncmp(&filename[l-4],".pfa",4)) {
1815 memcpy(&filename[l-4],".afm",4);
1817 memcpy(&filename[l-4],".pfa",4);
1820 if(!strncmp(&filename[l-4],".pfb",4)) {
1821 memcpy(&filename[l-4],".afm",4);
1823 memcpy(&filename[l-4],".pfb",4);
1828 void GFXOutputDev::setXRef(PDFDoc*doc, XRef *xref)
1834 int GFXOutputDev::setGfxFont(char*id, char*name, char*filename, double maxSize)
1837 fontlist_t*last=0,*l = this->fontlist;
1840 msg("<error> Internal Error: FontID is null");
1842 /* TODO: should this be part of the state? */
1845 if(!strcmp(l->font->id, id)) {
1846 current_gfxfont = l->font;
1848 device->addfont(device, current_gfxfont);
1853 if(!filename) return 0;
1855 /* A font size of e.g. 9 means the font will be scaled down by
1856 1024 and scaled up by 9. So to have a maximum error of 1/20px,
1857 we have to divide 0.05 by (fontsize/1024)
1859 double quality = (1024 * 0.05) / maxSize;
1861 msg("<verbose> Loading %s...", filename);
1862 font = gfxfont_load(id, filename, quality);
1864 msg("<verbose> Couldn't load Font %s (%s)", filename, id);
1867 msg("<verbose> Font %s (%s) loaded successfully", filename, id);
1871 l->filename = strdup(filename);
1873 current_gfxfont = l->font;
1879 device->addfont(device, current_gfxfont);
1883 void GFXOutputDev::updateFont(GfxState *state)
1885 GfxFont*gfxFont = state->getFont();
1891 char * fontid = getFontID(gfxFont);
1892 char * fontname = getFontName(gfxFont);
1894 double maxSize = 1.0;
1897 maxSize = this->info->getMaximumFontSize(fontid);
1901 /* first, look if we substituted this font before-
1902 this way, we don't initialize the T1 Fonts
1904 for(t=0;t<substitutepos;t++) {
1905 if(!strcmp(fontid, substitutesource[t])) {
1906 free(fontid);fontid=0;
1907 fontid = strdup(substitutetarget[t]);
1912 /* second, see if this is a font which was used before-
1913 if so, we are done */
1914 if(setGfxFont(fontid, fontname, 0, 0)) {
1919 /* if(swfoutput_queryfont(&device, fontid))
1920 swfoutput_setfont(&device, fontid, 0);
1922 msg("<debug> updateFont(%s) [cached]", fontid);
1926 // look for Type 3 font
1927 if (gfxFont->getType() == fontType3) {
1929 type3Warning = gTrue;
1930 showFontError(gfxFont, 2);
1937 /* now either load the font, or find a substitution */
1940 GBool embedded = gfxFont->getEmbeddedFontID(&embRef);
1945 (gfxFont->getType() == fontType1 ||
1946 gfxFont->getType() == fontType1C ||
1947 gfxFont->getType() == fontCIDType0C ||
1948 gfxFont->getType() == fontTrueType ||
1949 gfxFont->getType() == fontCIDType2
1952 fileName = writeEmbeddedFontToFile(xref, gfxFont);
1953 if(!fileName) showFontError(gfxFont,0);
1956 fileName = searchFont(fontname);
1957 if(!fileName) showFontError(gfxFont,0);
1960 char * fontname = getFontName(gfxFont);
1961 msg("<warning> Font %s %scould not be loaded.", fontname, embedded?"":"(not embedded) ");
1964 msg("<warning> Try putting a TTF version of that font (named \"%s.ttf\") into %s", fontname, lastfontdir);
1966 msg("<warning> Try specifying one or more font directories");
1968 fileName = substituteFont(gfxFont, fontid);
1971 if(fontid) { free(fontid);fontid = strdup(substitutetarget[substitutepos-1]); /*ugly hack*/};
1972 msg("<notice> Font is now %s (%s)", fontid, fileName);
1976 msg("<error> Couldn't set font %s\n", fontid);
1982 msg("<verbose> updateFont(%s) -> %s (max size: %f)", fontid, fileName, maxSize);
1983 dumpFontInfo("<verbose>", gfxFont);
1985 //swfoutput_setfont(&device, fontid, fileName);
1987 if(!setGfxFont(fontid, fontname, 0, 0)) {
1988 setGfxFont(fontid, fontname, fileName, maxSize);
1992 unlinkfont(fileName);
2002 #define SQR(x) ((x)*(x))
2004 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
2006 if((newwidth<2 || newheight<2) ||
2007 (width<=newwidth || height<=newheight))
2009 unsigned char*newdata;
2011 newdata= (unsigned char*)malloc(newwidth*newheight);
2013 double fx = (double)(width)/newwidth;
2014 double fy = (double)(height)/newheight;
2016 int blocksize = (int)(8192/(fx*fy));
2017 int r = 8192*256/palettesize;
2018 for(x=0;x<newwidth;x++) {
2019 double ex = px + fx;
2020 int fromx = (int)px;
2022 int xweight1 = (int)(((fromx+1)-px)*256);
2023 int xweight2 = (int)((ex-tox)*256);
2025 for(y=0;y<newheight;y++) {
2026 double ey = py + fy;
2027 int fromy = (int)py;
2029 int yweight1 = (int)(((fromy+1)-py)*256);
2030 int yweight2 = (int)((ey-toy)*256);
2033 for(xx=fromx;xx<=tox;xx++)
2034 for(yy=fromy;yy<=toy;yy++) {
2035 int b = 1-data[width*yy+xx];
2037 if(xx==fromx) weight = (weight*xweight1)/256;
2038 if(xx==tox) weight = (weight*xweight2)/256;
2039 if(yy==fromy) weight = (weight*yweight1)/256;
2040 if(yy==toy) weight = (weight*yweight2)/256;
2043 //if(a) a=(palettesize-1)*r/blocksize;
2044 newdata[y*newwidth+x] = (a*blocksize)/r;
2052 #define IMAGE_TYPE_JPEG 0
2053 #define IMAGE_TYPE_LOSSLESS 1
2055 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
2056 double x1,double y1,
2057 double x2,double y2,
2058 double x3,double y3,
2059 double x4,double y4, int type)
2061 gfxcolor_t*newpic=0;
2063 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
2064 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
2066 gfxline_t p1,p2,p3,p4,p5;
2067 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
2068 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
2069 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
2070 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
2071 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
2073 {p1.x = (int)(p1.x*20)/20.0;
2074 p1.y = (int)(p1.y*20)/20.0;
2075 p2.x = (int)(p2.x*20)/20.0;
2076 p2.y = (int)(p2.y*20)/20.0;
2077 p3.x = (int)(p3.x*20)/20.0;
2078 p3.y = (int)(p3.y*20)/20.0;
2079 p4.x = (int)(p4.x*20)/20.0;
2080 p4.y = (int)(p4.y*20)/20.0;
2081 p5.x = (int)(p5.x*20)/20.0;
2082 p5.y = (int)(p5.y*20)/20.0;
2089 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2090 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2095 img.data = (gfxcolor_t*)data;
2099 if(type == IMAGE_TYPE_JPEG)
2100 /* TODO: pass image_dpi to device instead */
2101 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2103 dev->fillbitmap(dev, &p1, &img, &m, 0);
2106 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2107 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
2109 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG);
2112 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2113 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
2115 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS);
2119 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2120 int width, int height, GfxImageColorMap*colorMap, GBool invert,
2121 GBool inlineImg, int mask, int*maskColors,
2122 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2124 double x1,y1,x2,y2,x3,y3,x4,y4;
2125 ImageStream *imgStr;
2130 unsigned char* maskbitmap = 0;
2133 ncomps = colorMap->getNumPixelComps();
2134 bits = colorMap->getBits();
2139 unsigned char buf[8];
2140 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2142 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2143 imgMaskStr->reset();
2144 unsigned char pal[256];
2145 int n = 1 << colorMap->getBits();
2150 maskColorMap->getGray(pixBuf, &gray);
2151 pal[t] = colToByte(gray);
2153 for (y = 0; y < maskHeight; y++) {
2154 for (x = 0; x < maskWidth; x++) {
2155 imgMaskStr->getPixel(buf);
2156 maskbitmap[y*maskWidth+x] = pal[buf[0]];
2161 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2162 imgMaskStr->reset();
2163 for (y = 0; y < maskHeight; y++) {
2164 for (x = 0; x < maskWidth; x++) {
2165 imgMaskStr->getPixel(buf);
2167 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2175 imgStr = new ImageStream(str, width, ncomps,bits);
2178 if(!width || !height || (height<=1 && width<=1))
2180 msg("<verbose> Ignoring %d by %d image", width, height);
2181 unsigned char buf[8];
2183 for (y = 0; y < height; ++y)
2184 for (x = 0; x < width; ++x) {
2185 imgStr->getPixel(buf);
2193 state->transform(0, 1, &x1, &y1); x1 += user_movex + clipmovex; y1 += user_movey + clipmovey;
2194 state->transform(0, 0, &x2, &y2); x2 += user_movex + clipmovex; y2 += user_movey + clipmovey;
2195 state->transform(1, 0, &x3, &y3); x3 += user_movex + clipmovex; y3 += user_movey + clipmovey;
2196 state->transform(1, 1, &x4, &y4); x4 += user_movex + clipmovex; y4 += user_movey + clipmovey;
2198 if(!pbminfo && !(str->getKind()==strDCT)) {
2200 msg("<notice> file contains pbm pictures %s",mask?"(masked)":"");
2204 msg("<verbose> drawing %d by %d masked picture\n", width, height);
2206 if(!jpeginfo && (str->getKind()==strDCT)) {
2207 msg("<notice> file contains jpeg pictures");
2213 unsigned char buf[8];
2215 unsigned char*pic = new unsigned char[width*height];
2216 gfxcolor_t pal[256];
2218 state->getFillRGB(&rgb);
2220 memset(pal,255,sizeof(pal));
2221 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2222 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2223 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2224 pal[0].a = 255; pal[1].a = 0;
2227 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2228 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2229 for (y = 0; y < height; ++y)
2230 for (x = 0; x < width; ++x)
2232 imgStr->getPixel(buf);
2235 pic[width*y+x] = buf[0];
2238 /* the size of the drawn image is added to the identifier
2239 as the same image may require different bitmaps if displayed
2240 at different sizes (due to antialiasing): */
2243 unsigned char*pic2 = 0;
2246 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2255 height = realheight;
2259 /* make a black/white palette */
2261 float r = 255/(numpalette-1);
2263 for(t=0;t<numpalette;t++) {
2264 pal[t].r = colToByte(rgb.r);
2265 pal[t].g = colToByte(rgb.g);
2266 pal[t].b = colToByte(rgb.b);
2267 pal[t].a = (unsigned char)(t*r);
2271 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2272 for (y = 0; y < height; ++y) {
2273 for (x = 0; x < width; ++x) {
2274 pic2[width*y+x] = pal[pic[y*width+x]];
2277 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2281 if(maskbitmap) free(maskbitmap);
2287 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2288 gfxcolor_t*pic=new gfxcolor_t[width*height];
2289 for (y = 0; y < height; ++y) {
2290 for (x = 0; x < width; ++x) {
2291 imgStr->getPixel(pixBuf);
2292 colorMap->getRGB(pixBuf, &rgb);
2293 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2294 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2295 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2296 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2298 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2302 if(str->getKind()==strDCT)
2303 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2305 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2308 if(maskbitmap) free(maskbitmap);
2311 gfxcolor_t*pic=new gfxcolor_t[width*height];
2312 gfxcolor_t pal[256];
2313 int n = 1 << colorMap->getBits();
2315 for(t=0;t<256;t++) {
2317 colorMap->getRGB(pixBuf, &rgb);
2319 {/*if(maskColors && *maskColors==t) {
2320 msg("<notice> Color %d is transparent", t);
2321 if (imgData->maskColors) {
2323 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2324 if (pix[i] < imgData->maskColors[2*i] ||
2325 pix[i] > imgData->maskColors[2*i+1]) {
2340 pal[t].r = (unsigned char)(colToByte(rgb.r));
2341 pal[t].g = (unsigned char)(colToByte(rgb.g));
2342 pal[t].b = (unsigned char)(colToByte(rgb.b));
2343 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2346 for (y = 0; y < height; ++y) {
2347 for (x = 0; x < width; ++x) {
2348 imgStr->getPixel(pixBuf);
2349 pic[width*y+x] = pal[pixBuf[0]];
2351 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2355 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2359 if(maskbitmap) free(maskbitmap);
2364 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2365 int width, int height, GBool invert,
2368 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2369 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2370 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2373 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2374 int width, int height, GfxImageColorMap *colorMap,
2375 int *maskColors, GBool inlineImg)
2377 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
2378 colorMap?"colorMap":"no colorMap",
2379 maskColors?"maskColors":"no maskColors",
2381 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
2382 colorMap?"colorMap":"no colorMap",
2383 maskColors?"maskColors":"no maskColors",
2386 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2387 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2388 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2391 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2392 int width, int height,
2393 GfxImageColorMap *colorMap,
2394 Stream *maskStr, int maskWidth, int maskHeight,
2397 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2398 colorMap?"colorMap":"no colorMap",
2399 maskWidth, maskHeight);
2400 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2401 colorMap?"colorMap":"no colorMap",
2402 maskWidth, maskHeight);
2404 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2405 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2406 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2409 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2410 int width, int height,
2411 GfxImageColorMap *colorMap,
2413 int maskWidth, int maskHeight,
2414 GfxImageColorMap *maskColorMap)
2416 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2417 colorMap?"colorMap":"no colorMap",
2418 maskWidth, maskHeight);
2419 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2420 colorMap?"colorMap":"no colorMap",
2421 maskWidth, maskHeight);
2423 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2424 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2425 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2428 void GFXOutputDev::stroke(GfxState *state)
2432 GfxPath * path = state->getPath();
2433 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2434 strokeGfxline(state, line);
2438 void GFXOutputDev::fill(GfxState *state)
2440 gfxcolor_t col = getFillColor(state);
2441 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2443 GfxPath * path = state->getPath();
2444 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2445 fillGfxLine(state, line);
2449 void GFXOutputDev::eoFill(GfxState *state)
2451 gfxcolor_t col = getFillColor(state);
2452 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2454 GfxPath * path = state->getPath();
2455 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2456 fillGfxLine(state, line);
2461 static char* dirseparator()
2470 void addGlobalFont(char*filename)
2473 memset(&f, 0, sizeof(fontfile_t));
2474 f.filename = filename;
2475 if(fontnum < sizeof(fonts)/sizeof(fonts[0])) {
2476 msg("<verbose> Adding font \"%s\".", filename);
2477 fonts[fontnum++] = f;
2479 msg("<error> Too many external fonts. Not adding font file \"%s\".", filename);
2483 void addGlobalLanguageDir(char*dir)
2486 globalParams = new GlobalParams("");
2488 msg("<notice> Adding %s to language pack directories", dir);
2492 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2493 strcpy(config_file, dir);
2494 strcat(config_file, dirseparator());
2495 strcat(config_file, "add-to-xpdfrc");
2497 fi = fopen(config_file, "rb");
2499 msg("<error> Could not open %s", config_file);
2502 globalParams->parseFile(new GString(config_file), fi);
2506 void addGlobalFontDir(char*dirname)
2508 #ifdef HAVE_DIRENT_H
2509 msg("<notice> Adding %s to font directories", dirname);
2510 lastfontdir = strdup(dirname);
2511 DIR*dir = opendir(dirname);
2513 msg("<warning> Couldn't open directory %s\n", dirname);
2518 ent = readdir (dir);
2522 char*name = ent->d_name;
2528 if(!strncasecmp(&name[l-4], ".pfa", 4))
2530 if(!strncasecmp(&name[l-4], ".pfb", 4))
2532 if(!strncasecmp(&name[l-4], ".ttf", 4))
2536 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2537 strcpy(fontname, dirname);
2538 strcat(fontname, dirseparator());
2539 strcat(fontname, name);
2540 addGlobalFont(fontname);
2545 msg("<warning> No dirent.h- unable to add font dir %s", dir);
2549 void GFXOutputDev::preparePage(int pdfpage, int outputpage)
2555 this->pagebuflen = 1024;
2556 this->pages = (int*)malloc(this->pagebuflen*sizeof(int));
2557 memset(this->pages, -1, this->pagebuflen*sizeof(int));
2559 while(pdfpage >= this->pagebuflen)
2561 int oldlen = this->pagebuflen;
2562 this->pagebuflen+=1024;
2563 this->pages = (int*)realloc(this->pages, this->pagebuflen*sizeof(int));
2564 memset(&this->pages[oldlen], -1, (this->pagebuflen-oldlen)*sizeof(int));
2567 this->pages[pdfpage] = outputpage;
2568 if(pdfpage>this->pagepos)
2569 this->pagepos = pdfpage;
2575 double width,height;
2578 BBox mkBBox(GfxState*state, double*bbox, double width, double height)
2580 double xMin, yMin, xMax, yMax, x, y;
2581 double tx, ty, w, h;
2582 // transform the bbox
2583 state->transform(bbox[0], bbox[1], &x, &y);
2586 state->transform(bbox[0], bbox[3], &x, &y);
2589 } else if (x > xMax) {
2594 } else if (y > yMax) {
2597 state->transform(bbox[2], bbox[1], &x, &y);
2600 } else if (x > xMax) {
2605 } else if (y > yMax) {
2608 state->transform(bbox[2], bbox[3], &x, &y);
2611 } else if (x > xMax) {
2616 } else if (y > yMax) {
2619 tx = (int)floor(xMin);
2622 } else if (tx > width) {
2625 ty = (int)floor(yMin);
2628 } else if (ty > height) {
2631 w = (int)ceil(xMax) - tx + 1;
2632 if (tx + w > width) {
2638 h = (int)ceil(yMax) - ty + 1;
2639 if (ty + h > height) {
2653 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2654 GfxColorSpace *blendingColorSpace,
2655 GBool isolated, GBool knockout,
2658 char*colormodename = "";
2659 BBox rect = mkBBox(state, bbox, this->width, this->height);
2661 if(blendingColorSpace) {
2662 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2664 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);
2665 dbg("using clipping rect %f/%f/%f/%f\n", rect.posx,rect.posy,rect.width,rect.height);
2666 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);
2668 states[statepos].createsoftmask |= forSoftMask;
2669 states[statepos].transparencygroup = !forSoftMask;
2670 states[statepos].isolated = isolated;
2672 states[statepos].olddevice = this->device;
2673 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2675 gfxdevice_record_init(this->device);
2677 /*if(!forSoftMask) { ////???
2678 state->setFillOpacity(0.0);
2683 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2686 dbg("endTransparencyGroup");
2687 msg("<verbose> endTransparencyGroup");
2689 gfxdevice_t*r = this->device;
2691 this->device = states[statepos].olddevice;
2693 if(states[statepos].createsoftmask) {
2694 states[statepos-1].softmaskrecording = r->finish(r);
2696 states[statepos-1].grouprecording = r->finish(r);
2699 states[statepos].createsoftmask = 0;
2700 states[statepos].transparencygroup = 0;
2704 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2706 char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2707 "colordodge","colorburn","hardlight","softlight","difference",
2708 "exclusion","hue","saturation","color","luminosity"};
2710 dbg("paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2711 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2713 if(state->getBlendMode() == gfxBlendNormal)
2714 infofeature("transparency groups");
2717 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2718 warnfeature(buffer, 0);
2721 gfxresult_t*grouprecording = states[statepos].grouprecording;
2723 if(state->getBlendMode() == gfxBlendNormal) {
2725 gfxdevice_ops_init(&ops, this->device, (unsigned char)(state->getFillOpacity()*255));
2726 gfxresult_record_replay(grouprecording, &ops);
2729 grouprecording->destroy(grouprecording);
2731 states[statepos].grouprecording = 0;
2734 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2736 /* alpha = 1: retrieve mask values from alpha layer
2737 alpha = 0: retrieve mask values from luminance */
2738 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2739 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2740 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2741 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2743 infofeature("soft masks");
2745 warnfeature("soft masks from alpha channel",0);
2747 states[statepos].olddevice = this->device;
2748 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2749 gfxdevice_record_init(this->device);
2751 dbg("softmaskrecording is %08x at statepos %d\n", states[statepos].softmaskrecording, statepos);
2753 states[statepos].softmask = 1;
2754 states[statepos].softmask_alpha = alpha;
2757 static inline Guchar div255(int x) {
2758 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2761 void GFXOutputDev::clearSoftMask(GfxState *state)
2763 if(!states[statepos].softmask)
2765 states[statepos].softmask = 0;
2766 dbg("clearSoftMask statepos=%d", statepos);
2767 msg("<verbose> clearSoftMask");
2769 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2770 msg("<error> Error in softmask/tgroup ordering");
2774 gfxresult_t*mask = states[statepos].softmaskrecording;
2775 gfxresult_t*below = this->device->finish(this->device);
2776 this->device = states[statepos].olddevice;
2778 /* get outline of all objects below the soft mask */
2779 gfxdevice_t uniondev;
2780 gfxdevice_union_init(&uniondev, 0);
2781 gfxresult_record_replay(below, &uniondev);
2782 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2783 uniondev.finish(&uniondev);
2785 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2787 this->device->startclip(this->device, belowoutline);
2788 gfxresult_record_replay(below, this->device);
2789 gfxresult_record_replay(mask, this->device);
2790 this->device->endclip(this->device);
2791 gfxline_free(belowoutline);
2794 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2796 gfxdevice_t belowrender;
2797 gfxdevice_render_init(&belowrender);
2798 if(states[statepos+1].isolated) {
2799 belowrender.setparameter(&belowrender, "fillwhite", "1");
2801 belowrender.setparameter(&belowrender, "antialize", "2");
2802 belowrender.startpage(&belowrender, width, height);
2803 gfxresult_record_replay(below, &belowrender);
2804 belowrender.endpage(&belowrender);
2805 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2806 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2807 //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2809 gfxdevice_t maskrender;
2810 gfxdevice_render_init(&maskrender);
2811 maskrender.startpage(&maskrender, width, height);
2812 gfxresult_record_replay(mask, &maskrender);
2813 maskrender.endpage(&maskrender);
2814 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2815 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2817 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2818 msg("<fatal> Internal error in mask drawing");
2823 for(y=0;y<height;y++) {
2824 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2825 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2826 for(x=0;x<width;x++) {
2828 if(states[statepos].softmask_alpha) {
2831 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2834 /* premultiply alpha */
2835 l2->a = div255(alpha*l2->a);
2836 l2->r = div255(alpha*l2->r);
2837 l2->g = div255(alpha*l2->g);
2838 l2->b = div255(alpha*l2->b);
2844 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2847 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2848 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2850 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2852 mask->destroy(mask);
2853 below->destroy(below);
2854 maskresult->destroy(maskresult);
2855 belowresult->destroy(belowresult);
2856 states[statepos].softmaskrecording = 0;
2863 delete globalParams;globalParams=0;
2864 Object::memCheck(stderr);