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"
76 typedef struct _fontfile
83 static fontfile_t fonts[2048];
84 static int fontnum = 0;
88 static char* lastfontdir = 0;
94 {"Times-Roman", "n021003l"},
95 {"Times-Italic", "n021023l"},
96 {"Times-Bold", "n021004l"},
97 {"Times-BoldItalic", "n021024l"},
98 {"Helvetica", "n019003l"},
99 {"Helvetica-Oblique", "n019023l"},
100 {"Helvetica-Bold", "n019004l"},
101 {"Helvetica-BoldOblique", "n019024l"},
102 {"Courier", "n022003l"},
103 {"Courier-Oblique", "n022023l"},
104 {"Courier-Bold", "n022004l"},
105 {"Courier-BoldOblique", "n022024l"},
106 {"Symbol", "s050000l"},
107 {"ZapfDingbats", "d050000l"}};
109 static int verbose = 0;
110 static int dbgindent = 0;
111 static void dbg(char*format, ...)
118 va_start(arglist, format);
119 vsprintf(buf, format, arglist);
122 while(l && buf[l-1]=='\n') {
127 int indent = dbgindent;
137 typedef struct _feature
140 struct _feature*next;
142 feature_t*featurewarnings = 0;
144 void GFXOutputDev::showfeature(char*feature,char fully, char warn)
146 feature_t*f = featurewarnings;
148 if(!strcmp(feature, f->string))
152 f = (feature_t*)malloc(sizeof(feature_t));
153 f->string = strdup(feature);
154 f->next = featurewarnings;
157 msg("<warning> %s not yet %ssupported!",feature,fully?"fully ":"");
158 if(this->config_break_on_warning) {
159 msg("<fatal> Aborting conversion due to unsupported feature");
163 msg("<notice> File contains %s",feature);
166 void GFXOutputDev::warnfeature(char*feature,char fully)
168 showfeature(feature,fully,1);
170 void GFXOutputDev::infofeature(char*feature)
172 showfeature(feature,0,0);
175 GFXOutputState::GFXOutputState() {
177 this->textRender = 0;
178 this->createsoftmask = 0;
179 this->transparencygroup = 0;
181 this->grouprecording = 0;
184 GBool GFXOutputDev::interpretType3Chars()
189 typedef struct _drawnchar
207 chars = (drawnchar_t*)malloc(sizeof(drawnchar_t)*buf_size);
208 memset(chars, 0, sizeof(drawnchar_t)*buf_size);
213 free(chars);chars = 0;
220 chars = (drawnchar_t*)realloc(chars, sizeof(drawnchar_t)*buf_size);
224 void addChar(int charid, gfxcoord_t x, gfxcoord_t y, gfxcolor_t color)
227 chars[num_chars].x = x;
228 chars[num_chars].y = y;
229 chars[num_chars].color = color;
230 chars[num_chars].charid = charid;
234 static char*getFontID(GfxFont*font);
236 GFXOutputDev::GFXOutputDev(parameter_t*p)
239 this->textmodeinfo = 0;
242 this->type3active = 0;
245 this->substitutepos = 0;
246 this->type3Warning = 0;
247 this->user_movex = 0;
248 this->user_movey = 0;
251 this->user_clipx1 = 0;
252 this->user_clipy1 = 0;
253 this->user_clipx2 = 0;
254 this->user_clipy2 = 0;
255 this->current_text_stroke = 0;
256 this->current_text_clip = 0;
258 this->outer_clip_box = 0;
260 this->pagebuflen = 0;
262 this->config_use_fontconfig=1;
263 this->config_break_on_warning=0;
265 this->parameters = p;
267 /* configure device */
269 if(!strcmp(p->name,"fontconfig")) {
270 this->config_use_fontconfig = atoi(p->value);
271 } else if(!strcmp(p->name,"breakonwarning")) {
272 this->config_break_on_warning = atoi(p->value);
278 void GFXOutputDev::setDevice(gfxdevice_t*dev)
280 parameter_t*p = this->parameters;
282 /* pass parameters to output device */
286 this->device->setparameter(this->device, p->name, p->value);
292 void GFXOutputDev::setMove(int x,int y)
294 this->user_movex = x;
295 this->user_movey = y;
298 void GFXOutputDev::setClip(int x1,int y1,int x2,int y2)
300 if(x2<x1) {int x3=x1;x1=x2;x2=x3;}
301 if(y2<y1) {int y3=y1;y1=y2;y2=y3;}
303 this->user_clipx1 = x1;
304 this->user_clipy1 = y1;
305 this->user_clipx2 = x2;
306 this->user_clipy2 = y2;
309 static char*getFontID(GfxFont*font)
311 Ref*ref = font->getID();
312 GString*gstr = font->getName();
313 char* fname = gstr==0?0:gstr->getCString();
316 sprintf(buf, "font-%d-%d", ref->num, ref->gen);
318 sprintf(buf, "%s-%d-%d", fname, ref->num, ref->gen);
323 static char*getFontName(GfxFont*font)
326 GString*gstr = font->getName();
327 char* fname = gstr==0?0:gstr->getCString();
331 sprintf(buf, "UFONT%d", r->num);
332 fontid = strdup(buf);
334 fontid = strdup(fname);
338 char* plus = strchr(fontid, '+');
339 if(plus && plus < &fontid[strlen(fontid)-1]) {
340 fontname = strdup(plus+1);
342 fontname = strdup(fontid);
348 static char mybuf[1024];
349 static char* gfxstate2str(GfxState *state)
353 bufpos+=sprintf(bufpos,"CTM[%.3f/%.3f/%.3f/%.3f/%.3f/%.3f] ",
360 if(state->getX1()!=0.0)
361 bufpos+=sprintf(bufpos,"X1-%.1f ",state->getX1());
362 if(state->getY1()!=0.0)
363 bufpos+=sprintf(bufpos,"Y1-%.1f ",state->getY1());
364 bufpos+=sprintf(bufpos,"X2-%.1f ",state->getX2());
365 bufpos+=sprintf(bufpos,"Y2-%.1f ",state->getY2());
366 bufpos+=sprintf(bufpos,"PW%.1f ",state->getPageWidth());
367 bufpos+=sprintf(bufpos,"PH%.1f ",state->getPageHeight());
368 /*bufpos+=sprintf(bufpos,"FC[%.1f/%.1f] ",
369 state->getFillColor()->c[0], state->getFillColor()->c[1]);
370 bufpos+=sprintf(bufpos,"SC[%.1f/%.1f] ",
371 state->getStrokeColor()->c[0], state->getFillColor()->c[1]);*/
372 /* bufpos+=sprintf(bufpos,"FC[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f]",
373 state->getFillColor()->c[0], state->getFillColor()->c[1],
374 state->getFillColor()->c[2], state->getFillColor()->c[3],
375 state->getFillColor()->c[4], state->getFillColor()->c[5],
376 state->getFillColor()->c[6], state->getFillColor()->c[7]);
377 bufpos+=sprintf(bufpos,"SC[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f]",
378 state->getStrokeColor()->c[0], state->getFillColor()->c[1],
379 state->getStrokeColor()->c[2], state->getFillColor()->c[3],
380 state->getStrokeColor()->c[4], state->getFillColor()->c[5],
381 state->getStrokeColor()->c[6], state->getFillColor()->c[7]);*/
382 state->getFillRGB(&rgb);
383 if(rgb.r || rgb.g || rgb.b)
384 bufpos+=sprintf(bufpos,"FR[%.1f/%.1f/%.1f] ", rgb.r,rgb.g,rgb.b);
385 state->getStrokeRGB(&rgb);
386 if(rgb.r || rgb.g || rgb.b)
387 bufpos+=sprintf(bufpos,"SR[%.1f/%.1f/%.1f] ", rgb.r,rgb.g,rgb.b);
388 if(state->getFillColorSpace()->getNComps()>1)
389 bufpos+=sprintf(bufpos,"CS[[%d]] ",state->getFillColorSpace()->getNComps());
390 if(state->getStrokeColorSpace()->getNComps()>1)
391 bufpos+=sprintf(bufpos,"SS[[%d]] ",state->getStrokeColorSpace()->getNComps());
392 if(state->getFillPattern())
393 bufpos+=sprintf(bufpos,"FP%08x ", state->getFillPattern());
394 if(state->getStrokePattern())
395 bufpos+=sprintf(bufpos,"SP%08x ", state->getStrokePattern());
397 if(state->getFillOpacity()!=1.0)
398 bufpos+=sprintf(bufpos,"FO%.1f ", state->getFillOpacity());
399 if(state->getStrokeOpacity()!=1.0)
400 bufpos+=sprintf(bufpos,"SO%.1f ", state->getStrokeOpacity());
402 bufpos+=sprintf(bufpos,"LW%.1f ", state->getLineWidth());
407 state->getLineDash(&dash, &length, &start);
411 bufpos+=sprintf(bufpos,"DASH%.1f[",start);
412 for(t=0;t<length;t++) {
413 bufpos+=sprintf(bufpos,"D%.1f",dash[t]);
415 bufpos+=sprintf(bufpos,"]");
418 if(state->getFlatness()!=1)
419 bufpos+=sprintf(bufpos,"F%d ", state->getFlatness());
420 if(state->getLineJoin()!=0)
421 bufpos+=sprintf(bufpos,"J%d ", state->getLineJoin());
422 if(state->getLineJoin()!=0)
423 bufpos+=sprintf(bufpos,"C%d ", state->getLineCap());
424 if(state->getLineJoin()!=0)
425 bufpos+=sprintf(bufpos,"ML%d ", state->getMiterLimit());
427 if(state->getFont() && getFontID(state->getFont()))
428 bufpos+=sprintf(bufpos,"F\"%s\" ",getFontID(state->getFont()));
429 bufpos+=sprintf(bufpos,"FS%.1f ", state->getFontSize());
430 bufpos+=sprintf(bufpos,"MAT[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f] ", state->getTextMat()[0],state->getTextMat()[1],state->getTextMat()[2],
431 state->getTextMat()[3],state->getTextMat()[4],state->getTextMat()[5]);
432 if(state->getCharSpace())
433 bufpos+=sprintf(bufpos,"CS%.5f ", state->getCharSpace());
434 if(state->getWordSpace())
435 bufpos+=sprintf(bufpos,"WS%.5f ", state->getWordSpace());
436 if(state->getHorizScaling()!=1.0)
437 bufpos+=sprintf(bufpos,"SC%.1f ", state->getHorizScaling());
438 if(state->getLeading())
439 bufpos+=sprintf(bufpos,"L%.1f ", state->getLeading());
441 bufpos+=sprintf(bufpos,"R%.1f ", state->getRise());
442 if(state->getRender())
443 bufpos+=sprintf(bufpos,"R%d ", state->getRender());
444 bufpos+=sprintf(bufpos,"P%08x ", state->getPath());
445 bufpos+=sprintf(bufpos,"CX%.1f ", state->getCurX());
446 bufpos+=sprintf(bufpos,"CY%.1f ", state->getCurY());
447 if(state->getLineX())
448 bufpos+=sprintf(bufpos,"LX%.1f ", state->getLineX());
449 if(state->getLineY())
450 bufpos+=sprintf(bufpos,"LY%.1f ", state->getLineY());
451 bufpos+=sprintf(bufpos," ");
455 static void dumpFontInfo(char*loglevel, GfxFont*font);
456 static int lastdumps[1024];
457 static int lastdumppos = 0;
462 static void showFontError(GfxFont*font, int nr)
466 for(t=0;t<lastdumppos;t++)
467 if(lastdumps[t] == r->num)
471 if(lastdumppos<sizeof(lastdumps)/sizeof(int))
472 lastdumps[lastdumppos++] = r->num;
474 msg("<warning> The following font caused problems:");
476 msg("<warning> The following font caused problems (substituting):");
478 msg("<warning> The following Type 3 Font will be rendered as bitmap:");
479 dumpFontInfo("<warning>", font);
482 static void dumpFontInfo(char*loglevel, GfxFont*font)
484 char* id = getFontID(font);
485 char* name = getFontName(font);
486 Ref* r=font->getID();
487 msg("%s=========== %s (ID:%d,%d) ==========\n", loglevel, name, r->num,r->gen);
489 GString*gstr = font->getTag();
491 msg("%s| Tag: %s\n", loglevel, id);
493 if(font->isCIDFont()) msg("%s| is CID font\n", loglevel);
495 GfxFontType type=font->getType();
497 case fontUnknownType:
498 msg("%s| Type: unknown\n",loglevel);
501 msg("%s| Type: 1\n",loglevel);
504 msg("%s| Type: 1C\n",loglevel);
507 msg("%s| Type: 3\n",loglevel);
510 msg("%s| Type: TrueType\n",loglevel);
513 msg("%s| Type: CIDType0\n",loglevel);
516 msg("%s| Type: CIDType0C\n",loglevel);
519 msg("%s| Type: CIDType2\n",loglevel);
524 GBool embedded = font->getEmbeddedFontID(&embRef);
526 if(font->getEmbeddedFontName()) {
527 embeddedName = font->getEmbeddedFontName()->getCString();
530 msg("%s| Embedded id: %s id: %d\n",loglevel, FIXNULL(embeddedName), embRef.num);
532 gstr = font->getExtFontFile();
534 msg("%s| External Font file: %s\n", loglevel, FIXNULL(gstr->getCString()));
536 // Get font descriptor flags.
537 if(font->isFixedWidth()) msg("%s| is fixed width\n", loglevel);
538 if(font->isSerif()) msg("%s| is serif\n", loglevel);
539 if(font->isSymbolic()) msg("%s| is symbolic\n", loglevel);
540 if(font->isItalic()) msg("%s| is italic\n", loglevel);
541 if(font->isBold()) msg("%s| is bold\n", loglevel);
547 //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");}
548 //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");}
551 void dump_outline(gfxline_t*line)
554 if(line->type == gfx_moveTo) {
555 msg("<debug> | moveTo %.2f %.2f", line->x,line->y);
556 } else if(line->type == gfx_lineTo) {
557 msg("<debug> | lineTo %.2f %.2f", line->x,line->y);
558 } else if(line->type == gfx_splineTo) {
559 msg("<debug> | splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y);
565 gfxline_t* gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user_movex, int user_movey)
567 int num = path->getNumSubpaths();
570 double lastx=0,lasty=0,posx=0,posy=0;
573 msg("<warning> empty path");
577 gfxdrawer_target_gfxline(&draw);
579 for(t = 0; t < num; t++) {
580 GfxSubpath *subpath = path->getSubpath(t);
581 int subnum = subpath->getNumPoints();
582 double bx=0,by=0,cx=0,cy=0;
584 for(s=0;s<subnum;s++) {
587 state->transform(subpath->getX(s),subpath->getY(s),&x,&y);
592 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
593 draw.lineTo(&draw, lastx, lasty);
595 draw.moveTo(&draw, x,y);
600 } else if(subpath->getCurve(s) && cpos==0) {
604 } else if(subpath->getCurve(s) && cpos==1) {
612 draw.lineTo(&draw, x,y);
614 gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y, 0.05);
621 /* fix non-closed lines */
622 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
623 draw.lineTo(&draw, lastx, lasty);
625 gfxline_t*result = (gfxline_t*)draw.result(&draw);
627 gfxline_optimize(result);
632 GBool GFXOutputDev::useTilingPatternFill()
634 infofeature("tiled patterns");
638 GBool GFXOutputDev::useShadedFills()
640 infofeature("shaded fills");
644 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line)
646 int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
647 int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
648 double miterLimit = state->getMiterLimit();
649 double width = state->getTransformedLineWidth();
652 double opaq = state->getStrokeOpacity();
654 state->getFillRGB(&rgb);
656 state->getStrokeRGB(&rgb);
658 col.r = colToByte(rgb.r);
659 col.g = colToByte(rgb.g);
660 col.b = colToByte(rgb.b);
661 col.a = (unsigned char)(opaq*255);
663 gfx_capType capType = gfx_capRound;
664 if(lineCap == 0) capType = gfx_capButt;
665 else if(lineCap == 1) capType = gfx_capRound;
666 else if(lineCap == 2) capType = gfx_capSquare;
668 gfx_joinType joinType = gfx_joinRound;
669 if(lineJoin == 0) joinType = gfx_joinMiter;
670 else if(lineJoin == 1) joinType = gfx_joinRound;
671 else if(lineJoin == 2) joinType = gfx_joinBevel;
674 double dashphase = 0;
676 state->getLineDash(&ldash, &dashnum, &dashphase);
680 if(dashnum && ldash) {
681 float * dash = (float*)malloc(sizeof(float)*(dashnum+1));
685 msg("<trace> %d dashes", dashnum);
686 msg("<trace> | phase: %f", dashphase);
687 for(t=0;t<dashnum;t++) {
689 msg("<trace> | d%-3d: %f", t, ldash[t]);
692 if(getLogLevel() >= LOGLEVEL_TRACE) {
696 line2 = gfxtool_dash_line(line, dash, dashphase);
699 msg("<trace> After dashing:");
702 if(getLogLevel() >= LOGLEVEL_TRACE) {
703 msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x\n",
705 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
706 lineCap==0?"butt": (lineJoin==1?"round":"square"),
708 col.r,col.g,col.b,col.a
713 //swfoutput_drawgfxline(output, line, width, &col, capType, joinType, miterLimit);
714 device->stroke(device, line, width, &col, capType, joinType, miterLimit);
720 gfxcolor_t getFillColor(GfxState * state)
723 double opaq = state->getFillOpacity();
724 state->getFillRGB(&rgb);
726 col.r = colToByte(rgb.r);
727 col.g = colToByte(rgb.g);
728 col.b = colToByte(rgb.b);
729 col.a = (unsigned char)(opaq*255);
733 void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line)
735 gfxcolor_t col = getFillColor(state);
737 if(getLogLevel() >= LOGLEVEL_TRACE) {
738 msg("<trace> fill %02x%02x%02x%02x\n", col.r, col.g, col.b, col.a);
741 device->fill(device, line, &col);
744 void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line)
746 if(getLogLevel() >= LOGLEVEL_TRACE) {
747 msg("<trace> clip\n");
751 device->startclip(device, line);
752 states[statepos].clipping++;
755 void GFXOutputDev::clip(GfxState *state)
757 GfxPath * path = state->getPath();
758 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
759 clipToGfxLine(state, line);
763 void GFXOutputDev::eoClip(GfxState *state)
765 GfxPath * path = state->getPath();
766 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
768 if(getLogLevel() >= LOGLEVEL_TRACE) {
769 msg("<trace> eoclip\n");
773 device->startclip(device, line);
774 states[statepos].clipping++;
778 void GFXOutputDev::endframe()
781 device->endclip(device);
785 device->endpage(device);
788 void GFXOutputDev::finish()
792 device->endclip(device);
798 GFXOutputDev::~GFXOutputDev()
803 free(this->pages); this->pages = 0;
806 fontlist_t*l = this->fontlist;
808 fontlist_t*next = l->next;
810 gfxfont_free(l->font);
811 free(l->filename);l->filename=0;
817 GBool GFXOutputDev::upsideDown()
821 GBool GFXOutputDev::useDrawChar()
826 char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
827 "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
829 #define RENDER_FILL 0
830 #define RENDER_STROKE 1
831 #define RENDER_FILLSTROKE 2
832 #define RENDER_INVISIBLE 3
833 #define RENDER_CLIP 4
835 static char tmp_printstr[4096];
836 char* makeStringPrintable(char*str)
838 int len = strlen(str);
853 tmp_printstr[len++] = '.';
854 tmp_printstr[len++] = '.';
855 tmp_printstr[len++] = '.';
857 tmp_printstr[len] = 0;
862 int getGfxCharID(gfxfont_t*font, int charnr, char *charname, int u)
867 /* find out char name from unicode index
868 TODO: should be precomputed
870 for(t=0;t<sizeof(nameToUnicodeTab)/sizeof(nameToUnicodeTab[0]);t++) {
871 if(nameToUnicodeTab[t].u == u) {
872 uniname = nameToUnicodeTab[t].name;
880 for(t=0;t<font->num_glyphs;t++) {
881 if(font->glyphs[t].name && !strcmp(font->glyphs[t].name,charname)) {
882 msg("<debug> Char [%d,>%s<,%d] maps to %d\n", charnr, charname, u, t);
886 /* if we didn't find the character, maybe
887 we can find the capitalized version */
888 for(t=0;t<font->num_glyphs;t++) {
889 if(font->glyphs[t].name && !strcasecmp(font->glyphs[t].name,charname)) {
890 msg("<debug> Char [%d,>>%s<<,%d] maps to %d\n", charnr, charname, u, t);
898 for(t=0;t<font->num_glyphs;t++) {
899 if(font->glyphs[t].name && !strcmp(font->glyphs[t].name,uniname)) {
900 msg("<debug> Char [%d,%s,>%d(%s)<] maps to %d\n", charnr, charname, u, uniname, t);
904 /* if we didn't find the character, maybe
905 we can find the capitalized version */
906 for(t=0;t<font->num_glyphs;t++) {
907 if(font->glyphs[t].name && !strcasecmp(font->glyphs[t].name,uniname)) {
908 msg("<debug> Char [%d,%s,>>%d(%s)<<] maps to %d\n", charnr, charname, u, uniname, t);
914 /* try to use the unicode id */
915 if(u>=0 && u<font->max_unicode && font->unicode2glyph[u]>=0) {
916 msg("<debug> Char [%d,%s,>%d<] maps to %d\n", charnr, charname, u, font->unicode2glyph[u]);
917 return font->unicode2glyph[u];
920 if(charnr>=0 && charnr<font->num_glyphs) {
921 msg("<debug> Char [>%d<,%s,%d] maps to %d\n", charnr, charname, u, charnr);
929 void GFXOutputDev::beginString(GfxState *state, GString *s)
931 int render = state->getRender();
932 if(current_text_stroke) {
933 msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
936 msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
937 double m11,m21,m12,m22;
938 // msg("<debug> %s beginstring \"%s\"\n", gfxstate2str(state), s->getCString());
939 state->getFontTransMat(&m11, &m12, &m21, &m22);
940 m11 *= state->getHorizScaling();
941 m21 *= state->getHorizScaling();
943 this->current_font_matrix.m00 = m11 / 1024.0;
944 this->current_font_matrix.m01 = m12 / 1024.0;
945 this->current_font_matrix.m10 = -m21 / 1024.0;
946 this->current_font_matrix.m11 = -m22 / 1024.0;
947 this->current_font_matrix.tx = 0;
948 this->current_font_matrix.ty = 0;
950 gfxmatrix_t m = this->current_font_matrix;
952 states[statepos].textRender = render;
955 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
956 double dx, double dy,
957 double originX, double originY,
958 CharCode c, int nBytes, Unicode *_u, int uLen)
960 int render = state->getRender();
961 // check for invisible text -- this is used by Acrobat Capture
963 msg("<debug> Ignoring invisible text: char %d at %f,%f", c, x, y);
967 if(states[statepos].textRender != render)
968 msg("<error> Internal error: drawChar.render!=beginString.render");
970 gfxcolor_t col = getFillColor(state);
972 Gushort *CIDToGIDMap = 0;
973 GfxFont*font = state->getFont();
975 if(font->getType() == fontType3) {
976 /* type 3 chars are passed as graphics */
977 msg("<debug> type3 char at %f/%f", x, y);
987 if(font->isCIDFont()) {
988 GfxCIDFont*cfont = (GfxCIDFont*)font;
990 if(font->getType() == fontCIDType2)
991 CIDToGIDMap = cfont->getCIDToGID();
994 font8 = (Gfx8BitFont*)font;
995 char**enc=font8->getEncoding();
999 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);
1002 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);
1008 charid = getGfxCharID(current_gfxfont, c, name, u);
1010 charid = getGfxCharID(current_gfxfont, c, name, -1);
1013 /* multiple unicodes- should usually map to a ligature.
1014 if the ligature doesn't exist, we need to draw
1015 the characters one-by-one. */
1017 msg("<warning> ligature %d missing in font %s\n", c, current_gfxfont->id);
1018 for(t=0;t<uLen;t++) {
1019 drawChar(state, x, y, dx, dy, originX, originY, c, nBytes, _u+t, 1);
1025 msg("<warning> Didn't find character '%s' (c=%d,u=%d) in current charset (%s, %d characters)",
1026 FIXNULL(name),c, u, FIXNULL((char*)current_gfxfont->id), current_gfxfont->num_glyphs);
1030 gfxmatrix_t m = this->current_font_matrix;
1031 state->transform(x, y, &m.tx, &m.ty);
1032 m.tx += user_movex + clipmovex;
1033 m.ty += user_movey + clipmovey;
1035 if(render == RENDER_FILL) {
1036 device->drawchar(device, current_gfxfont, charid, &col, &m);
1038 msg("<debug> Drawing glyph %d as shape", charid);
1040 msg("<notice> Some texts will be rendered as shape");
1043 gfxline_t*glyph = current_gfxfont->glyphs[charid].line;
1044 gfxline_t*tglyph = gfxline_clone(glyph);
1045 gfxline_transform(tglyph, &m);
1046 if((render&3) != RENDER_INVISIBLE) {
1047 gfxline_t*add = gfxline_clone(tglyph);
1048 current_text_stroke = gfxline_append(current_text_stroke, add);
1050 if(render&RENDER_CLIP) {
1051 gfxline_t*add = gfxline_clone(tglyph);
1052 current_text_clip = gfxline_append(current_text_clip, add);
1054 gfxline_free(tglyph);
1058 void GFXOutputDev::endString(GfxState *state)
1060 int render = state->getRender();
1061 msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
1062 if(states[statepos].textRender != render)
1063 msg("<error> Internal error: drawChar.render!=beginString.render");
1065 if(current_text_stroke) {
1066 /* fillstroke and stroke text rendering objects we can process right
1067 now (as there may be texts of other rendering modes in this
1068 text object)- clipping objects have to wait until endTextObject,
1070 device->setparameter(device, "mark","TXT");
1071 if((render&3) == RENDER_FILL) {
1072 fillGfxLine(state, current_text_stroke);
1073 gfxline_free(current_text_stroke);
1074 current_text_stroke = 0;
1075 } else if((render&3) == RENDER_FILLSTROKE) {
1076 fillGfxLine(state, current_text_stroke);
1077 strokeGfxline(state, current_text_stroke);
1078 gfxline_free(current_text_stroke);
1079 current_text_stroke = 0;
1080 } else if((render&3) == RENDER_STROKE) {
1081 strokeGfxline(state, current_text_stroke);
1082 gfxline_free(current_text_stroke);
1083 current_text_stroke = 0;
1085 device->setparameter(device, "mark","");
1089 void GFXOutputDev::endTextObject(GfxState *state)
1091 int render = state->getRender();
1092 msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1093 if(states[statepos].textRender != render)
1094 msg("<error> Internal error: drawChar.render!=beginString.render");
1096 if(current_text_clip) {
1097 device->setparameter(device, "mark","TXT");
1098 clipToGfxLine(state, current_text_clip);
1099 device->setparameter(device, "mark","");
1100 gfxline_free(current_text_clip);
1101 current_text_clip = 0;
1105 /* the logic seems to be as following:
1106 first, beginType3Char is called, with the charcode and the coordinates.
1107 if this function returns true, it already knew about the char and has now drawn it.
1108 if the function returns false, it's a new char, and type3D1 is called with some parameters-
1109 the all draw operations until endType3Char are part of the char (which in this moment is
1110 at the position first passed to beginType3Char). the char ends with endType3Char.
1112 The drawing operations between beginType3Char and endType3Char are somewhat different to
1113 the normal ones. For example, the fillcolor equals the stroke color.
1116 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode code, Unicode *u, int uLen)
1118 msg("<debug> beginType3Char %d, %08x, %d", code, *u, uLen);
1120 /* the character itself is going to be passed using the draw functions */
1121 return gFalse; /* gTrue= is_in_cache? */
1124 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1125 msg("<debug> type3D0 width=%f height=%f", wx, wy);
1127 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1128 msg("<debug> type3D1 width=%f height=%f bbox=(%f,%f,%f,%f)", wx, wy,
1132 void GFXOutputDev::endType3Char(GfxState *state)
1135 msg("<debug> endType3Char");
1138 void GFXOutputDev::startFrame(int width, int height)
1140 if(outer_clip_box) {
1141 device->endclip(device);
1145 device->startpage(device, width, height);
1146 this->width = width;
1147 this->height = height;
1150 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
1152 this->currentpage = pageNum;
1154 int rot = doc->getPageRotate(1);
1157 gfxline_t clippath[5];
1159 white.r = white.g = white.b = white.a = 255;
1161 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1162 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1163 Use CropBox, not MediaBox, as page size
1170 state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1171 state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1173 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1174 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1176 this->clipmovex = -(int)x1;
1177 this->clipmovey = -(int)y1;
1179 /* apply user clip box */
1180 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1181 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1182 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1183 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1184 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1185 msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1188 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1190 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);
1192 msg("<verbose> page is rotated %d degrees\n", rot);
1194 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1195 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1196 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1197 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1198 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1199 device->startclip(device, clippath); outer_clip_box = 1;
1200 device->fill(device, clippath, &white);
1203 #if xpdfUpdateVersion >= 16
1204 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1206 void GFXOutputDev::drawLink(Link *link, Catalog *catalog)
1209 double x1, y1, x2, y2, w;
1210 gfxline_t points[5];
1213 msg("<debug> drawlink\n");
1215 link->getRect(&x1, &y1, &x2, &y2);
1216 cvtUserToDev(x1, y1, &x, &y);
1217 points[0].type = gfx_moveTo;
1218 points[0].x = points[4].x = x + user_movex + clipmovex;
1219 points[0].y = points[4].y = y + user_movey + clipmovey;
1220 points[0].next = &points[1];
1221 cvtUserToDev(x2, y1, &x, &y);
1222 points[1].type = gfx_lineTo;
1223 points[1].x = x + user_movex + clipmovex;
1224 points[1].y = y + user_movey + clipmovey;
1225 points[1].next = &points[2];
1226 cvtUserToDev(x2, y2, &x, &y);
1227 points[2].type = gfx_lineTo;
1228 points[2].x = x + user_movex + clipmovex;
1229 points[2].y = y + user_movey + clipmovey;
1230 points[2].next = &points[3];
1231 cvtUserToDev(x1, y2, &x, &y);
1232 points[3].type = gfx_lineTo;
1233 points[3].x = x + user_movex + clipmovex;
1234 points[3].y = y + user_movey + clipmovey;
1235 points[3].next = &points[4];
1236 cvtUserToDev(x1, y1, &x, &y);
1237 points[4].type = gfx_lineTo;
1238 points[4].x = x + user_movex + clipmovex;
1239 points[4].y = y + user_movey + clipmovey;
1242 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f\n",
1243 points[0].x, points[0].y,
1244 points[1].x, points[1].y,
1245 points[2].x, points[2].y,
1246 points[3].x, points[3].y);
1248 LinkAction*action=link->getAction();
1254 msg("<trace> drawlink action=%d\n", action->getKind());
1255 switch(action->getKind())
1259 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1260 LinkDest *dest=NULL;
1261 if (ha->getDest()==NULL)
1262 dest=catalog->findDest(ha->getNamedDest());
1263 else dest=ha->getDest();
1265 if (dest->isPageRef()){
1266 Ref pageref=dest->getPageRef();
1267 page=catalog->findPage(pageref.num,pageref.gen);
1269 else page=dest->getPageNum();
1270 sprintf(buf, "%d", page);
1277 LinkGoToR*l = (LinkGoToR*)action;
1278 GString*g = l->getFileName();
1280 s = strdup(g->getCString());
1282 /* if the GoToR link has no filename, then
1283 try to find a refernce in the *local*
1285 GString*g = l->getNamedDest();
1287 s = strdup(g->getCString());
1293 LinkNamed*l = (LinkNamed*)action;
1294 GString*name = l->getName();
1296 s = strdup(name->lowerCase()->getCString());
1297 named = name->getCString();
1300 if(strstr(s, "next") || strstr(s, "forward"))
1302 page = currentpage + 1;
1304 else if(strstr(s, "prev") || strstr(s, "back"))
1306 page = currentpage - 1;
1308 else if(strstr(s, "last") || strstr(s, "end"))
1310 if(pages && pagepos>0)
1311 page = pages[pagepos-1];
1313 else if(strstr(s, "first") || strstr(s, "top"))
1321 case actionLaunch: {
1323 LinkLaunch*l = (LinkLaunch*)action;
1324 GString * str = new GString(l->getFileName());
1325 GString * params = l->getParams();
1327 str->append(params);
1328 s = strdup(str->getCString());
1335 LinkURI*l = (LinkURI*)action;
1336 GString*g = l->getURI();
1338 url = g->getCString();
1343 case actionUnknown: {
1345 LinkUnknown*l = (LinkUnknown*)action;
1350 msg("<error> Unknown link type!\n");
1355 if(!s) s = strdup("-?-");
1357 msg("<trace> drawlink s=%s\n", s);
1359 if(!linkinfo && (page || s))
1361 msg("<notice> File contains links");
1369 for(t=1;t<=pagepos;t++) {
1370 if(pages[t]==page) {
1379 sprintf(buf, "page%d", lpage);
1380 device->drawlink(device, points, buf);
1384 device->drawlink(device, points, s);
1387 msg("<verbose> \"%s\" link to \"%s\" (%d)\n", type, FIXNULL(s), page);
1391 void GFXOutputDev::saveState(GfxState *state) {
1392 dbg("saveState");dbgindent+=2;
1394 msg("<trace> saveState\n");
1397 msg("<error> Too many nested states in pdf.");
1401 states[statepos].textRender = states[statepos-1].textRender;
1402 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1403 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1404 states[statepos].clipping = 0;
1407 void GFXOutputDev::restoreState(GfxState *state) {
1408 dbgindent-=2; dbg("restoreState");
1411 msg("<error> Invalid restoreState");
1414 msg("<trace> restoreState");
1415 if(states[statepos].softmask) {
1416 clearSoftMask(state);
1419 while(states[statepos].clipping) {
1420 device->endclip(device);
1421 states[statepos].clipping--;
1426 char* GFXOutputDev::searchFont(char*name)
1430 int is_standard_font = 0;
1432 msg("<verbose> SearchFont(%s)", name);
1434 /* see if it is a pdf standard font */
1435 for(i=0;i<sizeof(pdf2t1map)/sizeof(mapping);i++)
1437 if(!strcmp(name, pdf2t1map[i].pdffont))
1439 name = pdf2t1map[i].filename;
1440 is_standard_font = 1;
1444 /* look in all font files */
1445 for(i=0;i<fontnum;i++)
1447 if(strstr(fonts[i].filename, name))
1449 if(!fonts[i].used) {
1452 if(!is_standard_font)
1453 msg("<notice> Using %s for %s", fonts[i].filename, name);
1455 return strdup(fonts[i].filename);
1461 void GFXOutputDev::updateLineWidth(GfxState *state)
1463 double width = state->getTransformedLineWidth();
1464 //swfoutput_setlinewidth(&device, width);
1467 void GFXOutputDev::updateLineCap(GfxState *state)
1469 int c = state->getLineCap();
1472 void GFXOutputDev::updateLineJoin(GfxState *state)
1474 int j = state->getLineJoin();
1477 void GFXOutputDev::updateFillColor(GfxState *state)
1480 double opaq = state->getFillOpacity();
1481 state->getFillRGB(&rgb);
1483 void GFXOutputDev::updateFillOpacity(GfxState *state)
1486 double opaq = state->getFillOpacity();
1487 state->getFillRGB(&rgb);
1488 dbg("update fillopaq %f", opaq);
1490 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1492 double opaq = state->getFillOpacity();
1493 dbg("update strokeopaq %f", opaq);
1495 void GFXOutputDev::updateFillOverprint(GfxState *state)
1497 double opaq = state->getFillOverprint();
1498 dbg("update filloverprint %f", opaq);
1500 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1502 double opaq = state->getStrokeOverprint();
1503 dbg("update strokeoverprint %f", opaq);
1505 void GFXOutputDev::updateTransfer(GfxState *state)
1507 dbg("update transfer");
1511 void GFXOutputDev::updateStrokeColor(GfxState *state)
1514 double opaq = state->getStrokeOpacity();
1515 state->getStrokeRGB(&rgb);
1518 void FoFiWrite(void *stream, char *data, int len)
1520 int ret = fwrite(data, len, 1, (FILE*)stream);
1523 char*GFXOutputDev::writeEmbeddedFontToFile(XRef*ref, GfxFont*font)
1525 char*tmpFileName = NULL;
1531 Object refObj, strObj;
1533 tmpFileName = mktmpname(namebuf);
1536 ret = font->getEmbeddedFontID(&embRef);
1538 msg("<verbose> Didn't get embedded font id");
1539 /* not embedded- the caller should now search the font
1540 directories for this font */
1544 f = fopen(tmpFileName, "wb");
1546 msg("<error> Couldn't create temporary Type 1 font file");
1550 /*if(font->isCIDFont()) {
1551 GfxCIDFont* cidFont = (GfxCIDFont *)font;
1552 GString c = cidFont->getCollection();
1553 msg("<notice> Collection: %s", c.getCString());
1556 //if (font->getType() == fontType1C) {
1557 if (0) { //font->getType() == fontType1C) {
1558 if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
1560 msg("<error> Couldn't read embedded font file");
1563 FoFiType1C *cvt = FoFiType1C::make(fontBuf, fontLen);
1565 cvt->convertToType1(NULL, gTrue, FoFiWrite, f);
1566 //cvt->convertToCIDType0("test", f);
1567 //cvt->convertToType0("test", f);
1570 } else if(font->getType() == fontTrueType) {
1571 msg("<verbose> writing font using TrueTypeFontFile::writeTTF");
1572 if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
1574 msg("<error> Couldn't read embedded font file");
1577 FoFiTrueType *cvt = FoFiTrueType::make(fontBuf, fontLen);
1578 cvt->writeTTF(FoFiWrite, f);
1582 font->getEmbeddedFontID(&embRef);
1583 refObj.initRef(embRef.num, embRef.gen);
1584 refObj.fetch(ref, &strObj);
1586 strObj.streamReset();
1591 f4[t] = strObj.streamGetChar();
1592 f4c[t] = (char)f4[t];
1597 if(!strncmp(f4c, "true", 4)) {
1598 /* some weird TTF fonts don't start with 0,1,0,0 but with "true".
1599 Change this on the fly */
1600 f4[0] = f4[2] = f4[3] = 0;
1608 while ((c = strObj.streamGetChar()) != EOF) {
1612 strObj.streamClose();
1617 return strdup(tmpFileName);
1620 char* GFXOutputDev::searchForSuitableFont(GfxFont*gfxFont)
1622 char*name = getFontName(gfxFont);
1626 if(!this->config_use_fontconfig)
1629 #ifdef HAVE_FONTCONFIG
1630 FcPattern *pattern, *match;
1634 static int fcinitcalled = false;
1636 msg("<debug> searchForSuitableFont(%s)", name);
1638 // call init ony once
1639 if (!fcinitcalled) {
1640 msg("<debug> Initializing FontConfig...");
1641 fcinitcalled = true;
1643 msg("<debug> FontConfig Initialization failed. Disabling.");
1644 config_use_fontconfig = 0;
1647 msg("<debug> ...initialized FontConfig");
1650 msg("<debug> FontConfig: Create \"%s\" Family Pattern", name);
1651 pattern = FcPatternBuild(NULL, FC_FAMILY, FcTypeString, name, NULL);
1652 if (gfxFont->isItalic()) // check for italic
1653 msg("<debug> FontConfig: Adding Italic Slant");
1654 FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
1655 if (gfxFont->isBold()) // check for bold
1656 msg("<debug> FontConfig: Adding Bold Weight");
1657 FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
1659 msg("<debug> FontConfig: Try to match...");
1660 // configure and match using the original font name
1661 FcConfigSubstitute(0, pattern, FcMatchPattern);
1662 FcDefaultSubstitute(pattern);
1663 match = FcFontMatch(0, pattern, &result);
1665 if (FcPatternGetString(match, "family", 0, &v) == FcResultMatch) {
1666 msg("<debug> FontConfig: family=%s", (char*)v);
1667 // if we get an exact match
1668 if (strcmp((char *)v, name) == 0) {
1669 if (FcPatternGetString(match, "file", 0, &v) == FcResultMatch) {
1670 filename = strdup((char*)v); // mem leak
1671 char *nfn = strrchr(filename, '/');
1672 if(nfn) fontname = strdup(nfn+1);
1673 else fontname = filename;
1675 msg("<debug> FontConfig: Returning \"%s\"", fontname);
1677 // initialize patterns
1678 FcPatternDestroy(pattern);
1679 FcPatternDestroy(match);
1681 // now match against serif etc.
1682 if (gfxFont->isSerif()) {
1683 msg("<debug> FontConfig: Create Serif Family Pattern");
1684 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "serif", NULL);
1685 } else if (gfxFont->isFixedWidth()) {
1686 msg("<debug> FontConfig: Create Monospace Family Pattern");
1687 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "monospace", NULL);
1689 msg("<debug> FontConfig: Create Sans Family Pattern");
1690 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "sans", NULL);
1694 if (gfxFont->isItalic()) {
1695 msg("<debug> FontConfig: Adding Italic Slant");
1696 int bb = FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
1699 if (gfxFont->isBold()) {
1700 msg("<debug> FontConfig: Adding Bold Weight");
1701 int bb = FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
1704 msg("<debug> FontConfig: Try to match... (2)");
1705 // configure and match using serif etc
1706 FcConfigSubstitute (0, pattern, FcMatchPattern);
1707 FcDefaultSubstitute (pattern);
1708 match = FcFontMatch (0, pattern, &result);
1710 if (FcPatternGetString(match, "file", 0, &v) == FcResultMatch) {
1711 filename = strdup((char*)v); // mem leak
1712 char *nfn = strrchr(filename, '/');
1713 if(nfn) fontname = strdup(nfn+1);
1714 else fontname = filename;
1716 msg("<debug> FontConfig: Returning \"%s\"", fontname);
1720 //printf("FONTCONFIG: pattern");
1721 //FcPatternPrint(pattern);
1722 //printf("FONTCONFIG: match");
1723 //FcPatternPrint(match);
1725 FcPatternDestroy(pattern);
1726 FcPatternDestroy(match);
1728 pdfswf_addfont(filename);
1735 char* GFXOutputDev::substituteFont(GfxFont*gfxFont, char* oldname)
1737 char*fontname = 0, *filename = 0;
1738 msg("<notice> substituteFont(%s)", oldname);
1740 if(!(fontname = searchForSuitableFont(gfxFont))) {
1741 fontname = "Times-Roman";
1743 filename = searchFont(fontname);
1745 msg("<error> Couldn't find font %s- did you install the default fonts?", fontname);
1749 if(substitutepos>=sizeof(substitutesource)/sizeof(char*)) {
1750 msg("<fatal> Too many fonts in file.");
1754 substitutesource[substitutepos] = strdup(oldname); //mem leak
1755 substitutetarget[substitutepos] = fontname;
1756 msg("<notice> substituting %s -> %s", FIXNULL(oldname), FIXNULL(fontname));
1759 return strdup(filename); //mem leak
1762 void unlinkfont(char* filename)
1769 if(!strncmp(&filename[l-4],".afm",4)) {
1770 memcpy(&filename[l-4],".pfb",4);
1772 memcpy(&filename[l-4],".pfa",4);
1774 memcpy(&filename[l-4],".afm",4);
1777 if(!strncmp(&filename[l-4],".pfa",4)) {
1778 memcpy(&filename[l-4],".afm",4);
1780 memcpy(&filename[l-4],".pfa",4);
1783 if(!strncmp(&filename[l-4],".pfb",4)) {
1784 memcpy(&filename[l-4],".afm",4);
1786 memcpy(&filename[l-4],".pfb",4);
1791 void GFXOutputDev::setXRef(PDFDoc*doc, XRef *xref)
1797 int GFXOutputDev::setGfxFont(char*id, char*name, char*filename, double maxSize)
1800 fontlist_t*last=0,*l = this->fontlist;
1803 msg("<error> Internal Error: FontID is null");
1805 /* TODO: should this be part of the state? */
1808 if(!strcmp(l->font->id, id)) {
1809 current_gfxfont = l->font;
1811 device->addfont(device, current_gfxfont);
1816 if(!filename) return 0;
1818 /* A font size of e.g. 9 means the font will be scaled down by
1819 1024 and scaled up by 9. So to have a maximum error of 1/20px,
1820 we have to divide 0.05 by (fontsize/1024)
1822 double quality = (1024 * 0.05) / maxSize;
1824 msg("<verbose> Loading %s...", filename);
1825 font = gfxfont_load(id, filename, quality);
1827 msg("<verbose> Couldn't load Font %s (%s)", filename, id);
1830 msg("<verbose> Font %s (%s) loaded successfully", filename, id);
1834 l->filename = strdup(filename);
1836 current_gfxfont = l->font;
1842 device->addfont(device, current_gfxfont);
1846 void GFXOutputDev::updateFont(GfxState *state)
1848 GfxFont*gfxFont = state->getFont();
1854 char * fontid = getFontID(gfxFont);
1855 char * fontname = getFontName(gfxFont);
1857 double maxSize = 1.0;
1860 maxSize = this->info->getMaximumFontSize(fontid);
1864 /* first, look if we substituted this font before-
1865 this way, we don't initialize the T1 Fonts
1867 for(t=0;t<substitutepos;t++) {
1868 if(!strcmp(fontid, substitutesource[t])) {
1869 free(fontid);fontid=0;
1870 fontid = strdup(substitutetarget[t]);
1875 /* second, see if this is a font which was used before-
1876 if so, we are done */
1877 if(setGfxFont(fontid, fontname, 0, 0)) {
1882 /* if(swfoutput_queryfont(&device, fontid))
1883 swfoutput_setfont(&device, fontid, 0);
1885 msg("<debug> updateFont(%s) [cached]", fontid);
1889 // look for Type 3 font
1890 if (gfxFont->getType() == fontType3) {
1892 type3Warning = gTrue;
1893 showFontError(gfxFont, 2);
1900 /* now either load the font, or find a substitution */
1903 GBool embedded = gfxFont->getEmbeddedFontID(&embRef);
1908 (gfxFont->getType() == fontType1 ||
1909 gfxFont->getType() == fontType1C ||
1910 gfxFont->getType() == fontCIDType0C ||
1911 gfxFont->getType() == fontTrueType ||
1912 gfxFont->getType() == fontCIDType2
1915 fileName = writeEmbeddedFontToFile(xref, gfxFont);
1916 if(!fileName) showFontError(gfxFont,0);
1919 fileName = searchFont(fontname);
1920 if(!fileName) showFontError(gfxFont,0);
1923 char * fontname = getFontName(gfxFont);
1924 msg("<warning> Font %s %scould not be loaded.", fontname, embedded?"":"(not embedded) ");
1927 msg("<warning> Try putting a TTF version of that font (named \"%s.ttf\") into %s", fontname, lastfontdir);
1929 msg("<warning> Try specifying one or more font directories");
1931 fileName = substituteFont(gfxFont, fontid);
1934 if(fontid) { free(fontid);fontid = strdup(substitutetarget[substitutepos-1]); /*ugly hack*/};
1935 msg("<notice> Font is now %s (%s)", fontid, fileName);
1939 msg("<error> Couldn't set font %s\n", fontid);
1945 msg("<verbose> updateFont(%s) -> %s (max size: %f)", fontid, fileName, maxSize);
1946 dumpFontInfo("<verbose>", gfxFont);
1948 //swfoutput_setfont(&device, fontid, fileName);
1950 if(!setGfxFont(fontid, fontname, 0, 0)) {
1951 setGfxFont(fontid, fontname, fileName, maxSize);
1955 unlinkfont(fileName);
1965 #define SQR(x) ((x)*(x))
1967 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
1969 if((newwidth<2 || newheight<2) ||
1970 (width<=newwidth || height<=newheight))
1972 unsigned char*newdata;
1974 newdata= (unsigned char*)malloc(newwidth*newheight);
1976 double fx = (double)(width)/newwidth;
1977 double fy = (double)(height)/newheight;
1979 int blocksize = (int)(8192/(fx*fy));
1980 int r = 8192*256/palettesize;
1981 for(x=0;x<newwidth;x++) {
1982 double ex = px + fx;
1983 int fromx = (int)px;
1985 int xweight1 = (int)(((fromx+1)-px)*256);
1986 int xweight2 = (int)((ex-tox)*256);
1988 for(y=0;y<newheight;y++) {
1989 double ey = py + fy;
1990 int fromy = (int)py;
1992 int yweight1 = (int)(((fromy+1)-py)*256);
1993 int yweight2 = (int)((ey-toy)*256);
1996 for(xx=fromx;xx<=tox;xx++)
1997 for(yy=fromy;yy<=toy;yy++) {
1998 int b = 1-data[width*yy+xx];
2000 if(xx==fromx) weight = (weight*xweight1)/256;
2001 if(xx==tox) weight = (weight*xweight2)/256;
2002 if(yy==fromy) weight = (weight*yweight1)/256;
2003 if(yy==toy) weight = (weight*yweight2)/256;
2006 //if(a) a=(palettesize-1)*r/blocksize;
2007 newdata[y*newwidth+x] = (a*blocksize)/r;
2015 #define IMAGE_TYPE_JPEG 0
2016 #define IMAGE_TYPE_LOSSLESS 1
2018 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
2019 double x1,double y1,
2020 double x2,double y2,
2021 double x3,double y3,
2022 double x4,double y4, int type)
2024 gfxcolor_t*newpic=0;
2026 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
2027 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
2029 gfxline_t p1,p2,p3,p4,p5;
2030 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
2031 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
2032 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
2033 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
2034 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
2036 {p1.x = (int)(p1.x*20)/20.0;
2037 p1.y = (int)(p1.y*20)/20.0;
2038 p2.x = (int)(p2.x*20)/20.0;
2039 p2.y = (int)(p2.y*20)/20.0;
2040 p3.x = (int)(p3.x*20)/20.0;
2041 p3.y = (int)(p3.y*20)/20.0;
2042 p4.x = (int)(p4.x*20)/20.0;
2043 p4.y = (int)(p4.y*20)/20.0;
2044 p5.x = (int)(p5.x*20)/20.0;
2045 p5.y = (int)(p5.y*20)/20.0;
2052 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2053 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2058 img.data = (gfxcolor_t*)data;
2062 if(type == IMAGE_TYPE_JPEG)
2063 /* TODO: pass image_dpi to device instead */
2064 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2066 dev->fillbitmap(dev, &p1, &img, &m, 0);
2069 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2070 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
2072 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG);
2075 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2076 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
2078 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS);
2082 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2083 int width, int height, GfxImageColorMap*colorMap, GBool invert,
2084 GBool inlineImg, int mask, int*maskColors,
2085 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2087 double x1,y1,x2,y2,x3,y3,x4,y4;
2088 ImageStream *imgStr;
2093 unsigned char* maskbitmap = 0;
2096 ncomps = colorMap->getNumPixelComps();
2097 bits = colorMap->getBits();
2102 unsigned char buf[8];
2103 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2105 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2106 imgMaskStr->reset();
2107 unsigned char pal[256];
2108 int n = 1 << colorMap->getBits();
2113 maskColorMap->getGray(pixBuf, &gray);
2114 pal[t] = colToByte(gray);
2116 for (y = 0; y < maskHeight; y++) {
2117 for (x = 0; x < maskWidth; x++) {
2118 imgMaskStr->getPixel(buf);
2119 maskbitmap[y*maskWidth+x] = pal[buf[0]];
2124 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2125 imgMaskStr->reset();
2126 for (y = 0; y < maskHeight; y++) {
2127 for (x = 0; x < maskWidth; x++) {
2128 imgMaskStr->getPixel(buf);
2130 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2138 imgStr = new ImageStream(str, width, ncomps,bits);
2141 if(!width || !height || (height<=1 && width<=1))
2143 msg("<verbose> Ignoring %d by %d image", width, height);
2144 unsigned char buf[8];
2146 for (y = 0; y < height; ++y)
2147 for (x = 0; x < width; ++x) {
2148 imgStr->getPixel(buf);
2156 state->transform(0, 1, &x1, &y1); x1 += user_movex + clipmovex; y1 += user_movey + clipmovey;
2157 state->transform(0, 0, &x2, &y2); x2 += user_movex + clipmovex; y2 += user_movey + clipmovey;
2158 state->transform(1, 0, &x3, &y3); x3 += user_movex + clipmovex; y3 += user_movey + clipmovey;
2159 state->transform(1, 1, &x4, &y4); x4 += user_movex + clipmovex; y4 += user_movey + clipmovey;
2161 if(!pbminfo && !(str->getKind()==strDCT)) {
2163 msg("<notice> file contains pbm pictures %s",mask?"(masked)":"");
2167 msg("<verbose> drawing %d by %d masked picture\n", width, height);
2169 if(!jpeginfo && (str->getKind()==strDCT)) {
2170 msg("<notice> file contains jpeg pictures");
2176 unsigned char buf[8];
2178 unsigned char*pic = new unsigned char[width*height];
2179 gfxcolor_t pal[256];
2181 state->getFillRGB(&rgb);
2183 memset(pal,255,sizeof(pal));
2184 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2185 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2186 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2187 pal[0].a = 255; pal[1].a = 0;
2190 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2191 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2192 for (y = 0; y < height; ++y)
2193 for (x = 0; x < width; ++x)
2195 imgStr->getPixel(buf);
2198 pic[width*y+x] = buf[0];
2201 /* the size of the drawn image is added to the identifier
2202 as the same image may require different bitmaps if displayed
2203 at different sizes (due to antialiasing): */
2206 unsigned char*pic2 = 0;
2209 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2218 height = realheight;
2222 /* make a black/white palette */
2224 float r = 255/(numpalette-1);
2226 for(t=0;t<numpalette;t++) {
2227 pal[t].r = colToByte(rgb.r);
2228 pal[t].g = colToByte(rgb.g);
2229 pal[t].b = colToByte(rgb.b);
2230 pal[t].a = (unsigned char)(t*r);
2234 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2235 for (y = 0; y < height; ++y) {
2236 for (x = 0; x < width; ++x) {
2237 pic2[width*y+x] = pal[pic[y*width+x]];
2240 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2244 if(maskbitmap) free(maskbitmap);
2250 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2251 gfxcolor_t*pic=new gfxcolor_t[width*height];
2252 for (y = 0; y < height; ++y) {
2253 for (x = 0; x < width; ++x) {
2254 imgStr->getPixel(pixBuf);
2255 colorMap->getRGB(pixBuf, &rgb);
2256 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2257 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2258 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2259 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2261 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2265 if(str->getKind()==strDCT)
2266 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2268 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2271 if(maskbitmap) free(maskbitmap);
2274 gfxcolor_t*pic=new gfxcolor_t[width*height];
2275 gfxcolor_t pal[256];
2276 int n = 1 << colorMap->getBits();
2278 for(t=0;t<256;t++) {
2280 colorMap->getRGB(pixBuf, &rgb);
2282 {/*if(maskColors && *maskColors==t) {
2283 msg("<notice> Color %d is transparent", t);
2284 if (imgData->maskColors) {
2286 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2287 if (pix[i] < imgData->maskColors[2*i] ||
2288 pix[i] > imgData->maskColors[2*i+1]) {
2303 pal[t].r = (unsigned char)(colToByte(rgb.r));
2304 pal[t].g = (unsigned char)(colToByte(rgb.g));
2305 pal[t].b = (unsigned char)(colToByte(rgb.b));
2306 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2309 for (y = 0; y < height; ++y) {
2310 for (x = 0; x < width; ++x) {
2311 imgStr->getPixel(pixBuf);
2312 pic[width*y+x] = pal[pixBuf[0]];
2314 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2318 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2322 if(maskbitmap) free(maskbitmap);
2327 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2328 int width, int height, GBool invert,
2331 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2332 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2333 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2336 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2337 int width, int height, GfxImageColorMap *colorMap,
2338 int *maskColors, GBool inlineImg)
2340 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
2341 colorMap?"colorMap":"no colorMap",
2342 maskColors?"maskColors":"no maskColors",
2344 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
2345 colorMap?"colorMap":"no colorMap",
2346 maskColors?"maskColors":"no maskColors",
2349 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2350 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2351 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2354 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2355 int width, int height,
2356 GfxImageColorMap *colorMap,
2357 Stream *maskStr, int maskWidth, int maskHeight,
2360 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2361 colorMap?"colorMap":"no colorMap",
2362 maskWidth, maskHeight);
2363 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2364 colorMap?"colorMap":"no colorMap",
2365 maskWidth, maskHeight);
2367 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2368 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2369 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2372 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2373 int width, int height,
2374 GfxImageColorMap *colorMap,
2376 int maskWidth, int maskHeight,
2377 GfxImageColorMap *maskColorMap)
2379 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2380 colorMap?"colorMap":"no colorMap",
2381 maskWidth, maskHeight);
2382 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2383 colorMap?"colorMap":"no colorMap",
2384 maskWidth, maskHeight);
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,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2391 void GFXOutputDev::stroke(GfxState *state)
2395 GfxPath * path = state->getPath();
2396 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2397 strokeGfxline(state, line);
2401 void GFXOutputDev::fill(GfxState *state)
2403 gfxcolor_t col = getFillColor(state);
2404 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2406 GfxPath * path = state->getPath();
2407 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2408 fillGfxLine(state, line);
2412 void GFXOutputDev::eoFill(GfxState *state)
2414 gfxcolor_t col = getFillColor(state);
2415 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2417 GfxPath * path = state->getPath();
2418 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2419 fillGfxLine(state, line);
2424 static char* dirseparator()
2433 void addGlobalFont(char*filename)
2436 memset(&f, 0, sizeof(fontfile_t));
2437 f.filename = filename;
2438 if(fontnum < sizeof(fonts)/sizeof(fonts[0])) {
2439 msg("<verbose> Adding font \"%s\".", filename);
2440 fonts[fontnum++] = f;
2442 msg("<error> Too many external fonts. Not adding font file \"%s\".", filename);
2446 void addGlobalLanguageDir(char*dir)
2449 globalParams = new GlobalParams("");
2451 msg("<notice> Adding %s to language pack directories", dir);
2455 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2456 strcpy(config_file, dir);
2457 strcat(config_file, dirseparator());
2458 strcat(config_file, "add-to-xpdfrc");
2460 fi = fopen(config_file, "rb");
2462 msg("<error> Could not open %s", config_file);
2465 globalParams->parseFile(new GString(config_file), fi);
2469 void addGlobalFontDir(char*dirname)
2471 #ifdef HAVE_DIRENT_H
2472 msg("<notice> Adding %s to font directories", dirname);
2473 lastfontdir = strdup(dirname);
2474 DIR*dir = opendir(dirname);
2476 msg("<warning> Couldn't open directory %s\n", dirname);
2481 ent = readdir (dir);
2485 char*name = ent->d_name;
2491 if(!strncasecmp(&name[l-4], ".pfa", 4))
2493 if(!strncasecmp(&name[l-4], ".pfb", 4))
2495 if(!strncasecmp(&name[l-4], ".ttf", 4))
2499 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2500 strcpy(fontname, dirname);
2501 strcat(fontname, dirseparator());
2502 strcat(fontname, name);
2503 addGlobalFont(fontname);
2508 msg("<warning> No dirent.h- unable to add font dir %s", dir);
2512 void GFXOutputDev::preparePage(int pdfpage, int outputpage)
2518 this->pagebuflen = 1024;
2519 this->pages = (int*)malloc(this->pagebuflen*sizeof(int));
2520 memset(this->pages, -1, this->pagebuflen*sizeof(int));
2522 while(pdfpage >= this->pagebuflen)
2524 int oldlen = this->pagebuflen;
2525 this->pagebuflen+=1024;
2526 this->pages = (int*)realloc(this->pages, this->pagebuflen*sizeof(int));
2527 memset(&this->pages[oldlen], -1, (this->pagebuflen-oldlen)*sizeof(int));
2530 this->pages[pdfpage] = outputpage;
2531 if(pdfpage>this->pagepos)
2532 this->pagepos = pdfpage;
2538 double width,height;
2541 BBox mkBBox(GfxState*state, double*bbox, double width, double height)
2543 double xMin, yMin, xMax, yMax, x, y;
2544 double tx, ty, w, h;
2545 // transform the bbox
2546 state->transform(bbox[0], bbox[1], &x, &y);
2549 state->transform(bbox[0], bbox[3], &x, &y);
2552 } else if (x > xMax) {
2557 } else if (y > yMax) {
2560 state->transform(bbox[2], bbox[1], &x, &y);
2563 } else if (x > xMax) {
2568 } else if (y > yMax) {
2571 state->transform(bbox[2], bbox[3], &x, &y);
2574 } else if (x > xMax) {
2579 } else if (y > yMax) {
2582 tx = (int)floor(xMin);
2585 } else if (tx > width) {
2588 ty = (int)floor(yMin);
2591 } else if (ty > height) {
2594 w = (int)ceil(xMax) - tx + 1;
2595 if (tx + w > width) {
2601 h = (int)ceil(yMax) - ty + 1;
2602 if (ty + h > height) {
2616 #if xpdfUpdateVersion >= 16
2617 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2618 GfxColorSpace *blendingColorSpace,
2619 GBool isolated, GBool knockout,
2622 char*colormodename = "";
2623 BBox rect = mkBBox(state, bbox, this->width, this->height);
2625 if(blendingColorSpace) {
2626 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2628 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);
2629 dbg("using clipping rect %f/%f/%f/%f\n", rect.posx,rect.posy,rect.width,rect.height);
2630 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);
2631 states[statepos].createsoftmask |= forSoftMask;
2632 states[statepos].transparencygroup = !forSoftMask;
2633 states[statepos].olddevice = this->device;
2635 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2637 gfxdevice_record_init(this->device);
2639 /*if(!forSoftMask) { ////???
2640 state->setFillOpacity(0.0);
2645 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2648 dbg("endTransparencyGroup");
2649 msg("<verbose> endTransparencyGroup");
2651 gfxdevice_t*r = this->device;
2653 this->device = states[statepos].olddevice;
2655 if(states[statepos].createsoftmask) {
2656 states[statepos-1].softmaskrecording = r->finish(r);
2658 states[statepos-1].grouprecording = r->finish(r);
2661 states[statepos].createsoftmask = 0;
2662 states[statepos].transparencygroup = 0;
2666 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2668 char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2669 "colordodge","colorburn","hardlight","softlight","difference",
2670 "exclusion","hue","saturation","color","luminosity"};
2672 dbg("paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2673 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2675 if(state->getBlendMode() == gfxBlendNormal)
2676 infofeature("transparency groups");
2679 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2680 warnfeature(buffer, 0);
2683 gfxresult_t*grouprecording = states[statepos].grouprecording;
2685 if(state->getBlendMode() == gfxBlendNormal) {
2687 gfxdevice_ops_init(&ops, this->device, (unsigned char)(state->getFillOpacity()*255));
2688 gfxresult_record_replay(grouprecording, &ops);
2691 grouprecording->destroy(grouprecording);
2693 states[statepos].grouprecording = 0;
2696 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2698 /* alpha = 1: retrieve mask values from alpha layer
2699 alpha = 0: retrieve mask values from luminance */
2700 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2701 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2702 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2703 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2705 infofeature("soft masks");
2707 warnfeature("soft masks from alpha channel",0);
2709 states[statepos].olddevice = this->device;
2710 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2711 gfxdevice_record_init(this->device);
2713 dbg("softmaskrecording is %08x at statepos %d\n", states[statepos].softmaskrecording, statepos);
2715 states[statepos].softmask = 1;
2718 static inline Guchar div255(int x) {
2719 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2722 void GFXOutputDev::clearSoftMask(GfxState *state)
2724 if(!states[statepos].softmask)
2726 states[statepos].softmask = 0;
2727 dbg("clearSoftMask statepos=%d", statepos);
2728 msg("<verbose> clearSoftMask");
2730 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2731 msg("<error> Error in softmask/tgroup ordering");
2735 gfxresult_t*mask = states[statepos].softmaskrecording;
2736 gfxresult_t*below = this->device->finish(this->device);
2737 this->device = states[statepos].olddevice;
2739 /* get outline of all objects below the soft mask */
2740 gfxdevice_t uniondev;
2741 gfxdevice_union_init(&uniondev, 0);
2742 gfxresult_record_replay(below, &uniondev);
2743 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2744 uniondev.finish(&uniondev);
2746 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2748 this->device->startclip(this->device, belowoutline);
2749 gfxresult_record_replay(below, this->device);
2750 gfxresult_record_replay(mask, this->device);
2751 this->device->endclip(this->device);
2752 gfxline_free(belowoutline);
2755 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2757 gfxdevice_t belowrender;
2758 gfxdevice_render_init(&belowrender);
2759 belowrender.setparameter(&belowrender, "fillwhite", "1"); //for isolated=0?
2760 belowrender.setparameter(&belowrender, "antialize", "2");
2761 belowrender.startpage(&belowrender, width, height);
2762 gfxresult_record_replay(below, &belowrender);
2763 belowrender.endpage(&belowrender);
2764 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2765 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2767 gfxdevice_t maskrender;
2768 gfxdevice_render_init(&maskrender);
2769 maskrender.startpage(&maskrender, width, height);
2770 gfxresult_record_replay(mask, &maskrender);
2771 maskrender.endpage(&maskrender);
2772 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2773 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2775 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2776 msg("<fatal> Internal error in mask drawing");
2781 for(y=0;y<height;y++) {
2782 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2783 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2784 for(x=0;x<width;x++) {
2785 l2->a = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2787 /* premultiply alpha */
2788 l2->r = div255(l2->a*l2->r);
2789 l2->g = div255(l2->a*l2->g);
2790 l2->b = div255(l2->a*l2->b);
2796 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2799 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2800 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2802 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2804 mask->destroy(mask);
2805 below->destroy(below);
2806 maskresult->destroy(maskresult);
2807 belowresult->destroy(belowresult);
2808 states[statepos].softmaskrecording = 0;
2817 delete globalParams;globalParams=0;
2818 Object::memCheck(stderr);