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"
73 #include "../art/libart.h"
74 #include "../devices/artsutils.c"
81 typedef struct _fontfile
88 static fontfile_t fonts[2048];
89 static int fontnum = 0;
93 static char* lastfontdir = 0;
104 {"Times-Roman", "n021003l", n021003l_afm, n021003l_afm_len, n021003l_pfb, n021003l_pfb_len},
105 {"Times-Italic", "n021023l", n021023l_afm, n021023l_afm_len, n021023l_pfb, n021023l_pfb_len},
106 {"Times-Bold", "n021004l", n021004l_afm, n021004l_afm_len, n021004l_pfb, n021004l_pfb_len},
107 {"Times-BoldItalic", "n021024l", n021024l_afm, n021024l_afm_len, n021024l_pfb, n021024l_pfb_len},
108 {"Helvetica", "n019003l", n019003l_afm, n019003l_afm_len, n019003l_pfb, n019003l_pfb_len},
109 {"Helvetica-Oblique", "n019023l", n019023l_afm, n019023l_afm_len, n019023l_pfb, n019023l_pfb_len},
110 {"Helvetica-Bold", "n019004l", n019004l_afm, n019004l_afm_len, n019004l_pfb, n019004l_pfb_len},
111 {"Helvetica-BoldOblique", "n019024l", n019024l_afm, n019024l_afm_len, n019024l_pfb, n019024l_pfb_len},
112 {"Courier", "n022003l", n022003l_afm, n022003l_afm_len, n022003l_pfb, n022003l_pfb_len},
113 {"Courier-Oblique", "n022023l", n022023l_afm, n022023l_afm_len, n022023l_pfb, n022023l_pfb_len},
114 {"Courier-Bold", "n022004l", n022004l_afm, n022004l_afm_len, n022004l_pfb, n022004l_pfb_len},
115 {"Courier-BoldOblique", "n022024l", n022024l_afm, n022024l_afm_len, n022024l_pfb, n022024l_pfb_len},
116 {"Symbol", "s050000l", s050000l_afm, s050000l_afm_len, s050000l_pfb, s050000l_pfb_len},
117 {"ZapfDingbats", "d050000l", d050000l_afm, d050000l_afm_len, d050000l_pfb, d050000l_pfb_len}};
120 static int verbose = 0;
121 static int dbgindent = 0;
122 static void dbg(const char*format, ...)
129 va_start(arglist, format);
130 vsprintf(buf, format, arglist);
133 while(l && buf[l-1]=='\n') {
138 int indent = dbgindent;
148 typedef struct _feature
151 struct _feature*next;
153 feature_t*featurewarnings = 0;
155 void GFXOutputDev::showfeature(const char*feature,char fully, char warn)
157 feature_t*f = featurewarnings;
159 if(!strcmp(feature, f->string))
163 f = (feature_t*)malloc(sizeof(feature_t));
164 f->string = strdup(feature);
165 f->next = featurewarnings;
168 msg("<warning> %s not yet %ssupported!",feature,fully?"fully ":"");
169 if(this->config_break_on_warning) {
170 msg("<fatal> Aborting conversion due to unsupported feature");
174 msg("<notice> File contains %s",feature);
177 void GFXOutputDev::warnfeature(const char*feature,char fully)
179 showfeature(feature,fully,1);
181 void GFXOutputDev::infofeature(const char*feature)
183 showfeature(feature,0,0);
186 GFXOutputState::GFXOutputState() {
188 this->createsoftmask = 0;
189 this->transparencygroup = 0;
191 this->grouprecording = 0;
195 GBool GFXOutputDev::interpretType3Chars()
197 return this->do_interpretType3Chars;
200 typedef struct _drawnchar
218 chars = (drawnchar_t*)malloc(sizeof(drawnchar_t)*buf_size);
219 memset(chars, 0, sizeof(drawnchar_t)*buf_size);
224 free(chars);chars = 0;
231 chars = (drawnchar_t*)realloc(chars, sizeof(drawnchar_t)*buf_size);
235 void addChar(int charid, gfxcoord_t x, gfxcoord_t y, gfxcolor_t color)
238 chars[num_chars].x = x;
239 chars[num_chars].y = y;
240 chars[num_chars].color = color;
241 chars[num_chars].charid = charid;
245 static char*getFontID(GfxFont*font);
247 GFXOutputDev::GFXOutputDev(parameter_t*p)
250 this->textmodeinfo = 0;
253 this->type3active = 0;
256 this->substitutepos = 0;
257 this->type3Warning = 0;
258 this->user_movex = 0;
259 this->user_movey = 0;
262 this->user_clipx1 = 0;
263 this->user_clipy1 = 0;
264 this->user_clipx2 = 0;
265 this->user_clipy2 = 0;
266 this->current_text_stroke = 0;
267 this->current_text_clip = 0;
269 this->outer_clip_box = 0;
271 this->pagebuflen = 0;
273 this->config_use_fontconfig=1;
274 this->config_break_on_warning=0;
275 this->config_remapunicode=0;
276 this->config_transparent=0;
277 this->do_interpretType3Chars = gTrue;
279 this->parameters = p;
281 memset(states, 0, sizeof(states));
283 /* configure device */
285 setParameter(p->name, p->value);
290 void GFXOutputDev::setParameter(const char*key, const char*value)
292 if(!strcmp(key,"rawtext")) {
293 this->do_interpretType3Chars = atoi(value)^1;
294 } else if(!strcmp(key,"breakonwarning")) {
295 this->config_break_on_warning = atoi(value);
296 } else if(!strcmp(key,"fontconfig")) {
297 this->config_use_fontconfig = atoi(value);
298 } else if(!strcmp(key,"remapunicode")) {
299 this->config_remapunicode = atoi(value);
300 } else if(!strcmp(key,"transparent")) {
301 this->config_transparent = atoi(value);
305 void GFXOutputDev::setDevice(gfxdevice_t*dev)
307 parameter_t*p = this->parameters;
309 /* pass parameters to output device */
313 this->device->setparameter(this->device, p->name, p->value);
319 void GFXOutputDev::setMove(int x,int y)
321 this->user_movex = x;
322 this->user_movey = y;
325 void GFXOutputDev::setClip(int x1,int y1,int x2,int y2)
327 if(x2<x1) {int x3=x1;x1=x2;x2=x3;}
328 if(y2<y1) {int y3=y1;y1=y2;y2=y3;}
330 this->user_clipx1 = x1;
331 this->user_clipy1 = y1;
332 this->user_clipx2 = x2;
333 this->user_clipy2 = y2;
336 static char*getFontID(GfxFont*font)
338 Ref*ref = font->getID();
339 GString*gstr = font->getName();
340 char* fname = gstr==0?0:gstr->getCString();
343 sprintf(buf, "font-%d-%d", ref->num, ref->gen);
345 sprintf(buf, "%s-%d-%d", fname, ref->num, ref->gen);
350 static char*getFontName(GfxFont*font)
353 GString*gstr = font->getName();
354 char* fname = gstr==0?0:gstr->getCString();
358 sprintf(buf, "UFONT%d", r->num);
359 fontid = strdup(buf);
361 fontid = strdup(fname);
365 char* plus = strchr(fontid, '+');
366 if(plus && plus < &fontid[strlen(fontid)-1]) {
367 fontname = strdup(plus+1);
369 fontname = strdup(fontid);
375 static char mybuf[1024];
376 static char* gfxstate2str(GfxState *state)
380 bufpos+=sprintf(bufpos,"CTM[%.3f/%.3f/%.3f/%.3f/%.3f/%.3f] ",
387 if(state->getX1()!=0.0)
388 bufpos+=sprintf(bufpos,"X1-%.1f ",state->getX1());
389 if(state->getY1()!=0.0)
390 bufpos+=sprintf(bufpos,"Y1-%.1f ",state->getY1());
391 bufpos+=sprintf(bufpos,"X2-%.1f ",state->getX2());
392 bufpos+=sprintf(bufpos,"Y2-%.1f ",state->getY2());
393 bufpos+=sprintf(bufpos,"PW%.1f ",state->getPageWidth());
394 bufpos+=sprintf(bufpos,"PH%.1f ",state->getPageHeight());
395 /*bufpos+=sprintf(bufpos,"FC[%.1f/%.1f] ",
396 state->getFillColor()->c[0], state->getFillColor()->c[1]);
397 bufpos+=sprintf(bufpos,"SC[%.1f/%.1f] ",
398 state->getStrokeColor()->c[0], state->getFillColor()->c[1]);*/
399 /* bufpos+=sprintf(bufpos,"FC[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f]",
400 state->getFillColor()->c[0], state->getFillColor()->c[1],
401 state->getFillColor()->c[2], state->getFillColor()->c[3],
402 state->getFillColor()->c[4], state->getFillColor()->c[5],
403 state->getFillColor()->c[6], state->getFillColor()->c[7]);
404 bufpos+=sprintf(bufpos,"SC[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f]",
405 state->getStrokeColor()->c[0], state->getFillColor()->c[1],
406 state->getStrokeColor()->c[2], state->getFillColor()->c[3],
407 state->getStrokeColor()->c[4], state->getFillColor()->c[5],
408 state->getStrokeColor()->c[6], state->getFillColor()->c[7]);*/
409 state->getFillRGB(&rgb);
410 if(rgb.r || rgb.g || rgb.b)
411 bufpos+=sprintf(bufpos,"FR[%.1f/%.1f/%.1f] ", rgb.r,rgb.g,rgb.b);
412 state->getStrokeRGB(&rgb);
413 if(rgb.r || rgb.g || rgb.b)
414 bufpos+=sprintf(bufpos,"SR[%.1f/%.1f/%.1f] ", rgb.r,rgb.g,rgb.b);
415 if(state->getFillColorSpace()->getNComps()>1)
416 bufpos+=sprintf(bufpos,"CS[[%d]] ",state->getFillColorSpace()->getNComps());
417 if(state->getStrokeColorSpace()->getNComps()>1)
418 bufpos+=sprintf(bufpos,"SS[[%d]] ",state->getStrokeColorSpace()->getNComps());
419 if(state->getFillPattern())
420 bufpos+=sprintf(bufpos,"FP%08x ", state->getFillPattern());
421 if(state->getStrokePattern())
422 bufpos+=sprintf(bufpos,"SP%08x ", state->getStrokePattern());
424 if(state->getFillOpacity()!=1.0)
425 bufpos+=sprintf(bufpos,"FO%.1f ", state->getFillOpacity());
426 if(state->getStrokeOpacity()!=1.0)
427 bufpos+=sprintf(bufpos,"SO%.1f ", state->getStrokeOpacity());
429 bufpos+=sprintf(bufpos,"LW%.1f ", state->getLineWidth());
434 state->getLineDash(&dash, &length, &start);
438 bufpos+=sprintf(bufpos,"DASH%.1f[",start);
439 for(t=0;t<length;t++) {
440 bufpos+=sprintf(bufpos,"D%.1f",dash[t]);
442 bufpos+=sprintf(bufpos,"]");
445 if(state->getFlatness()!=1)
446 bufpos+=sprintf(bufpos,"F%d ", state->getFlatness());
447 if(state->getLineJoin()!=0)
448 bufpos+=sprintf(bufpos,"J%d ", state->getLineJoin());
449 if(state->getLineJoin()!=0)
450 bufpos+=sprintf(bufpos,"C%d ", state->getLineCap());
451 if(state->getLineJoin()!=0)
452 bufpos+=sprintf(bufpos,"ML%d ", state->getMiterLimit());
454 if(state->getFont() && getFontID(state->getFont()))
455 bufpos+=sprintf(bufpos,"F\"%s\" ",getFontID(state->getFont()));
456 bufpos+=sprintf(bufpos,"FS%.1f ", state->getFontSize());
457 bufpos+=sprintf(bufpos,"MAT[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f] ", state->getTextMat()[0],state->getTextMat()[1],state->getTextMat()[2],
458 state->getTextMat()[3],state->getTextMat()[4],state->getTextMat()[5]);
459 if(state->getCharSpace())
460 bufpos+=sprintf(bufpos,"CS%.5f ", state->getCharSpace());
461 if(state->getWordSpace())
462 bufpos+=sprintf(bufpos,"WS%.5f ", state->getWordSpace());
463 if(state->getHorizScaling()!=1.0)
464 bufpos+=sprintf(bufpos,"SC%.1f ", state->getHorizScaling());
465 if(state->getLeading())
466 bufpos+=sprintf(bufpos,"L%.1f ", state->getLeading());
468 bufpos+=sprintf(bufpos,"R%.1f ", state->getRise());
469 if(state->getRender())
470 bufpos+=sprintf(bufpos,"R%d ", state->getRender());
471 bufpos+=sprintf(bufpos,"P%08x ", state->getPath());
472 bufpos+=sprintf(bufpos,"CX%.1f ", state->getCurX());
473 bufpos+=sprintf(bufpos,"CY%.1f ", state->getCurY());
474 if(state->getLineX())
475 bufpos+=sprintf(bufpos,"LX%.1f ", state->getLineX());
476 if(state->getLineY())
477 bufpos+=sprintf(bufpos,"LY%.1f ", state->getLineY());
478 bufpos+=sprintf(bufpos," ");
482 static void dumpFontInfo(const char*loglevel, GfxFont*font);
483 static int lastdumps[1024];
484 static int lastdumppos = 0;
489 static void showFontError(GfxFont*font, int nr)
493 for(t=0;t<lastdumppos;t++)
494 if(lastdumps[t] == r->num)
498 if(lastdumppos<sizeof(lastdumps)/sizeof(int))
499 lastdumps[lastdumppos++] = r->num;
501 msg("<warning> The following font caused problems:");
503 msg("<warning> The following font caused problems (substituting):");
505 msg("<warning> The following Type 3 Font will be rendered as graphics:");
506 dumpFontInfo("<warning>", font);
509 static void dumpFontInfo(const char*loglevel, GfxFont*font)
511 char* id = getFontID(font);
512 char* name = getFontName(font);
513 Ref* r=font->getID();
514 msg("%s=========== %s (ID:%d,%d) ==========\n", loglevel, name, r->num,r->gen);
516 GString*gstr = font->getTag();
518 msg("%s| Tag: %s\n", loglevel, id);
520 if(font->isCIDFont()) msg("%s| is CID font\n", loglevel);
522 GfxFontType type=font->getType();
524 case fontUnknownType:
525 msg("%s| Type: unknown\n",loglevel);
528 msg("%s| Type: 1\n",loglevel);
531 msg("%s| Type: 1C\n",loglevel);
534 msg("%s| Type: 3\n",loglevel);
537 msg("%s| Type: TrueType\n",loglevel);
540 msg("%s| Type: CIDType0\n",loglevel);
543 msg("%s| Type: CIDType0C\n",loglevel);
546 msg("%s| Type: CIDType2\n",loglevel);
551 GBool embedded = font->getEmbeddedFontID(&embRef);
553 if(font->getEmbeddedFontName()) {
554 embeddedName = font->getEmbeddedFontName()->getCString();
557 msg("%s| Embedded id: %s id: %d\n",loglevel, FIXNULL(embeddedName), embRef.num);
559 gstr = font->getExtFontFile();
561 msg("%s| External Font file: %s\n", loglevel, FIXNULL(gstr->getCString()));
563 // Get font descriptor flags.
564 if(font->isFixedWidth()) msg("%s| is fixed width\n", loglevel);
565 if(font->isSerif()) msg("%s| is serif\n", loglevel);
566 if(font->isSymbolic()) msg("%s| is symbolic\n", loglevel);
567 if(font->isItalic()) msg("%s| is italic\n", loglevel);
568 if(font->isBold()) msg("%s| is bold\n", loglevel);
574 //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");}
575 //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");}
578 void dump_outline(gfxline_t*line)
581 if(line->type == gfx_moveTo) {
582 msg("<debug> | moveTo %.2f %.2f", line->x,line->y);
583 } else if(line->type == gfx_lineTo) {
584 msg("<debug> | lineTo %.2f %.2f", line->x,line->y);
585 } else if(line->type == gfx_splineTo) {
586 msg("<debug> | splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y);
592 gfxline_t* gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user_movex, int user_movey)
594 int num = path->getNumSubpaths();
597 double lastx=0,lasty=0,posx=0,posy=0;
600 msg("<warning> empty path");
604 gfxdrawer_target_gfxline(&draw);
606 for(t = 0; t < num; t++) {
607 GfxSubpath *subpath = path->getSubpath(t);
608 int subnum = subpath->getNumPoints();
609 double bx=0,by=0,cx=0,cy=0;
611 for(s=0;s<subnum;s++) {
614 state->transform(subpath->getX(s),subpath->getY(s),&x,&y);
619 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
620 draw.lineTo(&draw, lastx, lasty);
622 draw.moveTo(&draw, x,y);
627 } else if(subpath->getCurve(s) && cpos==0) {
631 } else if(subpath->getCurve(s) && cpos==1) {
639 draw.lineTo(&draw, x,y);
641 gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y, 0.05);
648 /* fix non-closed lines */
649 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
650 draw.lineTo(&draw, lastx, lasty);
652 gfxline_t*result = (gfxline_t*)draw.result(&draw);
654 gfxline_optimize(result);
659 GBool GFXOutputDev::useTilingPatternFill()
661 infofeature("tiled patterns");
665 GBool GFXOutputDev::useShadedFills()
667 infofeature("shaded fills");
671 GBool GFXOutputDev::useDrawForm()
673 infofeature("forms");
676 void GFXOutputDev::drawForm(Ref id)
678 msg("<error> drawForm not implemented");
680 GBool GFXOutputDev::needNonText()
684 void GFXOutputDev::endPage()
686 msg("<verbose> endPage");
689 #define STROKE_FILL 1
690 #define STROKE_CLIP 2
691 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line, int flags)
693 int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
694 int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
695 double miterLimit = state->getMiterLimit();
696 double width = state->getTransformedLineWidth();
699 double opaq = state->getStrokeOpacity();
701 state->getFillRGB(&rgb);
703 state->getStrokeRGB(&rgb);
705 col.r = colToByte(rgb.r);
706 col.g = colToByte(rgb.g);
707 col.b = colToByte(rgb.b);
708 col.a = (unsigned char)(opaq*255);
710 gfx_capType capType = gfx_capRound;
711 if(lineCap == 0) capType = gfx_capButt;
712 else if(lineCap == 1) capType = gfx_capRound;
713 else if(lineCap == 2) capType = gfx_capSquare;
715 gfx_joinType joinType = gfx_joinRound;
716 if(lineJoin == 0) joinType = gfx_joinMiter;
717 else if(lineJoin == 1) joinType = gfx_joinRound;
718 else if(lineJoin == 2) joinType = gfx_joinBevel;
721 double dashphase = 0;
723 state->getLineDash(&ldash, &dashnum, &dashphase);
727 if(dashnum && ldash) {
728 float * dash = (float*)malloc(sizeof(float)*(dashnum+1));
730 msg("<trace> %d dashes", dashnum);
731 msg("<trace> | phase: %f", dashphase);
732 for(t=0;t<dashnum;t++) {
734 msg("<trace> | d%-3d: %f", t, ldash[t]);
737 if(getLogLevel() >= LOGLEVEL_TRACE) {
741 line2 = gfxtool_dash_line(line, dash, dashphase);
744 msg("<trace> After dashing:");
747 if(getLogLevel() >= LOGLEVEL_TRACE) {
748 msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x\n",
750 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
751 lineCap==0?"butt": (lineJoin==1?"round":"square"),
753 col.r,col.g,col.b,col.a
758 if(flags&STROKE_FILL) {
759 ArtSVP* svp = gfxstrokeToSVP(line, width, capType, joinType, miterLimit);
760 gfxline_t*gfxline = SVPtogfxline(svp);
761 if(getLogLevel() >= LOGLEVEL_TRACE) {
762 dump_outline(gfxline);
765 msg("<warning> Empty polygon (resulting from stroked line)");
767 if(flags&STROKE_CLIP) {
768 device->startclip(device, gfxline);
769 states[statepos].clipping++;
771 device->fill(device, gfxline, &col);
776 if(flags&STROKE_CLIP)
777 msg("<error> Stroke&clip not supported at the same time");
778 device->stroke(device, line, width, &col, capType, joinType, miterLimit);
785 gfxcolor_t getFillColor(GfxState * state)
788 double opaq = state->getFillOpacity();
789 state->getFillRGB(&rgb);
791 col.r = colToByte(rgb.r);
792 col.g = colToByte(rgb.g);
793 col.b = colToByte(rgb.b);
794 col.a = (unsigned char)(opaq*255);
798 void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line)
800 gfxcolor_t col = getFillColor(state);
802 if(getLogLevel() >= LOGLEVEL_TRACE) {
803 msg("<trace> fill %02x%02x%02x%02x\n", col.r, col.g, col.b, col.a);
806 device->fill(device, line, &col);
809 void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line)
811 if(getLogLevel() >= LOGLEVEL_TRACE) {
812 msg("<trace> clip\n");
816 device->startclip(device, line);
817 states[statepos].clipping++;
820 void GFXOutputDev::clip(GfxState *state)
822 GfxPath * path = state->getPath();
823 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
824 clipToGfxLine(state, line);
828 void GFXOutputDev::eoClip(GfxState *state)
830 GfxPath * path = state->getPath();
831 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
833 if(getLogLevel() >= LOGLEVEL_TRACE) {
834 msg("<trace> eoclip\n");
838 device->startclip(device, line);
839 states[statepos].clipping++;
842 void GFXOutputDev::clipToStrokePath(GfxState *state)
844 GfxPath * path = state->getPath();
845 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
847 if(getLogLevel() >= LOGLEVEL_TRACE) {
848 msg("<trace> cliptostrokepath\n");
852 strokeGfxline(state, line, STROKE_FILL|STROKE_CLIP);
856 void GFXOutputDev::endframe()
859 device->endclip(device);
864 void GFXOutputDev::finish()
868 device->endclip(device);
874 GFXOutputDev::~GFXOutputDev()
879 free(this->pages); this->pages = 0;
882 fontlist_t*l = this->fontlist;
884 fontlist_t*next = l->next;
886 gfxfont_free(l->font);
887 free(l->filename);l->filename=0;
893 GBool GFXOutputDev::upsideDown()
897 GBool GFXOutputDev::useDrawChar()
902 const char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
903 "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
905 #define RENDER_FILL 0
906 #define RENDER_STROKE 1
907 #define RENDER_FILLSTROKE 2
908 #define RENDER_INVISIBLE 3
909 #define RENDER_CLIP 4
911 static char tmp_printstr[4096];
912 char* makeStringPrintable(char*str)
914 int len = strlen(str);
929 tmp_printstr[len++] = '.';
930 tmp_printstr[len++] = '.';
931 tmp_printstr[len++] = '.';
933 tmp_printstr[len] = 0;
938 int getGfxCharID(gfxfont_t*font, int charnr, char *charname, int u)
940 const char*uniname = 0;
945 /* find out char name from unicode index
946 TODO: should be precomputed
948 for(t=0;t<sizeof(nameToUnicodeTab)/sizeof(nameToUnicodeTab[0]);t++) {
949 if(nameToUnicodeTab[t].u == u) {
950 uniname = nameToUnicodeTab[t].name;
958 for(t=0;t<font->num_glyphs;t++) {
959 if(font->glyphs[t].name && !strcmp(font->glyphs[t].name,charname)) {
960 msg("<debug> Char [%d,>%s<,%d] maps to %d\n", charnr, charname, u, t);
964 /* if we didn't find the character, maybe
965 we can find the capitalized version */
966 for(t=0;t<font->num_glyphs;t++) {
967 if(font->glyphs[t].name && !strcasecmp(font->glyphs[t].name,charname)) {
968 msg("<debug> Char [%d,>>%s<<,%d] maps to %d\n", charnr, charname, u, t);
976 for(t=0;t<font->num_glyphs;t++) {
977 if(font->glyphs[t].name && !strcmp(font->glyphs[t].name,uniname)) {
978 msg("<debug> Char [%d,%s,>%d(%s)<] maps to %d\n", charnr, charname, u, uniname, t);
982 /* if we didn't find the character, maybe
983 we can find the capitalized version */
984 for(t=0;t<font->num_glyphs;t++) {
985 if(font->glyphs[t].name && !strcasecmp(font->glyphs[t].name,uniname)) {
986 msg("<debug> Char [%d,%s,>>%d(%s)<<] maps to %d\n", charnr, charname, u, uniname, t);
992 /* try to use the unicode id */
993 if(u>=0 && u<font->max_unicode && font->unicode2glyph[u]>=0) {
994 msg("<debug> Char [%d,%s,>%d<] maps to %d\n", charnr, charname, u, font->unicode2glyph[u]);
995 return font->unicode2glyph[u];
997 /* try to use the unicode|0xe000 (needed for some WingDings fonts)
998 FIXME: do this only if we know the font is wingdings?
1001 if(u>=0 && u<font->max_unicode && font->unicode2glyph[u]>=0) {
1002 msg("<debug> Char [%d,%s,>%d<] maps to %d\n", charnr, charname, u, font->unicode2glyph[u]);
1003 return font->unicode2glyph[u];
1006 if(charnr>=0 && charnr<font->num_glyphs) {
1007 msg("<debug> Char [>%d<,%s,%d] maps to %d\n", charnr, charname, u, charnr);
1015 void GFXOutputDev::beginString(GfxState *state, GString *s)
1017 int render = state->getRender();
1018 if(current_text_stroke) {
1019 msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
1022 msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
1023 double m11,m21,m12,m22;
1024 // msg("<debug> %s beginstring \"%s\"\n", gfxstate2str(state), s->getCString());
1025 state->getFontTransMat(&m11, &m12, &m21, &m22);
1026 m11 *= state->getHorizScaling();
1027 m21 *= state->getHorizScaling();
1029 this->current_font_matrix.m00 = m11 / 1024.0;
1030 this->current_font_matrix.m01 = m12 / 1024.0;
1031 this->current_font_matrix.m10 = -m21 / 1024.0;
1032 this->current_font_matrix.m11 = -m22 / 1024.0;
1033 this->current_font_matrix.tx = 0;
1034 this->current_font_matrix.ty = 0;
1036 gfxmatrix_t m = this->current_font_matrix;
1039 static gfxline_t* mkEmptyGfxShape(double x, double y)
1041 gfxline_t*line = (gfxline_t*)malloc(sizeof(gfxline_t));
1042 line->x = x;line->y = y;line->type = gfx_moveTo;line->next = 0;
1046 static char isValidUnicode(int c)
1048 if(c>=32 && c<0x2fffe)
1053 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
1054 double dx, double dy,
1055 double originX, double originY,
1056 CharCode c, int nBytes, Unicode *_u, int uLen)
1058 int render = state->getRender();
1059 // check for invisible text -- this is used by Acrobat Capture
1061 msg("<debug> Ignoring invisible text: char %d at %f,%f", c, x, y);
1065 gfxcolor_t col = getFillColor(state);
1067 Gushort *CIDToGIDMap = 0;
1068 GfxFont*font = state->getFont();
1070 if(font->getType() == fontType3 && do_interpretType3Chars) {
1071 /* type 3 chars are passed as graphics */
1072 msg("<debug> type3 char at %f/%f", x, y);
1082 /* char*fontname = getFontName(font);
1083 if(u<256 && strstr(fontname, "ingdings")) {
1084 // symbols are at 0xe000 in the unicode table
1089 if(font->isCIDFont()) {
1090 GfxCIDFont*cfont = (GfxCIDFont*)font;
1092 if(font->getType() == fontCIDType2)
1093 CIDToGIDMap = cfont->getCIDToGID();
1096 font8 = (Gfx8BitFont*)font;
1097 char**enc=font8->getEncoding();
1101 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);
1104 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);
1110 charid = getGfxCharID(current_gfxfont, c, name, u);
1112 charid = getGfxCharID(current_gfxfont, c, name, -1);
1115 /* multiple unicodes- should usually map to a ligature.
1116 if the ligature doesn't exist, we need to draw
1117 the characters one-by-one. */
1119 msg("<warning> ligature %d missing in font %s\n", c, current_gfxfont->id);
1120 for(t=0;t<uLen;t++) {
1121 drawChar(state, x, y, dx, dy, originX, originY, c, nBytes, _u+t, 1);
1127 msg("<warning> Didn't find character '%s' (c=%d,u=%d) in current charset (%s, %d characters)",
1128 FIXNULL(name),c, u, FIXNULL((char*)current_gfxfont->id), current_gfxfont->num_glyphs);
1131 //useless- the font has already been passed to the output device
1132 //if(!isValidUnicode(current_gfxfont->glyphs[charid].unicode) && isValidUnicode(u)) {
1133 // current_gfxfont->glyphs[charid].
1136 gfxmatrix_t m = this->current_font_matrix;
1137 state->transform(x, y, &m.tx, &m.ty);
1138 m.tx += user_movex + clipmovex;
1139 m.ty += user_movey + clipmovey;
1141 if((!name || strcmp(name, "space")) && charid!=32 && u!=32)
1143 gfxline_t*l = current_gfxfont->glyphs[charid].line;
1147 if((l->type == gfx_lineTo || l->type == gfx_splineTo) && l->x!=x && l->y!=y) {
1153 static int lastemptychar = 0;
1154 if(charid != lastemptychar)
1155 msg("<warning> Drawing empty character charid=%d u=%d", charid, u);
1156 lastemptychar = charid;
1160 if(render == RENDER_FILL) {
1161 device->drawchar(device, current_gfxfont, charid, &col, &m);
1163 msg("<debug> Drawing glyph %d as shape", charid);
1165 msg("<notice> Some texts will be rendered as shape");
1168 gfxline_t*glyph = current_gfxfont->glyphs[charid].line;
1169 gfxline_t*tglyph = gfxline_clone(glyph);
1170 gfxline_transform(tglyph, &m);
1171 if((render&3) != RENDER_INVISIBLE) {
1172 gfxline_t*add = gfxline_clone(tglyph);
1173 current_text_stroke = gfxline_append(current_text_stroke, add);
1175 if(render&RENDER_CLIP) {
1176 gfxline_t*add = gfxline_clone(tglyph);
1177 current_text_clip = gfxline_append(current_text_clip, add);
1178 if(!current_text_clip) {
1179 current_text_clip = mkEmptyGfxShape(m.tx, m.ty);
1182 gfxline_free(tglyph);
1186 void GFXOutputDev::endString(GfxState *state)
1188 int render = state->getRender();
1189 msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
1191 if(current_text_stroke) {
1192 /* fillstroke and stroke text rendering objects we can process right
1193 now (as there may be texts of other rendering modes in this
1194 text object)- clipping objects have to wait until endTextObject,
1196 device->setparameter(device, "mark","TXT");
1197 if((render&3) == RENDER_FILL) {
1198 fillGfxLine(state, current_text_stroke);
1199 gfxline_free(current_text_stroke);
1200 current_text_stroke = 0;
1201 } else if((render&3) == RENDER_FILLSTROKE) {
1202 fillGfxLine(state, current_text_stroke);
1203 strokeGfxline(state, current_text_stroke,0);
1204 gfxline_free(current_text_stroke);
1205 current_text_stroke = 0;
1206 } else if((render&3) == RENDER_STROKE) {
1207 strokeGfxline(state, current_text_stroke,0);
1208 gfxline_free(current_text_stroke);
1209 current_text_stroke = 0;
1211 device->setparameter(device, "mark","");
1215 void GFXOutputDev::endTextObject(GfxState *state)
1217 int render = state->getRender();
1218 msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1220 if(current_text_clip) {
1221 device->setparameter(device, "mark","TXT");
1222 clipToGfxLine(state, current_text_clip);
1223 device->setparameter(device, "mark","");
1224 gfxline_free(current_text_clip);
1225 current_text_clip = 0;
1229 /* the logic seems to be as following:
1230 first, beginType3Char is called, with the charcode and the coordinates.
1231 if this function returns true, it already knew about the char and has now drawn it.
1232 if the function returns false, it's a new char, and type3D1 is called with some parameters-
1233 the all draw operations until endType3Char are part of the char (which in this moment is
1234 at the position first passed to beginType3Char). the char ends with endType3Char.
1236 The drawing operations between beginType3Char and endType3Char are somewhat different to
1237 the normal ones. For example, the fillcolor equals the stroke color.
1240 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode code, Unicode *u, int uLen)
1242 msg("<debug> beginType3Char %d, %08x, %d", code, *u, uLen);
1247 gfxcolor_t col={255,0,0,0};
1248 gfxmatrix_t m = {1,0,0, 0,1,0};
1250 for(t=0;t<uLen;t++) {
1251 device->drawchar(device, 0, u[t], &col, &m);
1254 /* the character itself is going to be passed using the draw functions */
1255 return gFalse; /* gTrue= is_in_cache? */
1258 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1259 msg("<debug> type3D0 width=%f height=%f", wx, wy);
1261 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1262 msg("<debug> type3D1 width=%f height=%f bbox=(%f,%f,%f,%f)", wx, wy,
1266 void GFXOutputDev::endType3Char(GfxState *state)
1269 msg("<debug> endType3Char");
1272 void GFXOutputDev::startFrame(int width, int height)
1274 if(outer_clip_box) {
1275 device->endclip(device);
1279 device->startpage(device, width, height);
1280 this->width = width;
1281 this->height = height;
1284 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
1286 this->currentpage = pageNum;
1288 int rot = doc->getPageRotate(1);
1291 gfxline_t clippath[5];
1293 white.r = white.g = white.b = white.a = 255;
1295 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1296 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1297 Use CropBox, not MediaBox, as page size
1304 state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1305 state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1307 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1308 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1310 this->clipmovex = -(int)x1;
1311 this->clipmovey = -(int)y1;
1313 /* apply user clip box */
1314 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1315 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1316 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1317 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1318 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1319 msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1322 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1324 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);
1326 msg("<verbose> page is rotated %d degrees\n", rot);
1328 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1329 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1330 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1331 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1332 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1333 device->startclip(device, clippath); outer_clip_box = 1;
1334 if(!config_transparent)
1335 device->fill(device, clippath, &white);
1339 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1341 double x1, y1, x2, y2, w;
1342 gfxline_t points[5];
1345 msg("<debug> drawlink\n");
1347 link->getRect(&x1, &y1, &x2, &y2);
1348 cvtUserToDev(x1, y1, &x, &y);
1349 points[0].type = gfx_moveTo;
1350 points[0].x = points[4].x = x + user_movex + clipmovex;
1351 points[0].y = points[4].y = y + user_movey + clipmovey;
1352 points[0].next = &points[1];
1353 cvtUserToDev(x2, y1, &x, &y);
1354 points[1].type = gfx_lineTo;
1355 points[1].x = x + user_movex + clipmovex;
1356 points[1].y = y + user_movey + clipmovey;
1357 points[1].next = &points[2];
1358 cvtUserToDev(x2, y2, &x, &y);
1359 points[2].type = gfx_lineTo;
1360 points[2].x = x + user_movex + clipmovex;
1361 points[2].y = y + user_movey + clipmovey;
1362 points[2].next = &points[3];
1363 cvtUserToDev(x1, y2, &x, &y);
1364 points[3].type = gfx_lineTo;
1365 points[3].x = x + user_movex + clipmovex;
1366 points[3].y = y + user_movey + clipmovey;
1367 points[3].next = &points[4];
1368 cvtUserToDev(x1, y1, &x, &y);
1369 points[4].type = gfx_lineTo;
1370 points[4].x = x + user_movex + clipmovex;
1371 points[4].y = y + user_movey + clipmovey;
1374 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f\n",
1375 points[0].x, points[0].y,
1376 points[1].x, points[1].y,
1377 points[2].x, points[2].y,
1378 points[3].x, points[3].y);
1380 LinkAction*action=link->getAction();
1383 const char*type = "-?-";
1386 msg("<trace> drawlink action=%d\n", action->getKind());
1387 switch(action->getKind())
1391 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1392 LinkDest *dest=NULL;
1393 if (ha->getDest()==NULL)
1394 dest=catalog->findDest(ha->getNamedDest());
1395 else dest=ha->getDest();
1397 if (dest->isPageRef()){
1398 Ref pageref=dest->getPageRef();
1399 page=catalog->findPage(pageref.num,pageref.gen);
1401 else page=dest->getPageNum();
1402 sprintf(buf, "%d", page);
1409 LinkGoToR*l = (LinkGoToR*)action;
1410 GString*g = l->getFileName();
1412 s = strdup(g->getCString());
1414 /* if the GoToR link has no filename, then
1415 try to find a refernce in the *local*
1417 GString*g = l->getNamedDest();
1419 s = strdup(g->getCString());
1425 LinkNamed*l = (LinkNamed*)action;
1426 GString*name = l->getName();
1428 s = strdup(name->lowerCase()->getCString());
1429 named = name->getCString();
1432 if(strstr(s, "next") || strstr(s, "forward"))
1434 page = currentpage + 1;
1436 else if(strstr(s, "prev") || strstr(s, "back"))
1438 page = currentpage - 1;
1440 else if(strstr(s, "last") || strstr(s, "end"))
1442 if(pages && pagepos>0)
1443 page = pages[pagepos-1];
1445 else if(strstr(s, "first") || strstr(s, "top"))
1453 case actionLaunch: {
1455 LinkLaunch*l = (LinkLaunch*)action;
1456 GString * str = new GString(l->getFileName());
1457 GString * params = l->getParams();
1459 str->append(params);
1460 s = strdup(str->getCString());
1467 LinkURI*l = (LinkURI*)action;
1468 GString*g = l->getURI();
1470 url = g->getCString();
1475 case actionUnknown: {
1477 LinkUnknown*l = (LinkUnknown*)action;
1482 msg("<error> Unknown link type!\n");
1487 if(!s) s = strdup("-?-");
1489 msg("<trace> drawlink s=%s\n", s);
1491 if(!linkinfo && (page || s))
1493 msg("<notice> File contains links");
1501 for(t=1;t<=pagepos;t++) {
1502 if(pages[t]==page) {
1511 sprintf(buf, "page%d", lpage);
1512 device->drawlink(device, points, buf);
1516 device->drawlink(device, points, s);
1519 msg("<verbose> \"%s\" link to \"%s\" (%d)\n", type, FIXNULL(s), page);
1523 void GFXOutputDev::saveState(GfxState *state) {
1524 dbg("saveState");dbgindent+=2;
1526 msg("<trace> saveState\n");
1529 msg("<error> Too many nested states in pdf.");
1533 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1534 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1535 states[statepos].clipping = 0;
1538 void GFXOutputDev::restoreState(GfxState *state) {
1539 dbgindent-=2; dbg("restoreState");
1542 msg("<error> Invalid restoreState");
1545 msg("<trace> restoreState%s%s", states[statepos].softmask?" (end softmask)":"",
1546 states[statepos].clipping?" (end clipping)":"");
1547 if(states[statepos].softmask) {
1548 clearSoftMask(state);
1551 while(states[statepos].clipping) {
1552 device->endclip(device);
1553 states[statepos].clipping--;
1558 char* writeOutStdFont(fontentry* f)
1563 char* tmpFileName = mktmpname(namebuf1);
1565 sprintf(namebuf2, "%s.afm", tmpFileName);
1566 fi = fopen(namebuf2, "wb");
1569 fwrite(f->afm, 1, f->afmlen, fi);
1572 sprintf(namebuf2, "%s.pfb", tmpFileName);
1573 fi = fopen(namebuf2, "wb");
1576 fwrite(f->pfb, 1, f->pfblen, fi);
1579 return strdup(namebuf2);
1582 char* GFXOutputDev::searchFont(const char*name)
1587 msg("<verbose> SearchFont(%s)", name);
1589 /* see if it is a pdf standard font */
1590 for(i=0;i<sizeof(pdf2t1map)/sizeof(fontentry);i++)
1592 if(!strcmp(name, pdf2t1map[i].pdffont))
1594 if(!pdf2t1map[i].fullfilename) {
1595 pdf2t1map[i].fullfilename = writeOutStdFont(&pdf2t1map[i]);
1596 if(!pdf2t1map[i].fullfilename) {
1597 msg("<error> Couldn't save default font- is the Temp Directory writable?");
1599 msg("<verbose> Storing standard PDF font %s at %s", name, pdf2t1map[i].fullfilename);
1602 return strdup(pdf2t1map[i].fullfilename);
1605 /* else look in all font files */
1606 for(i=0;i<fontnum;i++)
1608 if(strstr(fonts[i].filename, name)) {
1609 return strdup(fonts[i].filename);
1615 void GFXOutputDev::updateLineWidth(GfxState *state)
1617 double width = state->getTransformedLineWidth();
1618 //swfoutput_setlinewidth(&device, width);
1621 void GFXOutputDev::updateLineCap(GfxState *state)
1623 int c = state->getLineCap();
1626 void GFXOutputDev::updateLineJoin(GfxState *state)
1628 int j = state->getLineJoin();
1631 void GFXOutputDev::updateFillColor(GfxState *state)
1634 double opaq = state->getFillOpacity();
1635 state->getFillRGB(&rgb);
1637 void GFXOutputDev::updateFillOpacity(GfxState *state)
1640 double opaq = state->getFillOpacity();
1641 state->getFillRGB(&rgb);
1642 dbg("update fillopaq %f", opaq);
1644 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1646 double opaq = state->getFillOpacity();
1647 dbg("update strokeopaq %f", opaq);
1649 void GFXOutputDev::updateFillOverprint(GfxState *state)
1651 double opaq = state->getFillOverprint();
1652 dbg("update filloverprint %f", opaq);
1654 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1656 double opaq = state->getStrokeOverprint();
1657 dbg("update strokeoverprint %f", opaq);
1659 void GFXOutputDev::updateTransfer(GfxState *state)
1661 dbg("update transfer");
1665 void GFXOutputDev::updateStrokeColor(GfxState *state)
1668 double opaq = state->getStrokeOpacity();
1669 state->getStrokeRGB(&rgb);
1672 void FoFiWrite(void *stream, char *data, int len)
1674 int ret = fwrite(data, len, 1, (FILE*)stream);
1677 char*GFXOutputDev::writeEmbeddedFontToFile(XRef*ref, GfxFont*font)
1679 char*tmpFileName = NULL;
1685 Object refObj, strObj;
1687 tmpFileName = mktmpname(namebuf);
1690 ret = font->getEmbeddedFontID(&embRef);
1692 msg("<verbose> Didn't get embedded font id");
1693 /* not embedded- the caller should now search the font
1694 directories for this font */
1698 f = fopen(tmpFileName, "wb");
1700 msg("<error> Couldn't create temporary Type 1 font file");
1704 /*if(font->isCIDFont()) {
1705 GfxCIDFont* cidFont = (GfxCIDFont *)font;
1706 GString c = cidFont->getCollection();
1707 msg("<notice> Collection: %s", c.getCString());
1710 //if (font->getType() == fontType1C) {
1711 if (0) { //font->getType() == fontType1C) {
1712 if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
1714 msg("<error> Couldn't read embedded font file");
1717 FoFiType1C *cvt = FoFiType1C::make(fontBuf, fontLen);
1719 cvt->convertToType1(0, NULL, gTrue, FoFiWrite, f);
1720 //cvt->convertToCIDType0("test", f);
1721 //cvt->convertToType0("test", f);
1724 } else if(font->getType() == fontTrueType) {
1725 msg("<verbose> writing font using TrueTypeFontFile::writeTTF");
1726 if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
1728 msg("<error> Couldn't read embedded font file");
1731 FoFiTrueType *cvt = FoFiTrueType::make(fontBuf, fontLen);
1732 cvt->writeTTF(FoFiWrite, f);
1736 font->getEmbeddedFontID(&embRef);
1737 refObj.initRef(embRef.num, embRef.gen);
1738 refObj.fetch(ref, &strObj);
1740 strObj.streamReset();
1745 f4[t] = strObj.streamGetChar();
1746 f4c[t] = (char)f4[t];
1751 if(!strncmp(f4c, "true", 4)) {
1752 /* some weird TTF fonts don't start with 0,1,0,0 but with "true".
1753 Change this on the fly */
1754 f4[0] = f4[2] = f4[3] = 0;
1762 while ((c = strObj.streamGetChar()) != EOF) {
1766 strObj.streamClose();
1771 return strdup(tmpFileName);
1774 char* GFXOutputDev::searchForSuitableFont(GfxFont*gfxFont)
1776 char*name = getFontName(gfxFont);
1780 if(!this->config_use_fontconfig)
1783 #ifdef HAVE_FONTCONFIG
1784 FcPattern *pattern, *match;
1788 static int fcinitcalled = false;
1790 msg("<debug> searchForSuitableFont(%s)", name);
1792 // call init ony once
1793 if (!fcinitcalled) {
1794 msg("<debug> Initializing FontConfig...");
1795 fcinitcalled = true;
1797 msg("<debug> FontConfig Initialization failed. Disabling.");
1798 config_use_fontconfig = 0;
1801 msg("<debug> ...initialized FontConfig");
1804 msg("<debug> FontConfig: Create \"%s\" Family Pattern", name);
1805 pattern = FcPatternBuild(NULL, FC_FAMILY, FcTypeString, name, NULL);
1806 if (gfxFont->isItalic()) // check for italic
1807 msg("<debug> FontConfig: Adding Italic Slant");
1808 FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
1809 if (gfxFont->isBold()) // check for bold
1810 msg("<debug> FontConfig: Adding Bold Weight");
1811 FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
1813 msg("<debug> FontConfig: Try to match...");
1814 // configure and match using the original font name
1815 FcConfigSubstitute(0, pattern, FcMatchPattern);
1816 FcDefaultSubstitute(pattern);
1817 match = FcFontMatch(0, pattern, &result);
1819 if (FcPatternGetString(match, "family", 0, &v) == FcResultMatch) {
1820 msg("<debug> FontConfig: family=%s", (char*)v);
1821 // if we get an exact match
1822 if (strcmp((char *)v, name) == 0) {
1823 if (FcPatternGetString(match, "file", 0, &v) == FcResultMatch) {
1824 filename = strdup((char*)v); // mem leak
1825 char *nfn = strrchr(filename, '/');
1826 if(nfn) fontname = strdup(nfn+1);
1827 else fontname = filename;
1829 msg("<debug> FontConfig: Returning \"%s\"", fontname);
1831 // initialize patterns
1832 FcPatternDestroy(pattern);
1833 FcPatternDestroy(match);
1835 // now match against serif etc.
1836 if (gfxFont->isSerif()) {
1837 msg("<debug> FontConfig: Create Serif Family Pattern");
1838 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "serif", NULL);
1839 } else if (gfxFont->isFixedWidth()) {
1840 msg("<debug> FontConfig: Create Monospace Family Pattern");
1841 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "monospace", NULL);
1843 msg("<debug> FontConfig: Create Sans Family Pattern");
1844 pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "sans", NULL);
1848 if (gfxFont->isItalic()) {
1849 msg("<debug> FontConfig: Adding Italic Slant");
1850 int bb = FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
1853 if (gfxFont->isBold()) {
1854 msg("<debug> FontConfig: Adding Bold Weight");
1855 int bb = FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
1858 msg("<debug> FontConfig: Try to match... (2)");
1859 // configure and match using serif etc
1860 FcConfigSubstitute (0, pattern, FcMatchPattern);
1861 FcDefaultSubstitute (pattern);
1862 match = FcFontMatch (0, pattern, &result);
1864 if (FcPatternGetString(match, "file", 0, &v) == FcResultMatch) {
1865 filename = strdup((char*)v); // mem leak
1866 char *nfn = strrchr(filename, '/');
1867 if(nfn) fontname = strdup(nfn+1);
1868 else fontname = filename;
1870 msg("<debug> FontConfig: Returning \"%s\"", fontname);
1874 //printf("FONTCONFIG: pattern");
1875 //FcPatternPrint(pattern);
1876 //printf("FONTCONFIG: match");
1877 //FcPatternPrint(match);
1879 FcPatternDestroy(pattern);
1880 FcPatternDestroy(match);
1882 pdfswf_addfont(filename);
1889 char* GFXOutputDev::substituteFont(GfxFont*gfxFont, char* oldname)
1891 const char*fontname = 0, *filename = 0;
1892 msg("<notice> substituteFont(%s)", oldname);
1894 if(!(fontname = searchForSuitableFont(gfxFont))) {
1895 fontname = "Times-Roman";
1897 filename = searchFont(fontname);
1899 msg("<error> Couldn't find font %s- did you install the default fonts?", fontname);
1903 if(substitutepos>=sizeof(substitutesource)/sizeof(char*)) {
1904 msg("<fatal> Too many fonts in file.");
1908 substitutesource[substitutepos] = strdup(oldname); //mem leak
1909 substitutetarget[substitutepos] = fontname;
1910 msg("<notice> substituting %s -> %s", FIXNULL(oldname), FIXNULL(fontname));
1913 return strdup(filename); //mem leak
1916 void unlinkfont(char* filename)
1923 if(!strncmp(&filename[l-4],".afm",4)) {
1924 memcpy(&filename[l-4],".pfb",4);
1926 memcpy(&filename[l-4],".pfa",4);
1928 memcpy(&filename[l-4],".afm",4);
1931 if(!strncmp(&filename[l-4],".pfa",4)) {
1932 memcpy(&filename[l-4],".afm",4);
1934 memcpy(&filename[l-4],".pfa",4);
1937 if(!strncmp(&filename[l-4],".pfb",4)) {
1938 memcpy(&filename[l-4],".afm",4);
1940 memcpy(&filename[l-4],".pfb",4);
1945 void GFXOutputDev::setXRef(PDFDoc*doc, XRef *xref)
1951 int GFXOutputDev::setGfxFont(char*id, char*name, char*filename, double maxSize, CharCodeToUnicode*ctu)
1954 fontlist_t*last=0,*l = this->fontlist;
1957 msg("<error> Internal Error: FontID is null");
1959 /* TODO: should this be part of the state? */
1962 if(!strcmp(l->font->id, id)) {
1963 current_gfxfont = l->font;
1965 device->addfont(device, current_gfxfont);
1970 if(!filename) return 0;
1972 /* A font size of e.g. 9 means the font will be scaled down by
1973 1024 and scaled up by 9. So to have a maximum error of 1/20px,
1974 we have to divide 0.05 by (fontsize/1024)
1976 double quality = (1024 * 0.05) / maxSize;
1978 msg("<verbose> Loading %s...", filename);
1979 font = gfxfont_load(id, filename, 0, quality);
1981 msg("<verbose> Couldn't load Font %s (%s)", filename, id);
1984 msg("<verbose> Font %s (%s) loaded successfully", filename, id);
1986 if(this->config_remapunicode && ctu) {
1988 for(c=0;c<font->num_glyphs;c++) {
1990 int uLen = ctu->mapToUnicode(c, u, 8);
1991 if(uLen && !isValidUnicode(font->glyphs[c].unicode) && isValidUnicode(u[0]))
1992 font->glyphs[c].unicode = u[0];
1998 l->filename = strdup(filename);
2000 current_gfxfont = l->font;
2006 device->addfont(device, current_gfxfont);
2010 void GFXOutputDev::updateFont(GfxState *state)
2012 GfxFont*gfxFont = state->getFont();
2018 char * fontid = getFontID(gfxFont);
2019 char * fontname = getFontName(gfxFont);
2021 double maxSize = 1.0;
2024 maxSize = this->info->getMaximumFontSize(fontid);
2028 /* first, look if we substituted this font before-
2029 this way, we don't initialize the T1 Fonts
2031 for(t=0;t<substitutepos;t++) {
2032 if(!strcmp(fontid, substitutesource[t])) {
2033 free(fontid);fontid=0;
2034 fontid = strdup(substitutetarget[t]);
2039 /* second, see if this is a font which was used before-
2040 if so, we are done */
2041 if(setGfxFont(fontid, fontname, 0, 0, gfxFont->getCTU())) {
2046 /* if(swfoutput_queryfont(&device, fontid))
2047 swfoutput_setfont(&device, fontid, 0);
2049 msg("<debug> updateFont(%s) [cached]", fontid);
2053 // look for Type 3 font
2054 if (gfxFont->getType() == fontType3) {
2056 type3Warning = gTrue;
2057 showFontError(gfxFont, 2);
2064 /* now either load the font, or find a substitution */
2067 GBool embedded = gfxFont->getEmbeddedFontID(&embRef);
2072 (gfxFont->getType() == fontType1 ||
2073 gfxFont->getType() == fontType1C ||
2074 gfxFont->getType() == fontCIDType0C ||
2075 gfxFont->getType() == fontTrueType ||
2076 gfxFont->getType() == fontCIDType2
2079 fileName = writeEmbeddedFontToFile(xref, gfxFont);
2080 if(!fileName) showFontError(gfxFont,0);
2083 fileName = searchFont(fontname);
2084 if(!fileName) showFontError(gfxFont,0);
2087 char * fontname = getFontName(gfxFont);
2088 msg("<warning> Font %s %scould not be loaded.", fontname, embedded?"":"(not embedded) ");
2091 msg("<warning> Try putting a TTF version of that font (named \"%s.ttf\") into %s", fontname, lastfontdir);
2093 msg("<warning> Try specifying one or more font directories");
2095 fileName = substituteFont(gfxFont, fontid);
2098 if(fontid) { free(fontid);fontid = strdup(substitutetarget[substitutepos-1]); /*ugly hack*/};
2099 msg("<notice> Font is now %s (%s)", fontid, fileName);
2103 msg("<error> Couldn't set font %s\n", fontid);
2109 msg("<verbose> updateFont(%s) -> %s (max size: %f)", fontid, fileName, maxSize);
2110 dumpFontInfo("<verbose>", gfxFont);
2112 //swfoutput_setfont(&device, fontid, fileName);
2114 if(!setGfxFont(fontid, fontname, 0, 0, gfxFont->getCTU())) {
2115 setGfxFont(fontid, fontname, fileName, maxSize, gfxFont->getCTU());
2119 unlinkfont(fileName);
2129 #define SQR(x) ((x)*(x))
2131 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
2133 if((newwidth<2 || newheight<2) ||
2134 (width<=newwidth || height<=newheight))
2136 unsigned char*newdata;
2138 newdata= (unsigned char*)malloc(newwidth*newheight);
2140 double fx = (double)(width)/newwidth;
2141 double fy = (double)(height)/newheight;
2143 int blocksize = (int)(8192/(fx*fy));
2144 int r = 8192*256/palettesize;
2145 for(x=0;x<newwidth;x++) {
2146 double ex = px + fx;
2147 int fromx = (int)px;
2149 int xweight1 = (int)(((fromx+1)-px)*256);
2150 int xweight2 = (int)((ex-tox)*256);
2152 for(y=0;y<newheight;y++) {
2153 double ey = py + fy;
2154 int fromy = (int)py;
2156 int yweight1 = (int)(((fromy+1)-py)*256);
2157 int yweight2 = (int)((ey-toy)*256);
2160 for(xx=fromx;xx<=tox;xx++)
2161 for(yy=fromy;yy<=toy;yy++) {
2162 int b = 1-data[width*yy+xx];
2164 if(xx==fromx) weight = (weight*xweight1)/256;
2165 if(xx==tox) weight = (weight*xweight2)/256;
2166 if(yy==fromy) weight = (weight*yweight1)/256;
2167 if(yy==toy) weight = (weight*yweight2)/256;
2170 //if(a) a=(palettesize-1)*r/blocksize;
2171 newdata[y*newwidth+x] = (a*blocksize)/r;
2179 #define IMAGE_TYPE_JPEG 0
2180 #define IMAGE_TYPE_LOSSLESS 1
2182 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
2183 double x1,double y1,
2184 double x2,double y2,
2185 double x3,double y3,
2186 double x4,double y4, int type)
2188 gfxcolor_t*newpic=0;
2190 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
2191 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
2193 gfxline_t p1,p2,p3,p4,p5;
2194 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
2195 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
2196 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
2197 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
2198 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
2200 {p1.x = (int)(p1.x*20)/20.0;
2201 p1.y = (int)(p1.y*20)/20.0;
2202 p2.x = (int)(p2.x*20)/20.0;
2203 p2.y = (int)(p2.y*20)/20.0;
2204 p3.x = (int)(p3.x*20)/20.0;
2205 p3.y = (int)(p3.y*20)/20.0;
2206 p4.x = (int)(p4.x*20)/20.0;
2207 p4.y = (int)(p4.y*20)/20.0;
2208 p5.x = (int)(p5.x*20)/20.0;
2209 p5.y = (int)(p5.y*20)/20.0;
2216 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2217 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2222 img.data = (gfxcolor_t*)data;
2226 if(type == IMAGE_TYPE_JPEG)
2227 /* TODO: pass image_dpi to device instead */
2228 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2230 dev->fillbitmap(dev, &p1, &img, &m, 0);
2233 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2234 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
2236 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG);
2239 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2240 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
2242 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS);
2246 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2247 int width, int height, GfxImageColorMap*colorMap, GBool invert,
2248 GBool inlineImg, int mask, int*maskColors,
2249 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2251 double x1,y1,x2,y2,x3,y3,x4,y4;
2252 ImageStream *imgStr;
2257 unsigned char* maskbitmap = 0;
2260 ncomps = colorMap->getNumPixelComps();
2261 bits = colorMap->getBits();
2266 unsigned char buf[8];
2267 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2269 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2270 imgMaskStr->reset();
2271 unsigned char pal[256];
2272 int n = 1 << colorMap->getBits();
2277 maskColorMap->getGray(pixBuf, &gray);
2278 pal[t] = colToByte(gray);
2280 for (y = 0; y < maskHeight; y++) {
2281 for (x = 0; x < maskWidth; x++) {
2282 imgMaskStr->getPixel(buf);
2283 maskbitmap[y*maskWidth+x] = pal[buf[0]];
2288 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2289 imgMaskStr->reset();
2290 for (y = 0; y < maskHeight; y++) {
2291 for (x = 0; x < maskWidth; x++) {
2292 imgMaskStr->getPixel(buf);
2294 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2302 imgStr = new ImageStream(str, width, ncomps,bits);
2305 if(!width || !height || (height<=1 && width<=1))
2307 msg("<verbose> Ignoring %d by %d image", width, height);
2308 unsigned char buf[8];
2310 for (y = 0; y < height; ++y)
2311 for (x = 0; x < width; ++x) {
2312 imgStr->getPixel(buf);
2320 state->transform(0, 1, &x1, &y1); x1 += user_movex + clipmovex; y1 += user_movey + clipmovey;
2321 state->transform(0, 0, &x2, &y2); x2 += user_movex + clipmovex; y2 += user_movey + clipmovey;
2322 state->transform(1, 0, &x3, &y3); x3 += user_movex + clipmovex; y3 += user_movey + clipmovey;
2323 state->transform(1, 1, &x4, &y4); x4 += user_movex + clipmovex; y4 += user_movey + clipmovey;
2325 if(!pbminfo && !(str->getKind()==strDCT)) {
2327 msg("<notice> file contains pbm pictures %s",mask?"(masked)":"");
2331 msg("<verbose> drawing %d by %d masked picture\n", width, height);
2333 if(!jpeginfo && (str->getKind()==strDCT)) {
2334 msg("<notice> file contains jpeg pictures");
2340 unsigned char buf[8];
2342 unsigned char*pic = new unsigned char[width*height];
2343 gfxcolor_t pal[256];
2345 state->getFillRGB(&rgb);
2347 memset(pal,255,sizeof(pal));
2348 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2349 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2350 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2351 pal[0].a = 255; pal[1].a = 0;
2354 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2355 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2356 for (y = 0; y < height; ++y)
2357 for (x = 0; x < width; ++x)
2359 imgStr->getPixel(buf);
2362 pic[width*y+x] = buf[0];
2365 /* the size of the drawn image is added to the identifier
2366 as the same image may require different bitmaps if displayed
2367 at different sizes (due to antialiasing): */
2370 unsigned char*pic2 = 0;
2373 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2382 height = realheight;
2386 /* make a black/white palette */
2388 float r = 255/(numpalette-1);
2390 for(t=0;t<numpalette;t++) {
2391 pal[t].r = colToByte(rgb.r);
2392 pal[t].g = colToByte(rgb.g);
2393 pal[t].b = colToByte(rgb.b);
2394 pal[t].a = (unsigned char)(t*r);
2398 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2399 for (y = 0; y < height; ++y) {
2400 for (x = 0; x < width; ++x) {
2401 pic2[width*y+x] = pal[pic[y*width+x]];
2404 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2408 if(maskbitmap) free(maskbitmap);
2414 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2415 gfxcolor_t*pic=new gfxcolor_t[width*height];
2416 for (y = 0; y < height; ++y) {
2417 for (x = 0; x < width; ++x) {
2418 imgStr->getPixel(pixBuf);
2419 colorMap->getRGB(pixBuf, &rgb);
2420 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2421 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2422 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2423 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2425 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2429 if(str->getKind()==strDCT)
2430 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2432 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2435 if(maskbitmap) free(maskbitmap);
2438 gfxcolor_t*pic=new gfxcolor_t[width*height];
2439 gfxcolor_t pal[256];
2440 int n = 1 << colorMap->getBits();
2442 for(t=0;t<256;t++) {
2444 colorMap->getRGB(pixBuf, &rgb);
2446 {/*if(maskColors && *maskColors==t) {
2447 msg("<notice> Color %d is transparent", t);
2448 if (imgData->maskColors) {
2450 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2451 if (pix[i] < imgData->maskColors[2*i] ||
2452 pix[i] > imgData->maskColors[2*i+1]) {
2467 pal[t].r = (unsigned char)(colToByte(rgb.r));
2468 pal[t].g = (unsigned char)(colToByte(rgb.g));
2469 pal[t].b = (unsigned char)(colToByte(rgb.b));
2470 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2473 for (y = 0; y < height; ++y) {
2474 for (x = 0; x < width; ++x) {
2475 imgStr->getPixel(pixBuf);
2476 pic[width*y+x] = pal[pixBuf[0]];
2478 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2482 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2486 if(maskbitmap) free(maskbitmap);
2491 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2492 int width, int height, GBool invert,
2495 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2496 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2497 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2500 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2501 int width, int height, GfxImageColorMap *colorMap,
2502 int *maskColors, GBool inlineImg)
2504 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
2505 colorMap?"colorMap":"no colorMap",
2506 maskColors?"maskColors":"no maskColors",
2508 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
2509 colorMap?"colorMap":"no colorMap",
2510 maskColors?"maskColors":"no maskColors",
2513 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2514 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2515 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2518 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2519 int width, int height,
2520 GfxImageColorMap *colorMap,
2521 Stream *maskStr, int maskWidth, int maskHeight,
2524 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2525 colorMap?"colorMap":"no colorMap",
2526 maskWidth, maskHeight);
2527 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2528 colorMap?"colorMap":"no colorMap",
2529 maskWidth, maskHeight);
2531 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2532 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2533 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2536 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2537 int width, int height,
2538 GfxImageColorMap *colorMap,
2540 int maskWidth, int maskHeight,
2541 GfxImageColorMap *maskColorMap)
2543 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2544 colorMap?"colorMap":"no colorMap",
2545 maskWidth, maskHeight);
2546 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2547 colorMap?"colorMap":"no colorMap",
2548 maskWidth, maskHeight);
2550 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2551 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2552 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2555 void GFXOutputDev::stroke(GfxState *state)
2559 GfxPath * path = state->getPath();
2560 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2561 strokeGfxline(state, line, 0);
2565 void GFXOutputDev::fill(GfxState *state)
2567 gfxcolor_t col = getFillColor(state);
2568 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2570 GfxPath * path = state->getPath();
2571 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2572 fillGfxLine(state, line);
2576 void GFXOutputDev::eoFill(GfxState *state)
2578 gfxcolor_t col = getFillColor(state);
2579 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2581 GfxPath * path = state->getPath();
2582 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2583 fillGfxLine(state, line);
2588 static const char* dirseparator()
2597 void addGlobalFont(const char*filename)
2600 memset(&f, 0, sizeof(fontfile_t));
2601 f.filename = filename;
2602 if(fontnum < sizeof(fonts)/sizeof(fonts[0])) {
2603 msg("<verbose> Adding font \"%s\".", filename);
2604 fonts[fontnum++] = f;
2606 msg("<error> Too many external fonts. Not adding font file \"%s\".", filename);
2610 void addGlobalLanguageDir(const char*dir)
2613 globalParams = new GlobalParams((char*)"");
2615 msg("<notice> Adding %s to language pack directories", dir);
2619 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2620 strcpy(config_file, dir);
2621 strcat(config_file, dirseparator());
2622 strcat(config_file, "add-to-xpdfrc");
2624 fi = fopen(config_file, "rb");
2626 msg("<error> Could not open %s", config_file);
2629 globalParams->parseFile(new GString(config_file), fi);
2633 void addGlobalFontDir(const char*dirname)
2635 #ifdef HAVE_DIRENT_H
2636 msg("<notice> Adding %s to font directories", dirname);
2637 lastfontdir = strdup(dirname);
2638 DIR*dir = opendir(dirname);
2640 msg("<warning> Couldn't open directory %s\n", dirname);
2645 ent = readdir (dir);
2649 char*name = ent->d_name;
2655 if(!strncasecmp(&name[l-4], ".pfa", 4))
2657 if(!strncasecmp(&name[l-4], ".pfb", 4))
2659 if(!strncasecmp(&name[l-4], ".ttf", 4))
2663 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2664 strcpy(fontname, dirname);
2665 strcat(fontname, dirseparator());
2666 strcat(fontname, name);
2667 addGlobalFont(fontname);
2672 msg("<warning> No dirent.h- unable to add font dir %s", dir);
2676 void GFXOutputDev::preparePage(int pdfpage, int outputpage)
2682 this->pagebuflen = 1024;
2683 this->pages = (int*)malloc(this->pagebuflen*sizeof(int));
2684 memset(this->pages, -1, this->pagebuflen*sizeof(int));
2686 while(pdfpage >= this->pagebuflen)
2688 int oldlen = this->pagebuflen;
2689 this->pagebuflen+=1024;
2690 this->pages = (int*)realloc(this->pages, this->pagebuflen*sizeof(int));
2691 memset(&this->pages[oldlen], -1, (this->pagebuflen-oldlen)*sizeof(int));
2694 this->pages[pdfpage] = outputpage;
2695 if(pdfpage>this->pagepos)
2696 this->pagepos = pdfpage;
2702 double width,height;
2705 BBox mkBBox(GfxState*state, double*bbox, double width, double height)
2707 double xMin, yMin, xMax, yMax, x, y;
2708 double tx, ty, w, h;
2709 // transform the bbox
2710 state->transform(bbox[0], bbox[1], &x, &y);
2713 state->transform(bbox[0], bbox[3], &x, &y);
2716 } else if (x > xMax) {
2721 } else if (y > yMax) {
2724 state->transform(bbox[2], bbox[1], &x, &y);
2727 } else if (x > xMax) {
2732 } else if (y > yMax) {
2735 state->transform(bbox[2], bbox[3], &x, &y);
2738 } else if (x > xMax) {
2743 } else if (y > yMax) {
2746 tx = (int)floor(xMin);
2749 } else if (tx > width) {
2752 ty = (int)floor(yMin);
2755 } else if (ty > height) {
2758 w = (int)ceil(xMax) - tx + 1;
2759 if (tx + w > width) {
2765 h = (int)ceil(yMax) - ty + 1;
2766 if (ty + h > height) {
2780 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2781 GfxColorSpace *blendingColorSpace,
2782 GBool isolated, GBool knockout,
2785 const char*colormodename = "";
2786 BBox rect = mkBBox(state, bbox, this->width, this->height);
2788 if(blendingColorSpace) {
2789 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2791 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);
2792 dbg("using clipping rect %f/%f/%f/%f\n", rect.posx,rect.posy,rect.width,rect.height);
2793 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);
2795 states[statepos].createsoftmask |= forSoftMask;
2796 states[statepos].transparencygroup = !forSoftMask;
2797 states[statepos].isolated = isolated;
2799 states[statepos].olddevice = this->device;
2800 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2802 gfxdevice_record_init(this->device);
2804 /*if(!forSoftMask) { ////???
2805 state->setFillOpacity(0.0);
2810 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2813 dbg("endTransparencyGroup");
2814 msg("<verbose> endTransparencyGroup");
2816 gfxdevice_t*r = this->device;
2818 this->device = states[statepos].olddevice;
2820 if(states[statepos].createsoftmask) {
2821 states[statepos-1].softmaskrecording = r->finish(r);
2823 states[statepos-1].grouprecording = r->finish(r);
2826 states[statepos].createsoftmask = 0;
2827 states[statepos].transparencygroup = 0;
2831 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2833 const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2834 "colordodge","colorburn","hardlight","softlight","difference",
2835 "exclusion","hue","saturation","color","luminosity"};
2837 dbg("paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2838 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2840 if(state->getBlendMode() == gfxBlendNormal)
2841 infofeature("transparency groups");
2844 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2845 warnfeature(buffer, 0);
2848 gfxresult_t*grouprecording = states[statepos].grouprecording;
2850 if(state->getBlendMode() == gfxBlendNormal) {
2852 gfxdevice_ops_init(&ops, this->device, (unsigned char)(state->getFillOpacity()*255));
2853 gfxresult_record_replay(grouprecording, &ops);
2856 grouprecording->destroy(grouprecording);
2858 states[statepos].grouprecording = 0;
2861 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2863 /* alpha = 1: retrieve mask values from alpha layer
2864 alpha = 0: retrieve mask values from luminance */
2865 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2866 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2867 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2868 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2870 infofeature("soft masks");
2872 warnfeature("soft masks from alpha channel",0);
2874 states[statepos].olddevice = this->device;
2875 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2876 gfxdevice_record_init(this->device);
2878 dbg("softmaskrecording is %08x at statepos %d\n", states[statepos].softmaskrecording, statepos);
2880 states[statepos].softmask = 1;
2881 states[statepos].softmask_alpha = alpha;
2884 static inline Guchar div255(int x) {
2885 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2888 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2890 if(c < min) c = min;
2891 if(c > max) c = max;
2895 void GFXOutputDev::clearSoftMask(GfxState *state)
2897 if(!states[statepos].softmask)
2899 states[statepos].softmask = 0;
2900 dbg("clearSoftMask statepos=%d", statepos);
2901 msg("<verbose> clearSoftMask");
2903 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2904 msg("<error> Error in softmask/tgroup ordering");
2908 gfxresult_t*mask = states[statepos].softmaskrecording;
2909 gfxresult_t*below = this->device->finish(this->device);
2910 this->device = states[statepos].olddevice;
2912 /* get outline of all objects below the soft mask */
2913 gfxdevice_t uniondev;
2914 gfxdevice_union_init(&uniondev, 0);
2915 gfxresult_record_replay(below, &uniondev);
2916 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2917 uniondev.finish(&uniondev);
2919 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2921 this->device->startclip(this->device, belowoutline);
2922 gfxresult_record_replay(below, this->device);
2923 gfxresult_record_replay(mask, this->device);
2924 this->device->endclip(this->device);
2925 gfxline_free(belowoutline);
2928 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2929 if(width<=0 || height<=0)
2932 gfxdevice_t belowrender;
2933 gfxdevice_render_init(&belowrender);
2934 if(states[statepos+1].isolated) {
2935 belowrender.setparameter(&belowrender, "fillwhite", "1");
2937 belowrender.setparameter(&belowrender, "antialize", "2");
2938 belowrender.startpage(&belowrender, width, height);
2939 gfxresult_record_replay(below, &belowrender);
2940 belowrender.endpage(&belowrender);
2941 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2942 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2943 //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2945 gfxdevice_t maskrender;
2946 gfxdevice_render_init(&maskrender);
2947 maskrender.startpage(&maskrender, width, height);
2948 gfxresult_record_replay(mask, &maskrender);
2949 maskrender.endpage(&maskrender);
2950 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2951 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2953 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2954 msg("<fatal> Internal error in mask drawing");
2959 for(y=0;y<height;y++) {
2960 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2961 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2962 for(x=0;x<width;x++) {
2964 if(states[statepos].softmask_alpha) {
2967 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2970 l2->a = div255(alpha*l2->a);
2972 /* DON'T premultiply alpha- this is done by fillbitmap,
2973 depending on the output device */
2974 //l2->r = div255(alpha*l2->r);
2975 //l2->g = div255(alpha*l2->g);
2976 //l2->b = div255(alpha*l2->b);
2982 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2985 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2986 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2988 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2990 mask->destroy(mask);
2991 below->destroy(below);
2992 maskresult->destroy(maskresult);
2993 belowresult->destroy(belowresult);
2994 states[statepos].softmaskrecording = 0;
3001 delete globalParams;globalParams=0;
3002 Object::memCheck(stderr);