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);
1128 /* the character itself is going to be passed using the draw functions */
1129 return gFalse; /* gTrue= is_in_cache? */
1132 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1133 msg("<debug> type3D0 width=%f height=%f", wx, wy);
1135 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1136 msg("<debug> type3D1 width=%f height=%f bbox=(%f,%f,%f,%f)", wx, wy,
1140 void GFXOutputDev::endType3Char(GfxState *state)
1143 msg("<debug> endType3Char");
1146 void GFXOutputDev::startFrame(int width, int height)
1148 if(outer_clip_box) {
1149 device->endclip(device);
1153 device->startpage(device, width, height);
1154 this->width = width;
1155 this->height = height;
1158 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
1160 this->currentpage = pageNum;
1162 int rot = doc->getPageRotate(1);
1165 gfxline_t clippath[5];
1167 white.r = white.g = white.b = white.a = 255;
1169 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1170 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1171 Use CropBox, not MediaBox, as page size
1178 state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1179 state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1181 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1182 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1184 this->clipmovex = -(int)x1;
1185 this->clipmovey = -(int)y1;
1187 /* apply user clip box */
1188 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1189 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1190 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1191 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1192 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1193 msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1196 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1198 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);
1200 msg("<verbose> page is rotated %d degrees\n", rot);
1202 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1203 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1204 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1205 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1206 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1207 device->startclip(device, clippath); outer_clip_box = 1;
1208 device->fill(device, clippath, &white);
1212 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1214 double x1, y1, x2, y2, w;
1215 gfxline_t points[5];
1218 msg("<debug> drawlink\n");
1220 link->getRect(&x1, &y1, &x2, &y2);
1221 cvtUserToDev(x1, y1, &x, &y);
1222 points[0].type = gfx_moveTo;
1223 points[0].x = points[4].x = x + user_movex + clipmovex;
1224 points[0].y = points[4].y = y + user_movey + clipmovey;
1225 points[0].next = &points[1];
1226 cvtUserToDev(x2, y1, &x, &y);
1227 points[1].type = gfx_lineTo;
1228 points[1].x = x + user_movex + clipmovex;
1229 points[1].y = y + user_movey + clipmovey;
1230 points[1].next = &points[2];
1231 cvtUserToDev(x2, y2, &x, &y);
1232 points[2].type = gfx_lineTo;
1233 points[2].x = x + user_movex + clipmovex;
1234 points[2].y = y + user_movey + clipmovey;
1235 points[2].next = &points[3];
1236 cvtUserToDev(x1, y2, &x, &y);
1237 points[3].type = gfx_lineTo;
1238 points[3].x = x + user_movex + clipmovex;
1239 points[3].y = y + user_movey + clipmovey;
1240 points[3].next = &points[4];
1241 cvtUserToDev(x1, y1, &x, &y);
1242 points[4].type = gfx_lineTo;
1243 points[4].x = x + user_movex + clipmovex;
1244 points[4].y = y + user_movey + clipmovey;
1247 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f\n",
1248 points[0].x, points[0].y,
1249 points[1].x, points[1].y,
1250 points[2].x, points[2].y,
1251 points[3].x, points[3].y);
1253 LinkAction*action=link->getAction();
1259 msg("<trace> drawlink action=%d\n", action->getKind());
1260 switch(action->getKind())
1264 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1265 LinkDest *dest=NULL;
1266 if (ha->getDest()==NULL)
1267 dest=catalog->findDest(ha->getNamedDest());
1268 else dest=ha->getDest();
1270 if (dest->isPageRef()){
1271 Ref pageref=dest->getPageRef();
1272 page=catalog->findPage(pageref.num,pageref.gen);
1274 else page=dest->getPageNum();
1275 sprintf(buf, "%d", page);
1282 LinkGoToR*l = (LinkGoToR*)action;
1283 GString*g = l->getFileName();
1285 s = strdup(g->getCString());
1287 /* if the GoToR link has no filename, then
1288 try to find a refernce in the *local*
1290 GString*g = l->getNamedDest();
1292 s = strdup(g->getCString());
1298 LinkNamed*l = (LinkNamed*)action;
1299 GString*name = l->getName();
1301 s = strdup(name->lowerCase()->getCString());
1302 named = name->getCString();
1305 if(strstr(s, "next") || strstr(s, "forward"))
1307 page = currentpage + 1;
1309 else if(strstr(s, "prev") || strstr(s, "back"))
1311 page = currentpage - 1;
1313 else if(strstr(s, "last") || strstr(s, "end"))
1315 if(pages && pagepos>0)
1316 page = pages[pagepos-1];
1318 else if(strstr(s, "first") || strstr(s, "top"))
1326 case actionLaunch: {
1328 LinkLaunch*l = (LinkLaunch*)action;
1329 GString * str = new GString(l->getFileName());
1330 GString * params = l->getParams();
1332 str->append(params);
1333 s = strdup(str->getCString());
1340 LinkURI*l = (LinkURI*)action;
1341 GString*g = l->getURI();
1343 url = g->getCString();
1348 case actionUnknown: {
1350 LinkUnknown*l = (LinkUnknown*)action;
1355 msg("<error> Unknown link type!\n");
1360 if(!s) s = strdup("-?-");
1362 msg("<trace> drawlink s=%s\n", s);
1364 if(!linkinfo && (page || s))
1366 msg("<notice> File contains links");
1374 for(t=1;t<=pagepos;t++) {
1375 if(pages[t]==page) {
1384 sprintf(buf, "page%d", lpage);
1385 device->drawlink(device, points, buf);
1389 device->drawlink(device, points, s);
1392 msg("<verbose> \"%s\" link to \"%s\" (%d)\n", type, FIXNULL(s), page);
1396 void GFXOutputDev::saveState(GfxState *state) {
1397 dbg("saveState");dbgindent+=2;
1399 msg("<trace> saveState\n");
1402 msg("<error> Too many nested states in pdf.");
1406 states[statepos].textRender = states[statepos-1].textRender;
1407 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1408 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1409 states[statepos].clipping = 0;
1412 void GFXOutputDev::restoreState(GfxState *state) {
1413 dbgindent-=2; dbg("restoreState");
1416 msg("<error> Invalid restoreState");
1419 msg("<trace> restoreState");
1420 if(states[statepos].softmask) {
1421 clearSoftMask(state);
1424 while(states[statepos].clipping) {
1425 device->endclip(device);
1426 states[statepos].clipping--;
1431 char* writeOutStdFont(fontentry* f)
1436 char* tmpFileName = mktmpname(namebuf1);
1438 sprintf(namebuf2, "%s.afm", tmpFileName);
1439 fi = fopen(namebuf2, "wb");
1442 fwrite(f->afm, 1, f->afmlen, fi);
1445 sprintf(namebuf2, "%s.pfb", tmpFileName);
1446 fi = fopen(namebuf2, "wb");
1449 fwrite(f->pfb, 1, f->pfblen, fi);
1452 return strdup(namebuf2);
1455 char* GFXOutputDev::searchFont(char*name)
1460 msg("<verbose> SearchFont(%s)", name);
1462 /* see if it is a pdf standard font */
1463 for(i=0;i<sizeof(pdf2t1map)/sizeof(fontentry);i++)
1465 if(!strcmp(name, pdf2t1map[i].pdffont))
1467 if(!pdf2t1map[i].fullfilename) {
1468 pdf2t1map[i].fullfilename = writeOutStdFont(&pdf2t1map[i]);
1469 if(!pdf2t1map[i].fullfilename) {
1470 msg("<error> Couldn't save default font- is the Temp Directory writable?");
1472 msg("<verbose> Storing standard PDF font %s at %s", name, pdf2t1map[i].fullfilename);
1475 return strdup(pdf2t1map[i].fullfilename);
1478 /* else look in all font files */
1479 for(i=0;i<fontnum;i++)
1481 if(strstr(fonts[i].filename, name)) {
1482 return strdup(fonts[i].filename);
1488 void GFXOutputDev::updateLineWidth(GfxState *state)
1490 double width = state->getTransformedLineWidth();
1491 //swfoutput_setlinewidth(&device, width);
1494 void GFXOutputDev::updateLineCap(GfxState *state)
1496 int c = state->getLineCap();
1499 void GFXOutputDev::updateLineJoin(GfxState *state)
1501 int j = state->getLineJoin();
1504 void GFXOutputDev::updateFillColor(GfxState *state)
1507 double opaq = state->getFillOpacity();
1508 state->getFillRGB(&rgb);
1510 void GFXOutputDev::updateFillOpacity(GfxState *state)
1513 double opaq = state->getFillOpacity();
1514 state->getFillRGB(&rgb);
1515 dbg("update fillopaq %f", opaq);
1517 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1519 double opaq = state->getFillOpacity();
1520 dbg("update strokeopaq %f", opaq);
1522 void GFXOutputDev::updateFillOverprint(GfxState *state)
1524 double opaq = state->getFillOverprint();
1525 dbg("update filloverprint %f", opaq);
1527 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1529 double opaq = state->getStrokeOverprint();
1530 dbg("update strokeoverprint %f", opaq);
1532 void GFXOutputDev::updateTransfer(GfxState *state)
1534 dbg("update transfer");
1538 void GFXOutputDev::updateStrokeColor(GfxState *state)
1541 double opaq = state->getStrokeOpacity();
1542 state->getStrokeRGB(&rgb);
1545 void FoFiWrite(void *stream, char *data, int len)
1547 int ret = fwrite(data, len, 1, (FILE*)stream);
1550 char*GFXOutputDev::writeEmbeddedFontToFile(XRef*ref, GfxFont*font)
1552 char*tmpFileName = NULL;
1558 Object refObj, strObj;
1560 tmpFileName = mktmpname(namebuf);
1563 ret = font->getEmbeddedFontID(&embRef);
1565 msg("<verbose> Didn't get embedded font id");
1566 /* not embedded- the caller should now search the font
1567 directories for this font */
1571 f = fopen(tmpFileName, "wb");
1573 msg("<error> Couldn't create temporary Type 1 font file");
1577 /*if(font->isCIDFont()) {
1578 GfxCIDFont* cidFont = (GfxCIDFont *)font;
1579 GString c = cidFont->getCollection();
1580 msg("<notice> Collection: %s", c.getCString());
1583 //if (font->getType() == fontType1C) {
1584 if (0) { //font->getType() == fontType1C) {
1585 if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
1587 msg("<error> Couldn't read embedded font file");
1590 FoFiType1C *cvt = FoFiType1C::make(fontBuf, fontLen);
1592 cvt->convertToType1(0, NULL, gTrue, FoFiWrite, f);
1593 //cvt->convertToCIDType0("test", f);
1594 //cvt->convertToType0("test", f);
1597 } else if(font->getType() == fontTrueType) {
1598 msg("<verbose> writing font using TrueTypeFontFile::writeTTF");
1599 if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
1601 msg("<error> Couldn't read embedded font file");
1604 FoFiTrueType *cvt = FoFiTrueType::make(fontBuf, fontLen);
1605 cvt->writeTTF(FoFiWrite, f);
1609 font->getEmbeddedFontID(&embRef);
1610 refObj.initRef(embRef.num, embRef.gen);
1611 refObj.fetch(ref, &strObj);
1613 strObj.streamReset();
1618 f4[t] = strObj.streamGetChar();
1619 f4c[t] = (char)f4[t];
1624 if(!strncmp(f4c, "true", 4)) {
1625 /* some weird TTF fonts don't start with 0,1,0,0 but with "true".
1626 Change this on the fly */
1627 f4[0] = f4[2] = f4[3] = 0;
1635 while ((c = strObj.streamGetChar()) != EOF) {
1639 strObj.streamClose();
1644 return strdup(tmpFileName);
1647 char* GFXOutputDev::searchForSuitableFont(GfxFont*gfxFont)
1649 char*name = getFontName(gfxFont);
1653 if(!this->config_use_fontconfig)
1656 #ifdef HAVE_FONTCONFIG
1657 FcPattern *pattern, *match;
1661 static int fcinitcalled = false;
1663 msg("<debug> searchForSuitableFont(%s)", name);
1665 // call init ony once
1666 if (!fcinitcalled) {
1667 msg("<debug> Initializing FontConfig...");
1668 fcinitcalled = true;
1670 msg("<debug> FontConfig Initialization failed. Disabling.");
1671 config_use_fontconfig = 0;
1674 msg("<debug> ...initialized FontConfig");
1677 msg("<debug> FontConfig: Create \"%s\" Family Pattern", name);
1678 pattern = FcPatternBuild(NULL, FC_FAMILY, FcTypeString, name, NULL);
1679 if (gfxFont->isItalic()) // check for italic
1680 msg("<debug> FontConfig: Adding Italic Slant");
1681 FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
1682 if (gfxFont->isBold()) // check for bold
1683 msg("<debug> FontConfig: Adding Bold Weight");
1684 FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
1686 msg("<debug> FontConfig: Try to match...");
1687 // configure and match using the original font name
1688 FcConfigSubstitute(0, pattern, FcMatchPattern);
1689 FcDefaultSubstitute(pattern);
1690 match = FcFontMatch(0, pattern, &result);
1692 if (FcPatternGetString(match, "family", 0, &v) == FcResultMatch) {
1693 msg("<debug> FontConfig: family=%s", (char*)v);
1694 // if we get an exact match
1695 if (strcmp((char *)v, name) == 0) {
1696 if (FcPatternGetString(match, "file", 0, &v) == FcResultMatch) {
1697 filename = strdup((char*)v); // mem leak
1698 char *nfn = strrchr(filename, '/');
1699 if(nfn) fontname = strdup(nfn+1);
1700 else fontname = filename;
1702 msg("<debug> FontConfig: Returning \"%s\"", fontname);
1704 // initialize patterns
1705 FcPatternDestroy(pattern);
1706 FcPatternDestroy(match);
1708 // now match against serif etc.
1709 if (gfxFont->isSerif()) {
1710 msg("<debug> FontConfig: Create Serif Family Pattern");
1711 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "serif", NULL);
1712 } else if (gfxFont->isFixedWidth()) {
1713 msg("<debug> FontConfig: Create Monospace Family Pattern");
1714 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "monospace", NULL);
1716 msg("<debug> FontConfig: Create Sans Family Pattern");
1717 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "sans", NULL);
1721 if (gfxFont->isItalic()) {
1722 msg("<debug> FontConfig: Adding Italic Slant");
1723 int bb = FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
1726 if (gfxFont->isBold()) {
1727 msg("<debug> FontConfig: Adding Bold Weight");
1728 int bb = FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
1731 msg("<debug> FontConfig: Try to match... (2)");
1732 // configure and match using serif etc
1733 FcConfigSubstitute (0, pattern, FcMatchPattern);
1734 FcDefaultSubstitute (pattern);
1735 match = FcFontMatch (0, pattern, &result);
1737 if (FcPatternGetString(match, "file", 0, &v) == FcResultMatch) {
1738 filename = strdup((char*)v); // mem leak
1739 char *nfn = strrchr(filename, '/');
1740 if(nfn) fontname = strdup(nfn+1);
1741 else fontname = filename;
1743 msg("<debug> FontConfig: Returning \"%s\"", fontname);
1747 //printf("FONTCONFIG: pattern");
1748 //FcPatternPrint(pattern);
1749 //printf("FONTCONFIG: match");
1750 //FcPatternPrint(match);
1752 FcPatternDestroy(pattern);
1753 FcPatternDestroy(match);
1755 pdfswf_addfont(filename);
1762 char* GFXOutputDev::substituteFont(GfxFont*gfxFont, char* oldname)
1764 char*fontname = 0, *filename = 0;
1765 msg("<notice> substituteFont(%s)", oldname);
1767 if(!(fontname = searchForSuitableFont(gfxFont))) {
1768 fontname = "Times-Roman";
1770 filename = searchFont(fontname);
1772 msg("<error> Couldn't find font %s- did you install the default fonts?", fontname);
1776 if(substitutepos>=sizeof(substitutesource)/sizeof(char*)) {
1777 msg("<fatal> Too many fonts in file.");
1781 substitutesource[substitutepos] = strdup(oldname); //mem leak
1782 substitutetarget[substitutepos] = fontname;
1783 msg("<notice> substituting %s -> %s", FIXNULL(oldname), FIXNULL(fontname));
1786 return strdup(filename); //mem leak
1789 void unlinkfont(char* filename)
1796 if(!strncmp(&filename[l-4],".afm",4)) {
1797 memcpy(&filename[l-4],".pfb",4);
1799 memcpy(&filename[l-4],".pfa",4);
1801 memcpy(&filename[l-4],".afm",4);
1804 if(!strncmp(&filename[l-4],".pfa",4)) {
1805 memcpy(&filename[l-4],".afm",4);
1807 memcpy(&filename[l-4],".pfa",4);
1810 if(!strncmp(&filename[l-4],".pfb",4)) {
1811 memcpy(&filename[l-4],".afm",4);
1813 memcpy(&filename[l-4],".pfb",4);
1818 void GFXOutputDev::setXRef(PDFDoc*doc, XRef *xref)
1824 int GFXOutputDev::setGfxFont(char*id, char*name, char*filename, double maxSize)
1827 fontlist_t*last=0,*l = this->fontlist;
1830 msg("<error> Internal Error: FontID is null");
1832 /* TODO: should this be part of the state? */
1835 if(!strcmp(l->font->id, id)) {
1836 current_gfxfont = l->font;
1838 device->addfont(device, current_gfxfont);
1843 if(!filename) return 0;
1845 /* A font size of e.g. 9 means the font will be scaled down by
1846 1024 and scaled up by 9. So to have a maximum error of 1/20px,
1847 we have to divide 0.05 by (fontsize/1024)
1849 double quality = (1024 * 0.05) / maxSize;
1851 msg("<verbose> Loading %s...", filename);
1852 font = gfxfont_load(id, filename, quality);
1854 msg("<verbose> Couldn't load Font %s (%s)", filename, id);
1857 msg("<verbose> Font %s (%s) loaded successfully", filename, id);
1861 l->filename = strdup(filename);
1863 current_gfxfont = l->font;
1869 device->addfont(device, current_gfxfont);
1873 void GFXOutputDev::updateFont(GfxState *state)
1875 GfxFont*gfxFont = state->getFont();
1881 char * fontid = getFontID(gfxFont);
1882 char * fontname = getFontName(gfxFont);
1884 double maxSize = 1.0;
1887 maxSize = this->info->getMaximumFontSize(fontid);
1891 /* first, look if we substituted this font before-
1892 this way, we don't initialize the T1 Fonts
1894 for(t=0;t<substitutepos;t++) {
1895 if(!strcmp(fontid, substitutesource[t])) {
1896 free(fontid);fontid=0;
1897 fontid = strdup(substitutetarget[t]);
1902 /* second, see if this is a font which was used before-
1903 if so, we are done */
1904 if(setGfxFont(fontid, fontname, 0, 0)) {
1909 /* if(swfoutput_queryfont(&device, fontid))
1910 swfoutput_setfont(&device, fontid, 0);
1912 msg("<debug> updateFont(%s) [cached]", fontid);
1916 // look for Type 3 font
1917 if (gfxFont->getType() == fontType3) {
1919 type3Warning = gTrue;
1920 showFontError(gfxFont, 2);
1927 /* now either load the font, or find a substitution */
1930 GBool embedded = gfxFont->getEmbeddedFontID(&embRef);
1935 (gfxFont->getType() == fontType1 ||
1936 gfxFont->getType() == fontType1C ||
1937 gfxFont->getType() == fontCIDType0C ||
1938 gfxFont->getType() == fontTrueType ||
1939 gfxFont->getType() == fontCIDType2
1942 fileName = writeEmbeddedFontToFile(xref, gfxFont);
1943 if(!fileName) showFontError(gfxFont,0);
1946 fileName = searchFont(fontname);
1947 if(!fileName) showFontError(gfxFont,0);
1950 char * fontname = getFontName(gfxFont);
1951 msg("<warning> Font %s %scould not be loaded.", fontname, embedded?"":"(not embedded) ");
1954 msg("<warning> Try putting a TTF version of that font (named \"%s.ttf\") into %s", fontname, lastfontdir);
1956 msg("<warning> Try specifying one or more font directories");
1958 fileName = substituteFont(gfxFont, fontid);
1961 if(fontid) { free(fontid);fontid = strdup(substitutetarget[substitutepos-1]); /*ugly hack*/};
1962 msg("<notice> Font is now %s (%s)", fontid, fileName);
1966 msg("<error> Couldn't set font %s\n", fontid);
1972 msg("<verbose> updateFont(%s) -> %s (max size: %f)", fontid, fileName, maxSize);
1973 dumpFontInfo("<verbose>", gfxFont);
1975 //swfoutput_setfont(&device, fontid, fileName);
1977 if(!setGfxFont(fontid, fontname, 0, 0)) {
1978 setGfxFont(fontid, fontname, fileName, maxSize);
1982 unlinkfont(fileName);
1992 #define SQR(x) ((x)*(x))
1994 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
1996 if((newwidth<2 || newheight<2) ||
1997 (width<=newwidth || height<=newheight))
1999 unsigned char*newdata;
2001 newdata= (unsigned char*)malloc(newwidth*newheight);
2003 double fx = (double)(width)/newwidth;
2004 double fy = (double)(height)/newheight;
2006 int blocksize = (int)(8192/(fx*fy));
2007 int r = 8192*256/palettesize;
2008 for(x=0;x<newwidth;x++) {
2009 double ex = px + fx;
2010 int fromx = (int)px;
2012 int xweight1 = (int)(((fromx+1)-px)*256);
2013 int xweight2 = (int)((ex-tox)*256);
2015 for(y=0;y<newheight;y++) {
2016 double ey = py + fy;
2017 int fromy = (int)py;
2019 int yweight1 = (int)(((fromy+1)-py)*256);
2020 int yweight2 = (int)((ey-toy)*256);
2023 for(xx=fromx;xx<=tox;xx++)
2024 for(yy=fromy;yy<=toy;yy++) {
2025 int b = 1-data[width*yy+xx];
2027 if(xx==fromx) weight = (weight*xweight1)/256;
2028 if(xx==tox) weight = (weight*xweight2)/256;
2029 if(yy==fromy) weight = (weight*yweight1)/256;
2030 if(yy==toy) weight = (weight*yweight2)/256;
2033 //if(a) a=(palettesize-1)*r/blocksize;
2034 newdata[y*newwidth+x] = (a*blocksize)/r;
2042 #define IMAGE_TYPE_JPEG 0
2043 #define IMAGE_TYPE_LOSSLESS 1
2045 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
2046 double x1,double y1,
2047 double x2,double y2,
2048 double x3,double y3,
2049 double x4,double y4, int type)
2051 gfxcolor_t*newpic=0;
2053 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
2054 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
2056 gfxline_t p1,p2,p3,p4,p5;
2057 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
2058 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
2059 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
2060 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
2061 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
2063 {p1.x = (int)(p1.x*20)/20.0;
2064 p1.y = (int)(p1.y*20)/20.0;
2065 p2.x = (int)(p2.x*20)/20.0;
2066 p2.y = (int)(p2.y*20)/20.0;
2067 p3.x = (int)(p3.x*20)/20.0;
2068 p3.y = (int)(p3.y*20)/20.0;
2069 p4.x = (int)(p4.x*20)/20.0;
2070 p4.y = (int)(p4.y*20)/20.0;
2071 p5.x = (int)(p5.x*20)/20.0;
2072 p5.y = (int)(p5.y*20)/20.0;
2079 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2080 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2085 img.data = (gfxcolor_t*)data;
2089 if(type == IMAGE_TYPE_JPEG)
2090 /* TODO: pass image_dpi to device instead */
2091 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2093 dev->fillbitmap(dev, &p1, &img, &m, 0);
2096 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2097 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
2099 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG);
2102 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2103 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
2105 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS);
2109 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2110 int width, int height, GfxImageColorMap*colorMap, GBool invert,
2111 GBool inlineImg, int mask, int*maskColors,
2112 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2114 double x1,y1,x2,y2,x3,y3,x4,y4;
2115 ImageStream *imgStr;
2120 unsigned char* maskbitmap = 0;
2123 ncomps = colorMap->getNumPixelComps();
2124 bits = colorMap->getBits();
2129 unsigned char buf[8];
2130 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2132 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2133 imgMaskStr->reset();
2134 unsigned char pal[256];
2135 int n = 1 << colorMap->getBits();
2140 maskColorMap->getGray(pixBuf, &gray);
2141 pal[t] = colToByte(gray);
2143 for (y = 0; y < maskHeight; y++) {
2144 for (x = 0; x < maskWidth; x++) {
2145 imgMaskStr->getPixel(buf);
2146 maskbitmap[y*maskWidth+x] = pal[buf[0]];
2151 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2152 imgMaskStr->reset();
2153 for (y = 0; y < maskHeight; y++) {
2154 for (x = 0; x < maskWidth; x++) {
2155 imgMaskStr->getPixel(buf);
2157 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2165 imgStr = new ImageStream(str, width, ncomps,bits);
2168 if(!width || !height || (height<=1 && width<=1))
2170 msg("<verbose> Ignoring %d by %d image", width, height);
2171 unsigned char buf[8];
2173 for (y = 0; y < height; ++y)
2174 for (x = 0; x < width; ++x) {
2175 imgStr->getPixel(buf);
2183 state->transform(0, 1, &x1, &y1); x1 += user_movex + clipmovex; y1 += user_movey + clipmovey;
2184 state->transform(0, 0, &x2, &y2); x2 += user_movex + clipmovex; y2 += user_movey + clipmovey;
2185 state->transform(1, 0, &x3, &y3); x3 += user_movex + clipmovex; y3 += user_movey + clipmovey;
2186 state->transform(1, 1, &x4, &y4); x4 += user_movex + clipmovex; y4 += user_movey + clipmovey;
2188 if(!pbminfo && !(str->getKind()==strDCT)) {
2190 msg("<notice> file contains pbm pictures %s",mask?"(masked)":"");
2194 msg("<verbose> drawing %d by %d masked picture\n", width, height);
2196 if(!jpeginfo && (str->getKind()==strDCT)) {
2197 msg("<notice> file contains jpeg pictures");
2203 unsigned char buf[8];
2205 unsigned char*pic = new unsigned char[width*height];
2206 gfxcolor_t pal[256];
2208 state->getFillRGB(&rgb);
2210 memset(pal,255,sizeof(pal));
2211 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2212 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2213 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2214 pal[0].a = 255; pal[1].a = 0;
2217 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2218 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2219 for (y = 0; y < height; ++y)
2220 for (x = 0; x < width; ++x)
2222 imgStr->getPixel(buf);
2225 pic[width*y+x] = buf[0];
2228 /* the size of the drawn image is added to the identifier
2229 as the same image may require different bitmaps if displayed
2230 at different sizes (due to antialiasing): */
2233 unsigned char*pic2 = 0;
2236 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2245 height = realheight;
2249 /* make a black/white palette */
2251 float r = 255/(numpalette-1);
2253 for(t=0;t<numpalette;t++) {
2254 pal[t].r = colToByte(rgb.r);
2255 pal[t].g = colToByte(rgb.g);
2256 pal[t].b = colToByte(rgb.b);
2257 pal[t].a = (unsigned char)(t*r);
2261 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2262 for (y = 0; y < height; ++y) {
2263 for (x = 0; x < width; ++x) {
2264 pic2[width*y+x] = pal[pic[y*width+x]];
2267 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2271 if(maskbitmap) free(maskbitmap);
2277 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2278 gfxcolor_t*pic=new gfxcolor_t[width*height];
2279 for (y = 0; y < height; ++y) {
2280 for (x = 0; x < width; ++x) {
2281 imgStr->getPixel(pixBuf);
2282 colorMap->getRGB(pixBuf, &rgb);
2283 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2284 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2285 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2286 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2288 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2292 if(str->getKind()==strDCT)
2293 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2295 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2298 if(maskbitmap) free(maskbitmap);
2301 gfxcolor_t*pic=new gfxcolor_t[width*height];
2302 gfxcolor_t pal[256];
2303 int n = 1 << colorMap->getBits();
2305 for(t=0;t<256;t++) {
2307 colorMap->getRGB(pixBuf, &rgb);
2309 {/*if(maskColors && *maskColors==t) {
2310 msg("<notice> Color %d is transparent", t);
2311 if (imgData->maskColors) {
2313 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2314 if (pix[i] < imgData->maskColors[2*i] ||
2315 pix[i] > imgData->maskColors[2*i+1]) {
2330 pal[t].r = (unsigned char)(colToByte(rgb.r));
2331 pal[t].g = (unsigned char)(colToByte(rgb.g));
2332 pal[t].b = (unsigned char)(colToByte(rgb.b));
2333 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2336 for (y = 0; y < height; ++y) {
2337 for (x = 0; x < width; ++x) {
2338 imgStr->getPixel(pixBuf);
2339 pic[width*y+x] = pal[pixBuf[0]];
2341 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2345 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2349 if(maskbitmap) free(maskbitmap);
2354 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2355 int width, int height, GBool invert,
2358 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2359 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2360 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2363 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2364 int width, int height, GfxImageColorMap *colorMap,
2365 int *maskColors, GBool inlineImg)
2367 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
2368 colorMap?"colorMap":"no colorMap",
2369 maskColors?"maskColors":"no maskColors",
2371 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
2372 colorMap?"colorMap":"no colorMap",
2373 maskColors?"maskColors":"no maskColors",
2376 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2377 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2378 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2381 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2382 int width, int height,
2383 GfxImageColorMap *colorMap,
2384 Stream *maskStr, int maskWidth, int maskHeight,
2387 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2388 colorMap?"colorMap":"no colorMap",
2389 maskWidth, maskHeight);
2390 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2391 colorMap?"colorMap":"no colorMap",
2392 maskWidth, maskHeight);
2394 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2395 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2396 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2399 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2400 int width, int height,
2401 GfxImageColorMap *colorMap,
2403 int maskWidth, int maskHeight,
2404 GfxImageColorMap *maskColorMap)
2406 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2407 colorMap?"colorMap":"no colorMap",
2408 maskWidth, maskHeight);
2409 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2410 colorMap?"colorMap":"no colorMap",
2411 maskWidth, maskHeight);
2413 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2414 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2415 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2418 void GFXOutputDev::stroke(GfxState *state)
2422 GfxPath * path = state->getPath();
2423 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2424 strokeGfxline(state, line);
2428 void GFXOutputDev::fill(GfxState *state)
2430 gfxcolor_t col = getFillColor(state);
2431 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2433 GfxPath * path = state->getPath();
2434 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2435 fillGfxLine(state, line);
2439 void GFXOutputDev::eoFill(GfxState *state)
2441 gfxcolor_t col = getFillColor(state);
2442 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2444 GfxPath * path = state->getPath();
2445 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2446 fillGfxLine(state, line);
2451 static char* dirseparator()
2460 void addGlobalFont(char*filename)
2463 memset(&f, 0, sizeof(fontfile_t));
2464 f.filename = filename;
2465 if(fontnum < sizeof(fonts)/sizeof(fonts[0])) {
2466 msg("<verbose> Adding font \"%s\".", filename);
2467 fonts[fontnum++] = f;
2469 msg("<error> Too many external fonts. Not adding font file \"%s\".", filename);
2473 void addGlobalLanguageDir(char*dir)
2476 globalParams = new GlobalParams("");
2478 msg("<notice> Adding %s to language pack directories", dir);
2482 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2483 strcpy(config_file, dir);
2484 strcat(config_file, dirseparator());
2485 strcat(config_file, "add-to-xpdfrc");
2487 fi = fopen(config_file, "rb");
2489 msg("<error> Could not open %s", config_file);
2492 globalParams->parseFile(new GString(config_file), fi);
2496 void addGlobalFontDir(char*dirname)
2498 #ifdef HAVE_DIRENT_H
2499 msg("<notice> Adding %s to font directories", dirname);
2500 lastfontdir = strdup(dirname);
2501 DIR*dir = opendir(dirname);
2503 msg("<warning> Couldn't open directory %s\n", dirname);
2508 ent = readdir (dir);
2512 char*name = ent->d_name;
2518 if(!strncasecmp(&name[l-4], ".pfa", 4))
2520 if(!strncasecmp(&name[l-4], ".pfb", 4))
2522 if(!strncasecmp(&name[l-4], ".ttf", 4))
2526 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2527 strcpy(fontname, dirname);
2528 strcat(fontname, dirseparator());
2529 strcat(fontname, name);
2530 addGlobalFont(fontname);
2535 msg("<warning> No dirent.h- unable to add font dir %s", dir);
2539 void GFXOutputDev::preparePage(int pdfpage, int outputpage)
2545 this->pagebuflen = 1024;
2546 this->pages = (int*)malloc(this->pagebuflen*sizeof(int));
2547 memset(this->pages, -1, this->pagebuflen*sizeof(int));
2549 while(pdfpage >= this->pagebuflen)
2551 int oldlen = this->pagebuflen;
2552 this->pagebuflen+=1024;
2553 this->pages = (int*)realloc(this->pages, this->pagebuflen*sizeof(int));
2554 memset(&this->pages[oldlen], -1, (this->pagebuflen-oldlen)*sizeof(int));
2557 this->pages[pdfpage] = outputpage;
2558 if(pdfpage>this->pagepos)
2559 this->pagepos = pdfpage;
2565 double width,height;
2568 BBox mkBBox(GfxState*state, double*bbox, double width, double height)
2570 double xMin, yMin, xMax, yMax, x, y;
2571 double tx, ty, w, h;
2572 // transform the bbox
2573 state->transform(bbox[0], bbox[1], &x, &y);
2576 state->transform(bbox[0], bbox[3], &x, &y);
2579 } else if (x > xMax) {
2584 } else if (y > yMax) {
2587 state->transform(bbox[2], bbox[1], &x, &y);
2590 } else if (x > xMax) {
2595 } else if (y > yMax) {
2598 state->transform(bbox[2], bbox[3], &x, &y);
2601 } else if (x > xMax) {
2606 } else if (y > yMax) {
2609 tx = (int)floor(xMin);
2612 } else if (tx > width) {
2615 ty = (int)floor(yMin);
2618 } else if (ty > height) {
2621 w = (int)ceil(xMax) - tx + 1;
2622 if (tx + w > width) {
2628 h = (int)ceil(yMax) - ty + 1;
2629 if (ty + h > height) {
2643 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2644 GfxColorSpace *blendingColorSpace,
2645 GBool isolated, GBool knockout,
2648 char*colormodename = "";
2649 BBox rect = mkBBox(state, bbox, this->width, this->height);
2651 if(blendingColorSpace) {
2652 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2654 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);
2655 dbg("using clipping rect %f/%f/%f/%f\n", rect.posx,rect.posy,rect.width,rect.height);
2656 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);
2658 states[statepos].createsoftmask |= forSoftMask;
2659 states[statepos].transparencygroup = !forSoftMask;
2660 states[statepos].isolated = isolated;
2662 states[statepos].olddevice = this->device;
2663 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2665 gfxdevice_record_init(this->device);
2667 /*if(!forSoftMask) { ////???
2668 state->setFillOpacity(0.0);
2673 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2676 dbg("endTransparencyGroup");
2677 msg("<verbose> endTransparencyGroup");
2679 gfxdevice_t*r = this->device;
2681 this->device = states[statepos].olddevice;
2683 if(states[statepos].createsoftmask) {
2684 states[statepos-1].softmaskrecording = r->finish(r);
2686 states[statepos-1].grouprecording = r->finish(r);
2689 states[statepos].createsoftmask = 0;
2690 states[statepos].transparencygroup = 0;
2694 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2696 char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2697 "colordodge","colorburn","hardlight","softlight","difference",
2698 "exclusion","hue","saturation","color","luminosity"};
2700 dbg("paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2701 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2703 if(state->getBlendMode() == gfxBlendNormal)
2704 infofeature("transparency groups");
2707 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2708 warnfeature(buffer, 0);
2711 gfxresult_t*grouprecording = states[statepos].grouprecording;
2713 if(state->getBlendMode() == gfxBlendNormal) {
2715 gfxdevice_ops_init(&ops, this->device, (unsigned char)(state->getFillOpacity()*255));
2716 gfxresult_record_replay(grouprecording, &ops);
2719 grouprecording->destroy(grouprecording);
2721 states[statepos].grouprecording = 0;
2724 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2726 /* alpha = 1: retrieve mask values from alpha layer
2727 alpha = 0: retrieve mask values from luminance */
2728 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2729 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2730 msg("<verbose> 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]));
2733 infofeature("soft masks");
2735 warnfeature("soft masks from alpha channel",0);
2737 states[statepos].olddevice = this->device;
2738 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2739 gfxdevice_record_init(this->device);
2741 dbg("softmaskrecording is %08x at statepos %d\n", states[statepos].softmaskrecording, statepos);
2743 states[statepos].softmask = 1;
2744 states[statepos].softmask_alpha = alpha;
2747 static inline Guchar div255(int x) {
2748 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2751 void GFXOutputDev::clearSoftMask(GfxState *state)
2753 if(!states[statepos].softmask)
2755 states[statepos].softmask = 0;
2756 dbg("clearSoftMask statepos=%d", statepos);
2757 msg("<verbose> clearSoftMask");
2759 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2760 msg("<error> Error in softmask/tgroup ordering");
2764 gfxresult_t*mask = states[statepos].softmaskrecording;
2765 gfxresult_t*below = this->device->finish(this->device);
2766 this->device = states[statepos].olddevice;
2768 /* get outline of all objects below the soft mask */
2769 gfxdevice_t uniondev;
2770 gfxdevice_union_init(&uniondev, 0);
2771 gfxresult_record_replay(below, &uniondev);
2772 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2773 uniondev.finish(&uniondev);
2775 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2777 this->device->startclip(this->device, belowoutline);
2778 gfxresult_record_replay(below, this->device);
2779 gfxresult_record_replay(mask, this->device);
2780 this->device->endclip(this->device);
2781 gfxline_free(belowoutline);
2784 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2786 gfxdevice_t belowrender;
2787 gfxdevice_render_init(&belowrender);
2788 if(states[statepos+1].isolated) {
2789 belowrender.setparameter(&belowrender, "fillwhite", "1");
2791 belowrender.setparameter(&belowrender, "antialize", "2");
2792 belowrender.startpage(&belowrender, width, height);
2793 gfxresult_record_replay(below, &belowrender);
2794 belowrender.endpage(&belowrender);
2795 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2796 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2797 //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2799 gfxdevice_t maskrender;
2800 gfxdevice_render_init(&maskrender);
2801 maskrender.startpage(&maskrender, width, height);
2802 gfxresult_record_replay(mask, &maskrender);
2803 maskrender.endpage(&maskrender);
2804 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2805 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2807 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2808 msg("<fatal> Internal error in mask drawing");
2813 for(y=0;y<height;y++) {
2814 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2815 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2816 for(x=0;x<width;x++) {
2818 if(states[statepos].softmask_alpha) {
2821 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2824 /* premultiply alpha */
2825 l2->a = div255(alpha*l2->a);
2826 l2->r = div255(alpha*l2->r);
2827 l2->g = div255(alpha*l2->g);
2828 l2->b = div255(alpha*l2->b);
2834 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2837 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2838 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2840 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2842 mask->destroy(mask);
2843 below->destroy(below);
2844 maskresult->destroy(maskresult);
2845 belowresult->destroy(belowresult);
2846 states[statepos].softmaskrecording = 0;
2853 delete globalParams;globalParams=0;
2854 Object::memCheck(stderr);