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 char* writeOutStdFont(fontentry* f)
250 char* tmpFileName = mktmpname(namebuf1);
252 sprintf(namebuf2, "%s.afm", tmpFileName);
253 fi = fopen(namebuf2, "wb");
256 fwrite(f->afm, 1, f->afmlen, fi);
259 sprintf(namebuf2, "%s.pfb", tmpFileName);
260 fi = fopen(namebuf2, "wb");
263 fwrite(f->pfb, 1, f->pfblen, fi);
265 return strdup(namebuf2);
267 void unlinkfont(char* filename)
272 msg("<verbose> Removing temporary font file %s", filename);
275 if(!strncmp(&filename[l-4],".afm",4)) {
276 memcpy(&filename[l-4],".pfb",4); unlink(filename);
277 memcpy(&filename[l-4],".pfa",4); unlink(filename);
278 memcpy(&filename[l-4],".afm",4);
281 if(!strncmp(&filename[l-4],".pfa",4)) {
282 memcpy(&filename[l-4],".afm",4); unlink(filename);
283 memcpy(&filename[l-4],".pfa",4);
286 if(!strncmp(&filename[l-4],".pfb",4)) {
287 memcpy(&filename[l-4],".afm",4); unlink(filename);
288 memcpy(&filename[l-4],".pfb",4);
294 GFXGlobalParams::GFXGlobalParams()
297 //setupBaseFonts(char *dir); //not tested yet
299 GFXGlobalParams::~GFXGlobalParams()
301 msg("<verbose> Performing cleanups");
303 for(t=0;t<sizeof(pdf2t1map)/sizeof(fontentry);t++) {
304 if(pdf2t1map[t].fullfilename) {
305 unlinkfont(pdf2t1map[t].fullfilename);
309 DisplayFontParam *GFXGlobalParams::getDisplayFont(GString *fontName)
311 msg("<verbose> looking for font %s in global params\n", fontName->getCString());
313 char*name = fontName->getCString();
314 /* see if it is a pdf standard font */
316 for(t=0;t<sizeof(pdf2t1map)/sizeof(fontentry);t++) {
317 if(!strcmp(name, pdf2t1map[t].pdffont)) {
318 if(!pdf2t1map[t].fullfilename) {
319 pdf2t1map[t].fullfilename = writeOutStdFont(&pdf2t1map[t]);
320 if(!pdf2t1map[t].fullfilename) {
321 msg("<error> Couldn't save default font- is the Temp Directory writable?");
323 msg("<verbose> Storing standard PDF font %s at %s", name, pdf2t1map[t].fullfilename);
326 DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), displayFontT1);
327 dfp->t1.fileName = new GString(pdf2t1map[t].fullfilename);
331 return GlobalParams::getDisplayFont(fontName);
334 GFXOutputDev::GFXOutputDev(parameter_t*p)
337 this->textmodeinfo = 0;
340 this->type3active = 0;
343 this->substitutepos = 0;
344 this->type3Warning = 0;
345 this->user_movex = 0;
346 this->user_movey = 0;
349 this->user_clipx1 = 0;
350 this->user_clipy1 = 0;
351 this->user_clipx2 = 0;
352 this->user_clipy2 = 0;
353 this->current_text_stroke = 0;
354 this->current_text_clip = 0;
355 this->outer_clip_box = 0;
357 this->pagebuflen = 0;
359 this->config_use_fontconfig=1;
360 this->config_break_on_warning=0;
361 this->config_remapunicode=0;
362 this->config_transparent=0;
363 this->do_interpretType3Chars = gTrue;
365 this->parameters = p;
367 this->gfxfontlist = gfxfontlist_create();
369 memset(states, 0, sizeof(states));
371 /* configure device */
373 setParameter(p->name, p->value);
378 void GFXOutputDev::setParameter(const char*key, const char*value)
380 if(!strcmp(key,"rawtext")) {
381 this->do_interpretType3Chars = atoi(value)^1;
382 } else if(!strcmp(key,"breakonwarning")) {
383 this->config_break_on_warning = atoi(value);
384 } else if(!strcmp(key,"fontconfig")) {
385 this->config_use_fontconfig = atoi(value);
386 } else if(!strcmp(key,"remapunicode")) {
387 this->config_remapunicode = atoi(value);
388 } else if(!strcmp(key,"transparent")) {
389 this->config_transparent = atoi(value);
393 void GFXOutputDev::setDevice(gfxdevice_t*dev)
395 parameter_t*p = this->parameters;
397 /* pass parameters to output device */
401 this->device->setparameter(this->device, p->name, p->value);
407 void GFXOutputDev::setMove(int x,int y)
409 this->user_movex = x;
410 this->user_movey = y;
413 void GFXOutputDev::setClip(int x1,int y1,int x2,int y2)
415 if(x2<x1) {int x3=x1;x1=x2;x2=x3;}
416 if(y2<y1) {int y3=y1;y1=y2;y2=y3;}
418 this->user_clipx1 = x1;
419 this->user_clipy1 = y1;
420 this->user_clipx2 = x2;
421 this->user_clipy2 = y2;
424 static char*getFontName(GfxFont*font)
427 GString*gstr = font->getName();
428 char* fname = gstr==0?0:gstr->getCString();
432 sprintf(buf, "UFONT%d", r->num);
433 fontid = strdup(buf);
435 fontid = strdup(fname);
439 char* plus = strchr(fontid, '+');
440 if(plus && plus < &fontid[strlen(fontid)-1]) {
441 fontname = strdup(plus+1);
443 fontname = strdup(fontid);
449 static void dumpFontInfo(const char*loglevel, GfxFont*font);
450 static int lastdumps[1024];
451 static int lastdumppos = 0;
456 static void showFontError(GfxFont*font, int nr)
460 for(t=0;t<lastdumppos;t++)
461 if(lastdumps[t] == r->num)
465 if(lastdumppos<sizeof(lastdumps)/sizeof(int))
466 lastdumps[lastdumppos++] = r->num;
468 msg("<warning> The following font caused problems:");
470 msg("<warning> The following font caused problems (substituting):");
472 msg("<warning> The following Type 3 Font will be rendered as graphics:");
473 dumpFontInfo("<warning>", font);
476 static void dumpFontInfo(const char*loglevel, GfxFont*font)
478 char* id = getFontID(font);
479 char* name = getFontName(font);
480 Ref* r=font->getID();
481 msg("%s=========== %s (ID:%d,%d) ==========\n", loglevel, name, r->num,r->gen);
483 GString*gstr = font->getTag();
485 msg("%s| Tag: %s\n", loglevel, id);
487 if(font->isCIDFont()) msg("%s| is CID font\n", loglevel);
489 GfxFontType type=font->getType();
491 case fontUnknownType:
492 msg("%s| Type: unknown\n",loglevel);
495 msg("%s| Type: 1\n",loglevel);
498 msg("%s| Type: 1C\n",loglevel);
501 msg("%s| Type: 3\n",loglevel);
504 msg("%s| Type: TrueType\n",loglevel);
507 msg("%s| Type: CIDType0\n",loglevel);
510 msg("%s| Type: CIDType0C\n",loglevel);
513 msg("%s| Type: CIDType2\n",loglevel);
518 GBool embedded = font->getEmbeddedFontID(&embRef);
520 if(font->getEmbeddedFontName()) {
521 embeddedName = font->getEmbeddedFontName()->getCString();
524 msg("%s| Embedded id: %s id: %d\n",loglevel, FIXNULL(embeddedName), embRef.num);
526 gstr = font->getExtFontFile();
528 msg("%s| External Font file: %s\n", loglevel, FIXNULL(gstr->getCString()));
530 // Get font descriptor flags.
531 if(font->isFixedWidth()) msg("%s| is fixed width\n", loglevel);
532 if(font->isSerif()) msg("%s| is serif\n", loglevel);
533 if(font->isSymbolic()) msg("%s| is symbolic\n", loglevel);
534 if(font->isItalic()) msg("%s| is italic\n", loglevel);
535 if(font->isBold()) msg("%s| is bold\n", loglevel);
541 //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");}
542 //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");}
544 void dump_outline(gfxline_t*line)
547 if(line->type == gfx_moveTo) {
548 msg("<debug> | moveTo %.2f %.2f", line->x,line->y);
549 } else if(line->type == gfx_lineTo) {
550 msg("<debug> | lineTo %.2f %.2f", line->x,line->y);
551 } else if(line->type == gfx_splineTo) {
552 msg("<debug> | splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y);
558 gfxline_t* gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user_movex, int user_movey)
560 int num = path->getNumSubpaths();
563 double lastx=0,lasty=0,posx=0,posy=0;
566 msg("<warning> empty path");
570 gfxdrawer_target_gfxline(&draw);
572 for(t = 0; t < num; t++) {
573 GfxSubpath *subpath = path->getSubpath(t);
574 int subnum = subpath->getNumPoints();
575 double bx=0,by=0,cx=0,cy=0;
577 for(s=0;s<subnum;s++) {
580 state->transform(subpath->getX(s),subpath->getY(s),&x,&y);
585 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
586 draw.lineTo(&draw, lastx, lasty);
588 draw.moveTo(&draw, x,y);
593 } else if(subpath->getCurve(s) && cpos==0) {
597 } else if(subpath->getCurve(s) && cpos==1) {
605 draw.lineTo(&draw, x,y);
607 gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y, 0.05);
614 /* fix non-closed lines */
615 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
616 draw.lineTo(&draw, lastx, lasty);
618 gfxline_t*result = (gfxline_t*)draw.result(&draw);
620 gfxline_optimize(result);
625 GBool GFXOutputDev::useTilingPatternFill()
627 infofeature("tiled patterns");
631 GBool GFXOutputDev::useShadedFills()
633 infofeature("shaded fills");
637 GBool GFXOutputDev::useDrawForm()
639 infofeature("forms");
642 void GFXOutputDev::drawForm(Ref id)
644 msg("<error> drawForm not implemented");
646 GBool GFXOutputDev::needNonText()
650 void GFXOutputDev::endPage()
652 msg("<verbose> endPage");
655 #define STROKE_FILL 1
656 #define STROKE_CLIP 2
657 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line, int flags)
659 int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
660 int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
661 double miterLimit = state->getMiterLimit();
662 double width = state->getTransformedLineWidth();
665 double opaq = state->getStrokeOpacity();
667 state->getFillRGB(&rgb);
669 state->getStrokeRGB(&rgb);
671 col.r = colToByte(rgb.r);
672 col.g = colToByte(rgb.g);
673 col.b = colToByte(rgb.b);
674 col.a = (unsigned char)(opaq*255);
676 gfx_capType capType = gfx_capRound;
677 if(lineCap == 0) capType = gfx_capButt;
678 else if(lineCap == 1) capType = gfx_capRound;
679 else if(lineCap == 2) capType = gfx_capSquare;
681 gfx_joinType joinType = gfx_joinRound;
682 if(lineJoin == 0) joinType = gfx_joinMiter;
683 else if(lineJoin == 1) joinType = gfx_joinRound;
684 else if(lineJoin == 2) joinType = gfx_joinBevel;
687 double dashphase = 0;
689 state->getLineDash(&ldash, &dashnum, &dashphase);
693 if(dashnum && ldash) {
694 float * dash = (float*)malloc(sizeof(float)*(dashnum+1));
696 msg("<trace> %d dashes", dashnum);
697 msg("<trace> | phase: %f", dashphase);
698 for(t=0;t<dashnum;t++) {
700 msg("<trace> | d%-3d: %f", t, ldash[t]);
703 if(getLogLevel() >= LOGLEVEL_TRACE) {
707 line2 = gfxtool_dash_line(line, dash, dashphase);
710 msg("<trace> After dashing:");
713 if(getLogLevel() >= LOGLEVEL_TRACE) {
714 msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x\n",
716 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
717 lineCap==0?"butt": (lineJoin==1?"round":"square"),
719 col.r,col.g,col.b,col.a
724 if(flags&STROKE_FILL) {
725 ArtSVP* svp = gfxstrokeToSVP(line, width, capType, joinType, miterLimit);
726 gfxline_t*gfxline = SVPtogfxline(svp);
727 if(getLogLevel() >= LOGLEVEL_TRACE) {
728 dump_outline(gfxline);
731 msg("<warning> Empty polygon (resulting from stroked line)");
733 if(flags&STROKE_CLIP) {
734 device->startclip(device, gfxline);
735 states[statepos].clipping++;
737 device->fill(device, gfxline, &col);
742 if(flags&STROKE_CLIP)
743 msg("<error> Stroke&clip not supported at the same time");
744 device->stroke(device, line, width, &col, capType, joinType, miterLimit);
751 gfxcolor_t getFillColor(GfxState * state)
754 double opaq = state->getFillOpacity();
755 state->getFillRGB(&rgb);
757 col.r = colToByte(rgb.r);
758 col.g = colToByte(rgb.g);
759 col.b = colToByte(rgb.b);
760 col.a = (unsigned char)(opaq*255);
764 void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line)
766 gfxcolor_t col = getFillColor(state);
768 if(getLogLevel() >= LOGLEVEL_TRACE) {
769 msg("<trace> fill %02x%02x%02x%02x\n", col.r, col.g, col.b, col.a);
772 device->fill(device, line, &col);
775 void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line)
777 if(getLogLevel() >= LOGLEVEL_TRACE) {
778 msg("<trace> clip\n");
782 device->startclip(device, line);
783 states[statepos].clipping++;
786 void GFXOutputDev::clip(GfxState *state)
788 GfxPath * path = state->getPath();
789 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
790 clipToGfxLine(state, line);
794 void GFXOutputDev::eoClip(GfxState *state)
796 GfxPath * path = state->getPath();
797 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
799 if(getLogLevel() >= LOGLEVEL_TRACE) {
800 msg("<trace> eoclip\n");
804 device->startclip(device, line);
805 states[statepos].clipping++;
808 void GFXOutputDev::clipToStrokePath(GfxState *state)
810 GfxPath * path = state->getPath();
811 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
813 if(getLogLevel() >= LOGLEVEL_TRACE) {
814 msg("<trace> cliptostrokepath\n");
818 strokeGfxline(state, line, STROKE_FILL|STROKE_CLIP);
822 void GFXOutputDev::endframe()
825 device->endclip(device);
830 void GFXOutputDev::finish()
834 device->endclip(device);
840 GFXOutputDev::~GFXOutputDev()
845 free(this->pages); this->pages = 0;
848 gfxfontlist_free(this->gfxfontlist);
850 GBool GFXOutputDev::upsideDown()
854 GBool GFXOutputDev::useDrawChar()
859 const char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
860 "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
862 #define RENDER_FILL 0
863 #define RENDER_STROKE 1
864 #define RENDER_FILLSTROKE 2
865 #define RENDER_INVISIBLE 3
866 #define RENDER_CLIP 4
868 static char tmp_printstr[4096];
869 char* makeStringPrintable(char*str)
871 int len = strlen(str);
886 tmp_printstr[len++] = '.';
887 tmp_printstr[len++] = '.';
888 tmp_printstr[len++] = '.';
890 tmp_printstr[len] = 0;
894 void GFXOutputDev::beginString(GfxState *state, GString *s)
896 int render = state->getRender();
897 if(current_text_stroke) {
898 msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
901 msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
902 double m11,m21,m12,m22;
903 state->getFontTransMat(&m11, &m12, &m21, &m22);
904 m11 *= state->getHorizScaling();
905 m21 *= state->getHorizScaling();
907 this->current_font_matrix.m00 = m11 / 1024.0;
908 this->current_font_matrix.m01 = m12 / 1024.0;
909 this->current_font_matrix.m10 = -m21 / 1024.0;
910 this->current_font_matrix.m11 = -m22 / 1024.0;
911 this->current_font_matrix.tx = 0;
912 this->current_font_matrix.ty = 0;
915 static gfxline_t* mkEmptyGfxShape(double x, double y)
917 gfxline_t*line = (gfxline_t*)malloc(sizeof(gfxline_t));
918 line->x = x;line->y = y;line->type = gfx_moveTo;line->next = 0;
922 static char isValidUnicode(int c)
924 if(c>=32 && c<0x2fffe)
929 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
930 double dx, double dy,
931 double originX, double originY,
932 CharCode charid, int nBytes, Unicode *_u, int uLen)
934 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
935 msg("<error> Invalid charid %d for font %s", charid, current_font_id);
939 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
941 int render = state->getRender();
942 gfxcolor_t col = getFillColor(state);
944 // check for invisible text -- this is used by Acrobat Capture
945 if (render == RENDER_INVISIBLE) {
949 GfxFont*font = state->getFont();
951 if(font->getType() == fontType3 && do_interpretType3Chars) {
952 /* type 3 chars are passed as graphics */
953 msg("<debug> type3 char at %f/%f", x, y);
957 Unicode u = uLen?(_u[0]):0;
958 msg("<debug> drawChar(%f,%f,c='%c' (%d), u=%d <%d>) CID=%d render=%d\n",x,y,(charid&127)>=32?charid:'?', charid, u, uLen, font->isCIDFont(), render);
960 gfxmatrix_t m = this->current_font_matrix;
961 state->transform(x, y, &m.tx, &m.ty);
962 m.tx += user_movex + clipmovex;
963 m.ty += user_movey + clipmovey;
965 if(render == RENDER_FILL || render == RENDER_INVISIBLE) {
966 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
968 msg("<debug> Drawing glyph %d as shape", charid);
970 msg("<notice> Some texts will be rendered as shape");
973 gfxline_t*glyph = current_gfxfont->glyphs[glyphid].line;
974 gfxline_t*tglyph = gfxline_clone(glyph);
975 gfxline_transform(tglyph, &m);
976 if((render&3) != RENDER_INVISIBLE) {
977 gfxline_t*add = gfxline_clone(tglyph);
978 current_text_stroke = gfxline_append(current_text_stroke, add);
980 if(render&RENDER_CLIP) {
981 gfxline_t*add = gfxline_clone(tglyph);
982 current_text_clip = gfxline_append(current_text_clip, add);
983 if(!current_text_clip) {
984 current_text_clip = mkEmptyGfxShape(m.tx, m.ty);
987 gfxline_free(tglyph);
991 void GFXOutputDev::endString(GfxState *state)
993 int render = state->getRender();
994 msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
996 if(current_text_stroke) {
997 /* fillstroke and stroke text rendering objects we can process right
998 now (as there may be texts of other rendering modes in this
999 text object)- clipping objects have to wait until endTextObject,
1001 device->setparameter(device, "mark","TXT");
1002 if((render&3) == RENDER_FILL) {
1003 fillGfxLine(state, current_text_stroke);
1004 gfxline_free(current_text_stroke);
1005 current_text_stroke = 0;
1006 } else if((render&3) == RENDER_FILLSTROKE) {
1007 fillGfxLine(state, current_text_stroke);
1008 strokeGfxline(state, current_text_stroke,0);
1009 gfxline_free(current_text_stroke);
1010 current_text_stroke = 0;
1011 } else if((render&3) == RENDER_STROKE) {
1012 strokeGfxline(state, current_text_stroke,0);
1013 gfxline_free(current_text_stroke);
1014 current_text_stroke = 0;
1016 device->setparameter(device, "mark","");
1020 void GFXOutputDev::endTextObject(GfxState *state)
1022 int render = state->getRender();
1023 msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1025 if(current_text_clip) {
1026 device->setparameter(device, "mark","TXT");
1027 clipToGfxLine(state, current_text_clip);
1028 device->setparameter(device, "mark","");
1029 gfxline_free(current_text_clip);
1030 current_text_clip = 0;
1034 /* the logic seems to be as following:
1035 first, beginType3Char is called, with the charcode and the coordinates.
1036 if this function returns true, it already knew about the char and has now drawn it.
1037 if the function returns false, it's a new char, and type3D1 is called with some parameters-
1038 the all draw operations until endType3Char are part of the char (which in this moment is
1039 at the position first passed to beginType3Char). the char ends with endType3Char.
1041 The drawing operations between beginType3Char and endType3Char are somewhat different to
1042 the normal ones. For example, the fillcolor equals the stroke color.
1045 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode code, Unicode *u, int uLen)
1047 msg("<debug> beginType3Char %d, %08x, %d", code, *u, uLen);
1052 gfxcolor_t col={255,0,0,0};
1053 gfxmatrix_t m = {1,0,0, 0,1,0};
1055 for(t=0;t<uLen;t++) {
1056 device->drawchar(device, 0, u[t], &col, &m);
1059 /* the character itself is going to be passed using the draw functions */
1060 return gFalse; /* gTrue= is_in_cache? */
1063 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1064 msg("<debug> type3D0 width=%f height=%f", wx, wy);
1066 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1067 msg("<debug> type3D1 width=%f height=%f bbox=(%f,%f,%f,%f)", wx, wy,
1071 void GFXOutputDev::endType3Char(GfxState *state)
1074 msg("<debug> endType3Char");
1077 void GFXOutputDev::startFrame(int width, int height)
1079 if(outer_clip_box) {
1080 device->endclip(device);
1084 device->startpage(device, width, height);
1085 this->width = width;
1086 this->height = height;
1089 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
1091 this->currentpage = pageNum;
1093 int rot = doc->getPageRotate(1);
1096 gfxline_t clippath[5];
1098 white.r = white.g = white.b = white.a = 255;
1100 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1101 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1102 Use CropBox, not MediaBox, as page size
1109 state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1110 state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1112 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1113 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1115 this->clipmovex = -(int)x1;
1116 this->clipmovey = -(int)y1;
1118 /* apply user clip box */
1119 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1120 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1121 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1122 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1123 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1124 msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1127 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1129 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);
1131 msg("<verbose> page is rotated %d degrees\n", rot);
1133 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1134 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1135 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1136 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1137 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1138 device->startclip(device, clippath); outer_clip_box = 1;
1139 if(!config_transparent)
1140 device->fill(device, clippath, &white);
1144 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1146 double x1, y1, x2, y2, w;
1147 gfxline_t points[5];
1150 msg("<debug> drawlink\n");
1152 link->getRect(&x1, &y1, &x2, &y2);
1153 cvtUserToDev(x1, y1, &x, &y);
1154 points[0].type = gfx_moveTo;
1155 points[0].x = points[4].x = x + user_movex + clipmovex;
1156 points[0].y = points[4].y = y + user_movey + clipmovey;
1157 points[0].next = &points[1];
1158 cvtUserToDev(x2, y1, &x, &y);
1159 points[1].type = gfx_lineTo;
1160 points[1].x = x + user_movex + clipmovex;
1161 points[1].y = y + user_movey + clipmovey;
1162 points[1].next = &points[2];
1163 cvtUserToDev(x2, y2, &x, &y);
1164 points[2].type = gfx_lineTo;
1165 points[2].x = x + user_movex + clipmovex;
1166 points[2].y = y + user_movey + clipmovey;
1167 points[2].next = &points[3];
1168 cvtUserToDev(x1, y2, &x, &y);
1169 points[3].type = gfx_lineTo;
1170 points[3].x = x + user_movex + clipmovex;
1171 points[3].y = y + user_movey + clipmovey;
1172 points[3].next = &points[4];
1173 cvtUserToDev(x1, y1, &x, &y);
1174 points[4].type = gfx_lineTo;
1175 points[4].x = x + user_movex + clipmovex;
1176 points[4].y = y + user_movey + clipmovey;
1179 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f\n",
1180 points[0].x, points[0].y,
1181 points[1].x, points[1].y,
1182 points[2].x, points[2].y,
1183 points[3].x, points[3].y);
1185 LinkAction*action=link->getAction();
1188 const char*type = "-?-";
1191 msg("<trace> drawlink action=%d\n", action->getKind());
1192 switch(action->getKind())
1196 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1197 LinkDest *dest=NULL;
1198 if (ha->getDest()==NULL)
1199 dest=catalog->findDest(ha->getNamedDest());
1200 else dest=ha->getDest();
1202 if (dest->isPageRef()){
1203 Ref pageref=dest->getPageRef();
1204 page=catalog->findPage(pageref.num,pageref.gen);
1206 else page=dest->getPageNum();
1207 sprintf(buf, "%d", page);
1214 LinkGoToR*l = (LinkGoToR*)action;
1215 GString*g = l->getFileName();
1217 s = strdup(g->getCString());
1219 /* if the GoToR link has no filename, then
1220 try to find a refernce in the *local*
1222 GString*g = l->getNamedDest();
1224 s = strdup(g->getCString());
1230 LinkNamed*l = (LinkNamed*)action;
1231 GString*name = l->getName();
1233 s = strdup(name->lowerCase()->getCString());
1234 named = name->getCString();
1237 if(strstr(s, "next") || strstr(s, "forward"))
1239 page = currentpage + 1;
1241 else if(strstr(s, "prev") || strstr(s, "back"))
1243 page = currentpage - 1;
1245 else if(strstr(s, "last") || strstr(s, "end"))
1247 if(pages && pagepos>0)
1248 page = pages[pagepos-1];
1250 else if(strstr(s, "first") || strstr(s, "top"))
1258 case actionLaunch: {
1260 LinkLaunch*l = (LinkLaunch*)action;
1261 GString * str = new GString(l->getFileName());
1262 GString * params = l->getParams();
1264 str->append(params);
1265 s = strdup(str->getCString());
1272 LinkURI*l = (LinkURI*)action;
1273 GString*g = l->getURI();
1275 url = g->getCString();
1280 case actionUnknown: {
1282 LinkUnknown*l = (LinkUnknown*)action;
1287 msg("<error> Unknown link type!\n");
1292 if(!s) s = strdup("-?-");
1294 msg("<trace> drawlink s=%s\n", s);
1296 if(!linkinfo && (page || s))
1298 msg("<notice> File contains links");
1306 for(t=1;t<=pagepos;t++) {
1307 if(pages[t]==page) {
1316 sprintf(buf, "page%d", lpage);
1317 device->drawlink(device, points, buf);
1321 device->drawlink(device, points, s);
1324 msg("<verbose> \"%s\" link to \"%s\" (%d)\n", type, FIXNULL(s), page);
1328 void GFXOutputDev::saveState(GfxState *state) {
1329 dbg("saveState");dbgindent+=2;
1331 msg("<trace> saveState\n");
1334 msg("<error> Too many nested states in pdf.");
1338 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1339 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1340 states[statepos].clipping = 0;
1343 void GFXOutputDev::restoreState(GfxState *state) {
1344 dbgindent-=2; dbg("restoreState");
1347 msg("<error> Invalid restoreState");
1350 msg("<trace> restoreState%s%s", states[statepos].softmask?" (end softmask)":"",
1351 states[statepos].clipping?" (end clipping)":"");
1352 if(states[statepos].softmask) {
1353 clearSoftMask(state);
1356 while(states[statepos].clipping) {
1357 device->endclip(device);
1358 states[statepos].clipping--;
1363 void GFXOutputDev::updateLineWidth(GfxState *state)
1365 double width = state->getTransformedLineWidth();
1366 //swfoutput_setlinewidth(&device, width);
1369 void GFXOutputDev::updateLineCap(GfxState *state)
1371 int c = state->getLineCap();
1374 void GFXOutputDev::updateLineJoin(GfxState *state)
1376 int j = state->getLineJoin();
1379 void GFXOutputDev::updateFillColor(GfxState *state)
1382 double opaq = state->getFillOpacity();
1383 state->getFillRGB(&rgb);
1385 void GFXOutputDev::updateFillOpacity(GfxState *state)
1388 double opaq = state->getFillOpacity();
1389 state->getFillRGB(&rgb);
1390 dbg("update fillopaq %f", opaq);
1392 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1394 double opaq = state->getFillOpacity();
1395 dbg("update strokeopaq %f", opaq);
1397 void GFXOutputDev::updateFillOverprint(GfxState *state)
1399 double opaq = state->getFillOverprint();
1400 dbg("update filloverprint %f", opaq);
1402 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1404 double opaq = state->getStrokeOverprint();
1405 dbg("update strokeoverprint %f", opaq);
1407 void GFXOutputDev::updateTransfer(GfxState *state)
1409 dbg("update transfer");
1413 void GFXOutputDev::updateStrokeColor(GfxState *state)
1416 double opaq = state->getStrokeOpacity();
1417 state->getStrokeRGB(&rgb);
1420 void GFXOutputDev::setXRef(PDFDoc*doc, XRef *xref)
1426 gfxfont_t* createGfxFont(GfxFont*xpdffont, FontInfo*src)
1428 gfxfont_t*font = (gfxfont_t*)malloc(sizeof(gfxfont_t));
1429 memset(font, 0, sizeof(gfxfont_t));
1431 font->glyphs = (gfxglyph_t*)malloc(sizeof(gfxglyph_t)*src->num_glyphs);
1432 memset(font->glyphs, 0, sizeof(gfxglyph_t)*src->num_glyphs);
1433 font->id = strdup(getFontName(xpdffont));
1435 double quality = (1024 * 0.05) / src->max_size;
1437 //printf("%d glyphs\n", font->num_glyphs);
1438 font->num_glyphs = 0;
1439 for(t=0;t<src->num_glyphs;t++) {
1440 if(src->glyphs[t]) {
1441 SplashPath*path = src->glyphs[t]->path;
1442 int len = path?path->getLength():0;
1443 //printf("glyph %d) %08x (%d line segments)\n", t, path, len);
1444 gfxglyph_t*glyph = &font->glyphs[font->num_glyphs];
1445 src->glyphs[t]->glyphid = font->num_glyphs;
1446 glyph->unicode = src->glyphs[t]->unicode;
1447 if(glyph->unicode >= font->max_unicode)
1448 font->max_unicode = glyph->unicode+1;
1450 gfxdrawer_target_gfxline(&drawer);
1454 for(s=0;s<len;s++) {
1457 path->getPoint(s, &x, &y, &f);
1460 if(f&splashPathFirst) {
1461 drawer.moveTo(&drawer, x*scale, y*scale);
1463 if(f&splashPathCurve) {
1465 path->getPoint(++s, &x2, &y2, &f);
1466 if(f&splashPathCurve) {
1468 path->getPoint(++s, &x3, &y3, &f);
1469 gfxdraw_cubicTo(&drawer, x*scale, y*scale, x2*scale, y2*scale, x3*scale, y3*scale, quality);
1471 drawer.splineTo(&drawer, x*scale, y*scale, x2*scale, y2*scale);
1474 drawer.lineTo(&drawer, x*scale, y*scale);
1476 // printf("%f %f %s %s\n", x, y, (f&splashPathCurve)?"curve":"",
1477 // (f&splashPathFirst)?"first":"",
1478 // (f&splashPathLast)?"last":"");
1480 glyph->line = (gfxline_t*)drawer.result(&drawer);
1481 glyph->advance = xmax*scale; // we don't know the real advance value, so this'll have to do
1485 font->unicode2glyph = (int*)malloc(sizeof(int)*font->max_unicode);
1486 memset(font->unicode2glyph, -1, sizeof(int)*font->max_unicode);
1487 for(t=0;t<font->num_glyphs;t++) {
1488 if(font->glyphs[t].unicode>0 && font->glyphs[t].unicode<font->max_unicode) {
1489 font->unicode2glyph[font->glyphs[t].unicode] = t;
1496 void GFXOutputDev::updateFont(GfxState *state)
1498 GfxFont* gfxFont = state->getFont();
1502 char*id = getFontID(gfxFont);
1504 msg("<error> Internal Error: FontID is null");
1508 this->current_fontinfo = this->info->getFont(id);
1510 gfxfont_t*font = gfxfontlist_findfont(this->gfxfontlist,id);
1512 font = createGfxFont(gfxFont, current_fontinfo);
1513 gfxfontlist_addfont(this->gfxfontlist, font);
1514 device->addfont(device, font);
1516 current_gfxfont = font;
1520 #define SQR(x) ((x)*(x))
1522 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
1524 if((newwidth<2 || newheight<2) ||
1525 (width<=newwidth || height<=newheight))
1527 unsigned char*newdata;
1529 newdata= (unsigned char*)malloc(newwidth*newheight);
1531 double fx = (double)(width)/newwidth;
1532 double fy = (double)(height)/newheight;
1534 int blocksize = (int)(8192/(fx*fy));
1535 int r = 8192*256/palettesize;
1536 for(x=0;x<newwidth;x++) {
1537 double ex = px + fx;
1538 int fromx = (int)px;
1540 int xweight1 = (int)(((fromx+1)-px)*256);
1541 int xweight2 = (int)((ex-tox)*256);
1543 for(y=0;y<newheight;y++) {
1544 double ey = py + fy;
1545 int fromy = (int)py;
1547 int yweight1 = (int)(((fromy+1)-py)*256);
1548 int yweight2 = (int)((ey-toy)*256);
1551 for(xx=fromx;xx<=tox;xx++)
1552 for(yy=fromy;yy<=toy;yy++) {
1553 int b = 1-data[width*yy+xx];
1555 if(xx==fromx) weight = (weight*xweight1)/256;
1556 if(xx==tox) weight = (weight*xweight2)/256;
1557 if(yy==fromy) weight = (weight*yweight1)/256;
1558 if(yy==toy) weight = (weight*yweight2)/256;
1561 //if(a) a=(palettesize-1)*r/blocksize;
1562 newdata[y*newwidth+x] = (a*blocksize)/r;
1570 #define IMAGE_TYPE_JPEG 0
1571 #define IMAGE_TYPE_LOSSLESS 1
1573 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
1574 double x1,double y1,
1575 double x2,double y2,
1576 double x3,double y3,
1577 double x4,double y4, int type)
1579 gfxcolor_t*newpic=0;
1581 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
1582 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
1584 gfxline_t p1,p2,p3,p4,p5;
1585 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
1586 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
1587 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
1588 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
1589 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
1591 {p1.x = (int)(p1.x*20)/20.0;
1592 p1.y = (int)(p1.y*20)/20.0;
1593 p2.x = (int)(p2.x*20)/20.0;
1594 p2.y = (int)(p2.y*20)/20.0;
1595 p3.x = (int)(p3.x*20)/20.0;
1596 p3.y = (int)(p3.y*20)/20.0;
1597 p4.x = (int)(p4.x*20)/20.0;
1598 p4.y = (int)(p4.y*20)/20.0;
1599 p5.x = (int)(p5.x*20)/20.0;
1600 p5.y = (int)(p5.y*20)/20.0;
1607 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
1608 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
1613 img.data = (gfxcolor_t*)data;
1617 if(type == IMAGE_TYPE_JPEG)
1618 /* TODO: pass image_dpi to device instead */
1619 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
1621 dev->fillbitmap(dev, &p1, &img, &m, 0);
1624 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
1625 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
1627 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG);
1630 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
1631 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
1633 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS);
1637 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
1638 int width, int height, GfxImageColorMap*colorMap, GBool invert,
1639 GBool inlineImg, int mask, int*maskColors,
1640 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
1642 double x1,y1,x2,y2,x3,y3,x4,y4;
1643 ImageStream *imgStr;
1648 unsigned char* maskbitmap = 0;
1651 ncomps = colorMap->getNumPixelComps();
1652 bits = colorMap->getBits();
1657 unsigned char buf[8];
1658 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
1660 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
1661 imgMaskStr->reset();
1662 unsigned char pal[256];
1663 int n = 1 << colorMap->getBits();
1668 maskColorMap->getGray(pixBuf, &gray);
1669 pal[t] = colToByte(gray);
1671 for (y = 0; y < maskHeight; y++) {
1672 for (x = 0; x < maskWidth; x++) {
1673 imgMaskStr->getPixel(buf);
1674 maskbitmap[y*maskWidth+x] = pal[buf[0]];
1679 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
1680 imgMaskStr->reset();
1681 for (y = 0; y < maskHeight; y++) {
1682 for (x = 0; x < maskWidth; x++) {
1683 imgMaskStr->getPixel(buf);
1685 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
1693 imgStr = new ImageStream(str, width, ncomps,bits);
1696 if(!width || !height || (height<=1 && width<=1))
1698 msg("<verbose> Ignoring %d by %d image", width, height);
1699 unsigned char buf[8];
1701 for (y = 0; y < height; ++y)
1702 for (x = 0; x < width; ++x) {
1703 imgStr->getPixel(buf);
1711 state->transform(0, 1, &x1, &y1); x1 += user_movex + clipmovex; y1 += user_movey + clipmovey;
1712 state->transform(0, 0, &x2, &y2); x2 += user_movex + clipmovex; y2 += user_movey + clipmovey;
1713 state->transform(1, 0, &x3, &y3); x3 += user_movex + clipmovex; y3 += user_movey + clipmovey;
1714 state->transform(1, 1, &x4, &y4); x4 += user_movex + clipmovex; y4 += user_movey + clipmovey;
1716 if(!pbminfo && !(str->getKind()==strDCT)) {
1718 msg("<notice> file contains pbm pictures %s",mask?"(masked)":"");
1722 msg("<verbose> drawing %d by %d masked picture\n", width, height);
1724 if(!jpeginfo && (str->getKind()==strDCT)) {
1725 msg("<notice> file contains jpeg pictures");
1731 unsigned char buf[8];
1733 unsigned char*pic = new unsigned char[width*height];
1734 gfxcolor_t pal[256];
1736 state->getFillRGB(&rgb);
1738 memset(pal,255,sizeof(pal));
1739 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
1740 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
1741 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
1742 pal[0].a = 255; pal[1].a = 0;
1745 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
1746 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
1747 for (y = 0; y < height; ++y)
1748 for (x = 0; x < width; ++x)
1750 imgStr->getPixel(buf);
1753 pic[width*y+x] = buf[0];
1756 /* the size of the drawn image is added to the identifier
1757 as the same image may require different bitmaps if displayed
1758 at different sizes (due to antialiasing): */
1761 unsigned char*pic2 = 0;
1764 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
1773 height = realheight;
1777 /* make a black/white palette */
1779 float r = 255/(numpalette-1);
1781 for(t=0;t<numpalette;t++) {
1782 pal[t].r = colToByte(rgb.r);
1783 pal[t].g = colToByte(rgb.g);
1784 pal[t].b = colToByte(rgb.b);
1785 pal[t].a = (unsigned char)(t*r);
1789 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
1790 for (y = 0; y < height; ++y) {
1791 for (x = 0; x < width; ++x) {
1792 pic2[width*y+x] = pal[pic[y*width+x]];
1795 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1799 if(maskbitmap) free(maskbitmap);
1805 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
1806 gfxcolor_t*pic=new gfxcolor_t[width*height];
1807 for (y = 0; y < height; ++y) {
1808 for (x = 0; x < width; ++x) {
1809 imgStr->getPixel(pixBuf);
1810 colorMap->getRGB(pixBuf, &rgb);
1811 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
1812 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
1813 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
1814 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
1816 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
1820 if(str->getKind()==strDCT)
1821 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1823 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1826 if(maskbitmap) free(maskbitmap);
1829 gfxcolor_t*pic=new gfxcolor_t[width*height];
1830 gfxcolor_t pal[256];
1831 int n = 1 << colorMap->getBits();
1833 for(t=0;t<256;t++) {
1835 colorMap->getRGB(pixBuf, &rgb);
1837 {/*if(maskColors && *maskColors==t) {
1838 msg("<notice> Color %d is transparent", t);
1839 if (imgData->maskColors) {
1841 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
1842 if (pix[i] < imgData->maskColors[2*i] ||
1843 pix[i] > imgData->maskColors[2*i+1]) {
1858 pal[t].r = (unsigned char)(colToByte(rgb.r));
1859 pal[t].g = (unsigned char)(colToByte(rgb.g));
1860 pal[t].b = (unsigned char)(colToByte(rgb.b));
1861 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
1864 for (y = 0; y < height; ++y) {
1865 for (x = 0; x < width; ++x) {
1866 imgStr->getPixel(pixBuf);
1867 pic[width*y+x] = pal[pixBuf[0]];
1869 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
1873 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1877 if(maskbitmap) free(maskbitmap);
1882 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
1883 int width, int height, GBool invert,
1886 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
1887 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
1888 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
1891 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
1892 int width, int height, GfxImageColorMap *colorMap,
1893 int *maskColors, GBool inlineImg)
1895 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
1896 colorMap?"colorMap":"no colorMap",
1897 maskColors?"maskColors":"no maskColors",
1899 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
1900 colorMap?"colorMap":"no colorMap",
1901 maskColors?"maskColors":"no maskColors",
1904 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
1905 colorMap->getBits(),colorMap->getColorSpace()->getMode());
1906 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
1909 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
1910 int width, int height,
1911 GfxImageColorMap *colorMap,
1912 Stream *maskStr, int maskWidth, int maskHeight,
1915 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
1916 colorMap?"colorMap":"no colorMap",
1917 maskWidth, maskHeight);
1918 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
1919 colorMap?"colorMap":"no colorMap",
1920 maskWidth, maskHeight);
1922 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
1923 colorMap->getBits(),colorMap->getColorSpace()->getMode());
1924 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
1927 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
1928 int width, int height,
1929 GfxImageColorMap *colorMap,
1931 int maskWidth, int maskHeight,
1932 GfxImageColorMap *maskColorMap)
1934 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
1935 colorMap?"colorMap":"no colorMap",
1936 maskWidth, maskHeight);
1937 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
1938 colorMap?"colorMap":"no colorMap",
1939 maskWidth, maskHeight);
1941 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
1942 colorMap->getBits(),colorMap->getColorSpace()->getMode());
1943 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
1946 void GFXOutputDev::stroke(GfxState *state)
1950 GfxPath * path = state->getPath();
1951 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
1952 strokeGfxline(state, line, 0);
1956 void GFXOutputDev::fill(GfxState *state)
1958 gfxcolor_t col = getFillColor(state);
1959 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
1961 GfxPath * path = state->getPath();
1962 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1963 fillGfxLine(state, line);
1967 void GFXOutputDev::eoFill(GfxState *state)
1969 gfxcolor_t col = getFillColor(state);
1970 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
1972 GfxPath * path = state->getPath();
1973 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1974 fillGfxLine(state, line);
1979 static const char* dirseparator()
1988 void addGlobalFont(const char*filename)
1991 memset(&f, 0, sizeof(fontfile_t));
1992 f.filename = filename;
1993 if(fontnum < sizeof(fonts)/sizeof(fonts[0])) {
1994 msg("<verbose> Adding font \"%s\".", filename);
1995 fonts[fontnum++] = f;
1997 msg("<error> Too many external fonts. Not adding font file \"%s\".", filename);
2001 void addGlobalLanguageDir(const char*dir)
2003 msg("<notice> Adding %s to language pack directories", dir);
2007 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2008 strcpy(config_file, dir);
2009 strcat(config_file, dirseparator());
2010 strcat(config_file, "add-to-xpdfrc");
2012 fi = fopen(config_file, "rb");
2014 msg("<error> Could not open %s", config_file);
2017 globalParams->parseFile(new GString(config_file), fi);
2021 void addGlobalFontDir(const char*dirname)
2023 #ifdef HAVE_DIRENT_H
2024 msg("<notice> Adding %s to font directories", dirname);
2025 lastfontdir = strdup(dirname);
2026 DIR*dir = opendir(dirname);
2028 msg("<warning> Couldn't open directory %s\n", dirname);
2033 ent = readdir (dir);
2037 char*name = ent->d_name;
2043 if(!strncasecmp(&name[l-4], ".pfa", 4))
2045 if(!strncasecmp(&name[l-4], ".pfb", 4))
2047 if(!strncasecmp(&name[l-4], ".ttf", 4))
2051 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2052 strcpy(fontname, dirname);
2053 strcat(fontname, dirseparator());
2054 strcat(fontname, name);
2055 addGlobalFont(fontname);
2060 msg("<warning> No dirent.h- unable to add font dir %s", dir);
2064 void GFXOutputDev::preparePage(int pdfpage, int outputpage)
2070 this->pagebuflen = 1024;
2071 this->pages = (int*)malloc(this->pagebuflen*sizeof(int));
2072 memset(this->pages, -1, this->pagebuflen*sizeof(int));
2074 while(pdfpage >= this->pagebuflen)
2076 int oldlen = this->pagebuflen;
2077 this->pagebuflen+=1024;
2078 this->pages = (int*)realloc(this->pages, this->pagebuflen*sizeof(int));
2079 memset(&this->pages[oldlen], -1, (this->pagebuflen-oldlen)*sizeof(int));
2082 this->pages[pdfpage] = outputpage;
2083 if(pdfpage>this->pagepos)
2084 this->pagepos = pdfpage;
2090 double width,height;
2093 BBox mkBBox(GfxState*state, double*bbox, double width, double height)
2095 double xMin, yMin, xMax, yMax, x, y;
2096 double tx, ty, w, h;
2097 // transform the bbox
2098 state->transform(bbox[0], bbox[1], &x, &y);
2101 state->transform(bbox[0], bbox[3], &x, &y);
2104 } else if (x > xMax) {
2109 } else if (y > yMax) {
2112 state->transform(bbox[2], bbox[1], &x, &y);
2115 } else if (x > xMax) {
2120 } else if (y > yMax) {
2123 state->transform(bbox[2], bbox[3], &x, &y);
2126 } else if (x > xMax) {
2131 } else if (y > yMax) {
2134 tx = (int)floor(xMin);
2137 } else if (tx > width) {
2140 ty = (int)floor(yMin);
2143 } else if (ty > height) {
2146 w = (int)ceil(xMax) - tx + 1;
2147 if (tx + w > width) {
2153 h = (int)ceil(yMax) - ty + 1;
2154 if (ty + h > height) {
2168 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2169 GfxColorSpace *blendingColorSpace,
2170 GBool isolated, GBool knockout,
2173 const char*colormodename = "";
2174 BBox rect = mkBBox(state, bbox, this->width, this->height);
2176 if(blendingColorSpace) {
2177 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2179 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);
2180 dbg("using clipping rect %f/%f/%f/%f\n", rect.posx,rect.posy,rect.width,rect.height);
2181 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);
2183 states[statepos].createsoftmask |= forSoftMask;
2184 states[statepos].transparencygroup = !forSoftMask;
2185 states[statepos].isolated = isolated;
2187 states[statepos].olddevice = this->device;
2188 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2190 gfxdevice_record_init(this->device);
2192 /*if(!forSoftMask) { ////???
2193 state->setFillOpacity(0.0);
2198 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2201 dbg("endTransparencyGroup");
2202 msg("<verbose> endTransparencyGroup");
2204 gfxdevice_t*r = this->device;
2206 this->device = states[statepos].olddevice;
2208 if(states[statepos].createsoftmask) {
2209 states[statepos-1].softmaskrecording = r->finish(r);
2211 states[statepos-1].grouprecording = r->finish(r);
2214 states[statepos].createsoftmask = 0;
2215 states[statepos].transparencygroup = 0;
2219 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2221 const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2222 "colordodge","colorburn","hardlight","softlight","difference",
2223 "exclusion","hue","saturation","color","luminosity"};
2225 dbg("paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2226 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2228 if(state->getBlendMode() == gfxBlendNormal)
2229 infofeature("transparency groups");
2232 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2233 warnfeature(buffer, 0);
2236 gfxresult_t*grouprecording = states[statepos].grouprecording;
2238 if(state->getBlendMode() == gfxBlendNormal) {
2240 gfxdevice_ops_init(&ops, this->device, (unsigned char)(state->getFillOpacity()*255));
2241 gfxresult_record_replay(grouprecording, &ops);
2244 grouprecording->destroy(grouprecording);
2246 states[statepos].grouprecording = 0;
2249 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2251 /* alpha = 1: retrieve mask values from alpha layer
2252 alpha = 0: retrieve mask values from luminance */
2253 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2254 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2255 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2256 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2258 infofeature("soft masks");
2260 warnfeature("soft masks from alpha channel",0);
2262 states[statepos].olddevice = this->device;
2263 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2264 gfxdevice_record_init(this->device);
2266 dbg("softmaskrecording is %08x at statepos %d\n", states[statepos].softmaskrecording, statepos);
2268 states[statepos].softmask = 1;
2269 states[statepos].softmask_alpha = alpha;
2272 static inline Guchar div255(int x) {
2273 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2276 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2278 if(c < min) c = min;
2279 if(c > max) c = max;
2283 void GFXOutputDev::clearSoftMask(GfxState *state)
2285 if(!states[statepos].softmask)
2287 states[statepos].softmask = 0;
2288 dbg("clearSoftMask statepos=%d", statepos);
2289 msg("<verbose> clearSoftMask");
2291 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2292 msg("<error> Error in softmask/tgroup ordering");
2296 gfxresult_t*mask = states[statepos].softmaskrecording;
2297 gfxresult_t*below = this->device->finish(this->device);
2298 this->device = states[statepos].olddevice;
2300 /* get outline of all objects below the soft mask */
2301 gfxdevice_t uniondev;
2302 gfxdevice_union_init(&uniondev, 0);
2303 gfxresult_record_replay(below, &uniondev);
2304 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2305 uniondev.finish(&uniondev);
2307 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2309 this->device->startclip(this->device, belowoutline);
2310 gfxresult_record_replay(below, this->device);
2311 gfxresult_record_replay(mask, this->device);
2312 this->device->endclip(this->device);
2313 gfxline_free(belowoutline);
2316 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2317 if(width<=0 || height<=0)
2320 gfxdevice_t belowrender;
2321 gfxdevice_render_init(&belowrender);
2322 if(states[statepos+1].isolated) {
2323 belowrender.setparameter(&belowrender, "fillwhite", "1");
2325 belowrender.setparameter(&belowrender, "antialize", "2");
2326 belowrender.startpage(&belowrender, width, height);
2327 gfxresult_record_replay(below, &belowrender);
2328 belowrender.endpage(&belowrender);
2329 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2330 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2331 //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2333 gfxdevice_t maskrender;
2334 gfxdevice_render_init(&maskrender);
2335 maskrender.startpage(&maskrender, width, height);
2336 gfxresult_record_replay(mask, &maskrender);
2337 maskrender.endpage(&maskrender);
2338 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2339 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2341 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2342 msg("<fatal> Internal error in mask drawing");
2347 for(y=0;y<height;y++) {
2348 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2349 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2350 for(x=0;x<width;x++) {
2352 if(states[statepos].softmask_alpha) {
2355 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2358 l2->a = div255(alpha*l2->a);
2360 /* DON'T premultiply alpha- this is done by fillbitmap,
2361 depending on the output device */
2362 //l2->r = div255(alpha*l2->r);
2363 //l2->g = div255(alpha*l2->g);
2364 //l2->b = div255(alpha*l2->b);
2370 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2373 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2374 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2376 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2378 mask->destroy(mask);
2379 below->destroy(below);
2380 maskresult->destroy(maskresult);
2381 belowresult->destroy(belowresult);
2382 states[statepos].softmaskrecording = 0;
2387 // public: ~MemCheck()
2389 // delete globalParams;globalParams=0;
2390 // Object::memCheck(stderr);
2391 // gMemReport(stderr);