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()
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->config_extrafontdata = 0;
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,"breakonwarning")) {
381 this->config_break_on_warning = atoi(value);
382 } else if(!strcmp(key,"fontconfig")) {
383 this->config_use_fontconfig = atoi(value);
384 } else if(!strcmp(key,"remapunicode")) {
385 this->config_remapunicode = atoi(value);
386 } else if(!strcmp(key,"transparent")) {
387 this->config_transparent = atoi(value);
388 } else if(!strcmp(key,"extrafontdata")) {
389 this->config_extrafontdata = 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;
893 #define INTERNAL_FONT_SIZE 1024.0
894 void GFXOutputDev::updateFontMatrix(GfxState*state)
896 double m11,m21,m12,m22;
897 state->getFontTransMat(&m11, &m12, &m21, &m22);
898 m11 *= state->getHorizScaling();
899 m21 *= state->getHorizScaling();
901 this->current_font_matrix.m00 = m11 / INTERNAL_FONT_SIZE;
902 this->current_font_matrix.m01 = m12 / INTERNAL_FONT_SIZE;
903 this->current_font_matrix.m10 = -m21 / INTERNAL_FONT_SIZE;
904 this->current_font_matrix.m11 = -m22 / INTERNAL_FONT_SIZE;
905 this->current_font_matrix.tx = 0;
906 this->current_font_matrix.ty = 0;
909 void GFXOutputDev::beginString(GfxState *state, GString *s)
911 int render = state->getRender();
912 if(current_text_stroke) {
913 msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
916 msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
917 updateFontMatrix(state);
920 static gfxline_t* mkEmptyGfxShape(double x, double y)
922 gfxline_t*line = (gfxline_t*)malloc(sizeof(gfxline_t));
923 line->x = x;line->y = y;line->type = gfx_moveTo;line->next = 0;
927 static char isValidUnicode(int c)
929 if(c>=32 && c<0x2fffe)
934 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
935 double dx, double dy,
936 double originX, double originY,
937 CharCode charid, int nBytes, Unicode *_u, int uLen)
939 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
940 msg("<error> Invalid charid %d for font %s", charid, current_font_id);
944 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
946 int render = state->getRender();
947 gfxcolor_t col = getFillColor(state);
949 // check for invisible text -- this is used by Acrobat Capture
950 if (render == RENDER_INVISIBLE) {
954 GfxFont*font = state->getFont();
956 if(font->getType() == fontType3) {
957 /* type 3 chars are passed as graphics */
958 msg("<debug> type3 char at %f/%f", x, y);
962 Unicode u = uLen?(_u[0]):0;
963 msg("<debug> drawChar(%f,%f,c='%c' (%d), u=%d <%d>) CID=%d render=%d glyphid=%d\n",x,y,(charid&127)>=32?charid:'?', charid, u, uLen, font->isCIDFont(), render, glyphid);
965 gfxmatrix_t m = this->current_font_matrix;
966 state->transform(x, y, &m.tx, &m.ty);
967 m.tx += user_movex + clipmovex;
968 m.ty += user_movey + clipmovey;
970 if(render == RENDER_FILL || render == RENDER_INVISIBLE) {
971 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
973 msg("<debug> Drawing glyph %d as shape", charid);
975 msg("<notice> Some texts will be rendered as shape");
978 gfxline_t*glyph = current_gfxfont->glyphs[glyphid].line;
979 gfxline_t*tglyph = gfxline_clone(glyph);
980 gfxline_transform(tglyph, &m);
981 if((render&3) != RENDER_INVISIBLE) {
982 gfxline_t*add = gfxline_clone(tglyph);
983 current_text_stroke = gfxline_append(current_text_stroke, add);
985 if(render&RENDER_CLIP) {
986 gfxline_t*add = gfxline_clone(tglyph);
987 current_text_clip = gfxline_append(current_text_clip, add);
988 if(!current_text_clip) {
989 current_text_clip = mkEmptyGfxShape(m.tx, m.ty);
992 gfxline_free(tglyph);
996 void GFXOutputDev::endString(GfxState *state)
998 int render = state->getRender();
999 msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
1001 if(current_text_stroke) {
1002 /* fillstroke and stroke text rendering objects we can process right
1003 now (as there may be texts of other rendering modes in this
1004 text object)- clipping objects have to wait until endTextObject,
1006 device->setparameter(device, "mark","TXT");
1007 if((render&3) == RENDER_FILL) {
1008 fillGfxLine(state, current_text_stroke);
1009 gfxline_free(current_text_stroke);
1010 current_text_stroke = 0;
1011 } else if((render&3) == RENDER_FILLSTROKE) {
1012 fillGfxLine(state, current_text_stroke);
1013 strokeGfxline(state, current_text_stroke,0);
1014 gfxline_free(current_text_stroke);
1015 current_text_stroke = 0;
1016 } else if((render&3) == RENDER_STROKE) {
1017 strokeGfxline(state, current_text_stroke,0);
1018 gfxline_free(current_text_stroke);
1019 current_text_stroke = 0;
1021 device->setparameter(device, "mark","");
1025 void GFXOutputDev::endTextObject(GfxState *state)
1027 int render = state->getRender();
1028 msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1030 if(current_text_clip) {
1031 device->setparameter(device, "mark","TXT");
1032 clipToGfxLine(state, current_text_clip);
1033 device->setparameter(device, "mark","");
1034 gfxline_free(current_text_clip);
1035 current_text_clip = 0;
1039 /* the logic seems to be as following:
1040 first, beginType3Char is called, with the charcode and the coordinates.
1041 if this function returns true, it already knew about the char and has now drawn it.
1042 if the function returns false, it's a new char, and type3D0 and/or type3D1 might be
1043 called with some parameters.
1044 Afterwards, all draw operations until endType3Char are part of the char (which in this moment is
1045 at the position first passed to beginType3Char). the char ends with endType3Char.
1047 The drawing operations between beginType3Char and endType3Char are somewhat different to
1048 the normal ones. For example, the fillcolor equals the stroke color. (Because the stroke
1049 color determines the color of a font)
1052 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode charid, Unicode *u, int uLen)
1054 msg("<debug> beginType3Char %d u=%d", charid, uLen?u[0]:0);
1057 if(config_extrafontdata && current_fontinfo) {
1059 gfxmatrix_t m = this->current_font_matrix;
1060 state->transform(0, 0, &m.tx, &m.ty);
1061 m.m00*=INTERNAL_FONT_SIZE;
1062 m.m01*=INTERNAL_FONT_SIZE;
1063 m.m10*=INTERNAL_FONT_SIZE;
1064 m.m11*=INTERNAL_FONT_SIZE;
1065 m.tx += user_movex + clipmovex;
1066 m.ty += user_movey + clipmovey;
1068 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1069 msg("<error> Invalid charid %d for font %s", charid, current_font_id);
1072 gfxcolor_t col={0,0,0,0};
1073 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1074 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1078 /* the character itself is going to be passed using the draw functions */
1079 return gFalse; /* gTrue= is_in_cache? */
1082 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1084 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1087 void GFXOutputDev::endType3Char(GfxState *state)
1090 msg("<debug> endType3Char");
1093 void GFXOutputDev::startFrame(int width, int height)
1095 if(outer_clip_box) {
1096 device->endclip(device);
1100 device->startpage(device, width, height);
1101 this->width = width;
1102 this->height = height;
1105 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
1107 this->currentpage = pageNum;
1109 int rot = doc->getPageRotate(1);
1112 gfxline_t clippath[5];
1114 white.r = white.g = white.b = white.a = 255;
1116 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1117 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1118 Use CropBox, not MediaBox, as page size
1125 state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1126 state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1128 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1129 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1131 this->clipmovex = -(int)x1;
1132 this->clipmovey = -(int)y1;
1134 /* apply user clip box */
1135 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1136 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1137 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1138 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1139 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1140 msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1143 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1145 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);
1147 msg("<verbose> page is rotated %d degrees\n", rot);
1149 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1150 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1151 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1152 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1153 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1154 device->startclip(device, clippath); outer_clip_box = 1;
1155 if(!config_transparent) {
1156 device->fill(device, clippath, &white);
1161 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1163 double x1, y1, x2, y2, w;
1164 gfxline_t points[5];
1167 msg("<debug> drawlink\n");
1169 link->getRect(&x1, &y1, &x2, &y2);
1170 cvtUserToDev(x1, y1, &x, &y);
1171 points[0].type = gfx_moveTo;
1172 points[0].x = points[4].x = x + user_movex + clipmovex;
1173 points[0].y = points[4].y = y + user_movey + clipmovey;
1174 points[0].next = &points[1];
1175 cvtUserToDev(x2, y1, &x, &y);
1176 points[1].type = gfx_lineTo;
1177 points[1].x = x + user_movex + clipmovex;
1178 points[1].y = y + user_movey + clipmovey;
1179 points[1].next = &points[2];
1180 cvtUserToDev(x2, y2, &x, &y);
1181 points[2].type = gfx_lineTo;
1182 points[2].x = x + user_movex + clipmovex;
1183 points[2].y = y + user_movey + clipmovey;
1184 points[2].next = &points[3];
1185 cvtUserToDev(x1, y2, &x, &y);
1186 points[3].type = gfx_lineTo;
1187 points[3].x = x + user_movex + clipmovex;
1188 points[3].y = y + user_movey + clipmovey;
1189 points[3].next = &points[4];
1190 cvtUserToDev(x1, y1, &x, &y);
1191 points[4].type = gfx_lineTo;
1192 points[4].x = x + user_movex + clipmovex;
1193 points[4].y = y + user_movey + clipmovey;
1196 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f\n",
1197 points[0].x, points[0].y,
1198 points[1].x, points[1].y,
1199 points[2].x, points[2].y,
1200 points[3].x, points[3].y);
1202 LinkAction*action=link->getAction();
1205 const char*type = "-?-";
1208 msg("<trace> drawlink action=%d\n", action->getKind());
1209 switch(action->getKind())
1213 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1214 LinkDest *dest=NULL;
1215 if (ha->getDest()==NULL)
1216 dest=catalog->findDest(ha->getNamedDest());
1217 else dest=ha->getDest();
1219 if (dest->isPageRef()){
1220 Ref pageref=dest->getPageRef();
1221 page=catalog->findPage(pageref.num,pageref.gen);
1223 else page=dest->getPageNum();
1224 sprintf(buf, "%d", page);
1231 LinkGoToR*l = (LinkGoToR*)action;
1232 GString*g = l->getFileName();
1234 s = strdup(g->getCString());
1236 /* if the GoToR link has no filename, then
1237 try to find a refernce in the *local*
1239 GString*g = l->getNamedDest();
1241 s = strdup(g->getCString());
1247 LinkNamed*l = (LinkNamed*)action;
1248 GString*name = l->getName();
1250 s = strdup(name->lowerCase()->getCString());
1251 named = name->getCString();
1254 if(strstr(s, "next") || strstr(s, "forward"))
1256 page = currentpage + 1;
1258 else if(strstr(s, "prev") || strstr(s, "back"))
1260 page = currentpage - 1;
1262 else if(strstr(s, "last") || strstr(s, "end"))
1264 if(pages && pagepos>0)
1265 page = pages[pagepos-1];
1267 else if(strstr(s, "first") || strstr(s, "top"))
1275 case actionLaunch: {
1277 LinkLaunch*l = (LinkLaunch*)action;
1278 GString * str = new GString(l->getFileName());
1279 GString * params = l->getParams();
1281 str->append(params);
1282 s = strdup(str->getCString());
1289 LinkURI*l = (LinkURI*)action;
1290 GString*g = l->getURI();
1292 url = g->getCString();
1297 case actionUnknown: {
1299 LinkUnknown*l = (LinkUnknown*)action;
1304 msg("<error> Unknown link type!\n");
1309 if(!s) s = strdup("-?-");
1311 msg("<trace> drawlink s=%s\n", s);
1313 if(!linkinfo && (page || s))
1315 msg("<notice> File contains links");
1323 for(t=1;t<=pagepos;t++) {
1324 if(pages[t]==page) {
1333 sprintf(buf, "page%d", lpage);
1334 device->drawlink(device, points, buf);
1338 device->drawlink(device, points, s);
1341 msg("<verbose> \"%s\" link to \"%s\" (%d)\n", type, FIXNULL(s), page);
1345 void GFXOutputDev::saveState(GfxState *state) {
1346 dbg("saveState");dbgindent+=2;
1348 msg("<trace> saveState\n");
1351 msg("<error> Too many nested states in pdf.");
1355 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1356 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1357 states[statepos].clipping = 0;
1360 void GFXOutputDev::restoreState(GfxState *state) {
1361 dbgindent-=2; dbg("restoreState");
1364 msg("<error> Invalid restoreState");
1367 msg("<trace> restoreState%s%s", states[statepos].softmask?" (end softmask)":"",
1368 states[statepos].clipping?" (end clipping)":"");
1369 if(states[statepos].softmask) {
1370 clearSoftMask(state);
1373 while(states[statepos].clipping) {
1374 device->endclip(device);
1375 states[statepos].clipping--;
1380 void GFXOutputDev::updateLineWidth(GfxState *state)
1382 double width = state->getTransformedLineWidth();
1383 //swfoutput_setlinewidth(&device, width);
1386 void GFXOutputDev::updateLineCap(GfxState *state)
1388 int c = state->getLineCap();
1391 void GFXOutputDev::updateLineJoin(GfxState *state)
1393 int j = state->getLineJoin();
1396 void GFXOutputDev::updateFillColor(GfxState *state)
1399 double opaq = state->getFillOpacity();
1400 state->getFillRGB(&rgb);
1402 void GFXOutputDev::updateFillOpacity(GfxState *state)
1405 double opaq = state->getFillOpacity();
1406 state->getFillRGB(&rgb);
1407 dbg("update fillopaq %f", opaq);
1409 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1411 double opaq = state->getFillOpacity();
1412 dbg("update strokeopaq %f", opaq);
1414 void GFXOutputDev::updateFillOverprint(GfxState *state)
1416 double opaq = state->getFillOverprint();
1417 dbg("update filloverprint %f", opaq);
1419 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1421 double opaq = state->getStrokeOverprint();
1422 dbg("update strokeoverprint %f", opaq);
1424 void GFXOutputDev::updateTransfer(GfxState *state)
1426 dbg("update transfer");
1430 void GFXOutputDev::updateStrokeColor(GfxState *state)
1433 double opaq = state->getStrokeOpacity();
1434 state->getStrokeRGB(&rgb);
1437 void GFXOutputDev::setXRef(PDFDoc*doc, XRef *xref)
1443 gfxfont_t* createGfxFont(GfxFont*xpdffont, FontInfo*src)
1445 gfxfont_t*font = (gfxfont_t*)malloc(sizeof(gfxfont_t));
1446 memset(font, 0, sizeof(gfxfont_t));
1448 font->glyphs = (gfxglyph_t*)malloc(sizeof(gfxglyph_t)*src->num_glyphs);
1449 memset(font->glyphs, 0, sizeof(gfxglyph_t)*src->num_glyphs);
1450 font->id = strdup(getFontID(xpdffont));
1452 double quality = (INTERNAL_FONT_SIZE * 0.05) / src->max_size;
1454 //printf("%d glyphs\n", font->num_glyphs);
1455 font->num_glyphs = 0;
1456 for(t=0;t<src->num_glyphs;t++) {
1457 if(src->glyphs[t]) {
1458 SplashPath*path = src->glyphs[t]->path;
1459 int len = path?path->getLength():0;
1460 //printf("glyph %d) %08x (%d line segments)\n", t, path, len);
1461 gfxglyph_t*glyph = &font->glyphs[font->num_glyphs];
1462 src->glyphs[t]->glyphid = font->num_glyphs;
1463 glyph->unicode = src->glyphs[t]->unicode;
1464 if(glyph->unicode >= font->max_unicode)
1465 font->max_unicode = glyph->unicode+1;
1467 gfxdrawer_target_gfxline(&drawer);
1471 for(s=0;s<len;s++) {
1474 path->getPoint(s, &x, &y, &f);
1477 if(f&splashPathFirst) {
1478 drawer.moveTo(&drawer, x*scale, y*scale);
1480 if(f&splashPathCurve) {
1482 path->getPoint(++s, &x2, &y2, &f);
1483 if(f&splashPathCurve) {
1485 path->getPoint(++s, &x3, &y3, &f);
1486 gfxdraw_cubicTo(&drawer, x*scale, y*scale, x2*scale, y2*scale, x3*scale, y3*scale, quality);
1488 drawer.splineTo(&drawer, x*scale, y*scale, x2*scale, y2*scale);
1491 drawer.lineTo(&drawer, x*scale, y*scale);
1493 // printf("%f %f %s %s\n", x, y, (f&splashPathCurve)?"curve":"",
1494 // (f&splashPathFirst)?"first":"",
1495 // (f&splashPathLast)?"last":"");
1497 glyph->line = (gfxline_t*)drawer.result(&drawer);
1498 glyph->advance = xmax*scale; // we don't know the real advance value, so this'll have to do
1502 font->unicode2glyph = (int*)malloc(sizeof(int)*font->max_unicode);
1503 memset(font->unicode2glyph, -1, sizeof(int)*font->max_unicode);
1504 for(t=0;t<font->num_glyphs;t++) {
1505 if(font->glyphs[t].unicode>0 && font->glyphs[t].unicode<font->max_unicode) {
1506 font->unicode2glyph[font->glyphs[t].unicode] = t;
1510 msg("<trace> %d glyphs.", t, font->num_glyphs);
1514 void GFXOutputDev::updateFont(GfxState *state)
1516 GfxFont* gfxFont = state->getFont();
1520 char*id = getFontID(gfxFont);
1521 msg("<verbose> Updating font to %s", id);
1522 if(gfxFont->getType() == fontType3) {
1523 infofeature("Type3 fonts");
1524 if(!config_extrafontdata) {
1529 msg("<error> Internal Error: FontID is null");
1533 this->current_fontinfo = this->info->getFont(id);
1534 if(!this->current_fontinfo->seen) {
1535 dumpFontInfo("<verbose>", gfxFont);
1538 gfxfont_t*font = gfxfontlist_findfont(this->gfxfontlist,id);
1540 font = createGfxFont(gfxFont, current_fontinfo);
1541 gfxfontlist_addfont(this->gfxfontlist, font);
1542 device->addfont(device, font);
1544 current_gfxfont = font;
1547 updateFontMatrix(state);
1550 #define SQR(x) ((x)*(x))
1552 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
1554 if((newwidth<2 || newheight<2) ||
1555 (width<=newwidth || height<=newheight))
1557 unsigned char*newdata;
1559 newdata= (unsigned char*)malloc(newwidth*newheight);
1561 double fx = (double)(width)/newwidth;
1562 double fy = (double)(height)/newheight;
1564 int blocksize = (int)(8192/(fx*fy));
1565 int r = 8192*256/palettesize;
1566 for(x=0;x<newwidth;x++) {
1567 double ex = px + fx;
1568 int fromx = (int)px;
1570 int xweight1 = (int)(((fromx+1)-px)*256);
1571 int xweight2 = (int)((ex-tox)*256);
1573 for(y=0;y<newheight;y++) {
1574 double ey = py + fy;
1575 int fromy = (int)py;
1577 int yweight1 = (int)(((fromy+1)-py)*256);
1578 int yweight2 = (int)((ey-toy)*256);
1581 for(xx=fromx;xx<=tox;xx++)
1582 for(yy=fromy;yy<=toy;yy++) {
1583 int b = 1-data[width*yy+xx];
1585 if(xx==fromx) weight = (weight*xweight1)/256;
1586 if(xx==tox) weight = (weight*xweight2)/256;
1587 if(yy==fromy) weight = (weight*yweight1)/256;
1588 if(yy==toy) weight = (weight*yweight2)/256;
1591 //if(a) a=(palettesize-1)*r/blocksize;
1592 newdata[y*newwidth+x] = (a*blocksize)/r;
1600 #define IMAGE_TYPE_JPEG 0
1601 #define IMAGE_TYPE_LOSSLESS 1
1603 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
1604 double x1,double y1,
1605 double x2,double y2,
1606 double x3,double y3,
1607 double x4,double y4, int type)
1609 gfxcolor_t*newpic=0;
1611 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
1612 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
1614 gfxline_t p1,p2,p3,p4,p5;
1615 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
1616 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
1617 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
1618 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
1619 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
1621 {p1.x = (int)(p1.x*20)/20.0;
1622 p1.y = (int)(p1.y*20)/20.0;
1623 p2.x = (int)(p2.x*20)/20.0;
1624 p2.y = (int)(p2.y*20)/20.0;
1625 p3.x = (int)(p3.x*20)/20.0;
1626 p3.y = (int)(p3.y*20)/20.0;
1627 p4.x = (int)(p4.x*20)/20.0;
1628 p4.y = (int)(p4.y*20)/20.0;
1629 p5.x = (int)(p5.x*20)/20.0;
1630 p5.y = (int)(p5.y*20)/20.0;
1637 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
1638 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
1643 img.data = (gfxcolor_t*)data;
1647 if(type == IMAGE_TYPE_JPEG)
1648 /* TODO: pass image_dpi to device instead */
1649 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
1651 dev->fillbitmap(dev, &p1, &img, &m, 0);
1654 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
1655 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
1657 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG);
1660 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
1661 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
1663 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS);
1667 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
1668 int width, int height, GfxImageColorMap*colorMap, GBool invert,
1669 GBool inlineImg, int mask, int*maskColors,
1670 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
1672 double x1,y1,x2,y2,x3,y3,x4,y4;
1673 ImageStream *imgStr;
1678 unsigned char* maskbitmap = 0;
1681 ncomps = colorMap->getNumPixelComps();
1682 bits = colorMap->getBits();
1687 unsigned char buf[8];
1688 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
1690 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
1691 imgMaskStr->reset();
1692 unsigned char pal[256];
1693 int n = 1 << colorMap->getBits();
1698 maskColorMap->getGray(pixBuf, &gray);
1699 pal[t] = colToByte(gray);
1701 for (y = 0; y < maskHeight; y++) {
1702 for (x = 0; x < maskWidth; x++) {
1703 imgMaskStr->getPixel(buf);
1704 maskbitmap[y*maskWidth+x] = pal[buf[0]];
1709 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
1710 imgMaskStr->reset();
1711 for (y = 0; y < maskHeight; y++) {
1712 for (x = 0; x < maskWidth; x++) {
1713 imgMaskStr->getPixel(buf);
1715 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
1723 imgStr = new ImageStream(str, width, ncomps,bits);
1726 if(!width || !height || (height<=1 && width<=1))
1728 msg("<verbose> Ignoring %d by %d image", width, height);
1729 unsigned char buf[8];
1731 for (y = 0; y < height; ++y)
1732 for (x = 0; x < width; ++x) {
1733 imgStr->getPixel(buf);
1741 state->transform(0, 1, &x1, &y1); x1 += user_movex + clipmovex; y1 += user_movey + clipmovey;
1742 state->transform(0, 0, &x2, &y2); x2 += user_movex + clipmovex; y2 += user_movey + clipmovey;
1743 state->transform(1, 0, &x3, &y3); x3 += user_movex + clipmovex; y3 += user_movey + clipmovey;
1744 state->transform(1, 1, &x4, &y4); x4 += user_movex + clipmovex; y4 += user_movey + clipmovey;
1746 if(!pbminfo && !(str->getKind()==strDCT)) {
1748 msg("<notice> file contains pbm pictures %s",mask?"(masked)":"");
1752 msg("<verbose> drawing %d by %d masked picture\n", width, height);
1754 if(!jpeginfo && (str->getKind()==strDCT)) {
1755 msg("<notice> file contains jpeg pictures");
1761 unsigned char buf[8];
1763 unsigned char*pic = new unsigned char[width*height];
1764 gfxcolor_t pal[256];
1766 state->getFillRGB(&rgb);
1768 memset(pal,255,sizeof(pal));
1769 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
1770 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
1771 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
1772 pal[0].a = 255; pal[1].a = 0;
1775 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
1776 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
1777 for (y = 0; y < height; ++y)
1778 for (x = 0; x < width; ++x)
1780 imgStr->getPixel(buf);
1783 pic[width*y+x] = buf[0];
1786 /* the size of the drawn image is added to the identifier
1787 as the same image may require different bitmaps if displayed
1788 at different sizes (due to antialiasing): */
1791 unsigned char*pic2 = 0;
1794 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
1803 height = realheight;
1807 /* make a black/white palette */
1809 float r = 255/(numpalette-1);
1811 for(t=0;t<numpalette;t++) {
1812 pal[t].r = colToByte(rgb.r);
1813 pal[t].g = colToByte(rgb.g);
1814 pal[t].b = colToByte(rgb.b);
1815 pal[t].a = (unsigned char)(t*r);
1819 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
1820 for (y = 0; y < height; ++y) {
1821 for (x = 0; x < width; ++x) {
1822 pic2[width*y+x] = pal[pic[y*width+x]];
1825 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1829 if(maskbitmap) free(maskbitmap);
1835 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
1836 gfxcolor_t*pic=new gfxcolor_t[width*height];
1837 for (y = 0; y < height; ++y) {
1838 for (x = 0; x < width; ++x) {
1839 imgStr->getPixel(pixBuf);
1840 colorMap->getRGB(pixBuf, &rgb);
1841 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
1842 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
1843 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
1844 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
1846 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
1850 if(str->getKind()==strDCT)
1851 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1853 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1856 if(maskbitmap) free(maskbitmap);
1859 gfxcolor_t*pic=new gfxcolor_t[width*height];
1860 gfxcolor_t pal[256];
1861 int n = 1 << colorMap->getBits();
1863 for(t=0;t<256;t++) {
1865 colorMap->getRGB(pixBuf, &rgb);
1867 {/*if(maskColors && *maskColors==t) {
1868 msg("<notice> Color %d is transparent", t);
1869 if (imgData->maskColors) {
1871 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
1872 if (pix[i] < imgData->maskColors[2*i] ||
1873 pix[i] > imgData->maskColors[2*i+1]) {
1888 pal[t].r = (unsigned char)(colToByte(rgb.r));
1889 pal[t].g = (unsigned char)(colToByte(rgb.g));
1890 pal[t].b = (unsigned char)(colToByte(rgb.b));
1891 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
1894 for (y = 0; y < height; ++y) {
1895 for (x = 0; x < width; ++x) {
1896 imgStr->getPixel(pixBuf);
1897 pic[width*y+x] = pal[pixBuf[0]];
1899 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
1903 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1907 if(maskbitmap) free(maskbitmap);
1912 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
1913 int width, int height, GBool invert,
1916 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
1917 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
1918 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
1921 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
1922 int width, int height, GfxImageColorMap *colorMap,
1923 int *maskColors, GBool inlineImg)
1925 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
1926 colorMap?"colorMap":"no colorMap",
1927 maskColors?"maskColors":"no maskColors",
1929 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
1930 colorMap?"colorMap":"no colorMap",
1931 maskColors?"maskColors":"no maskColors",
1934 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
1935 colorMap->getBits(),colorMap->getColorSpace()->getMode());
1936 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
1939 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
1940 int width, int height,
1941 GfxImageColorMap *colorMap,
1942 Stream *maskStr, int maskWidth, int maskHeight,
1945 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
1946 colorMap?"colorMap":"no colorMap",
1947 maskWidth, maskHeight);
1948 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
1949 colorMap?"colorMap":"no colorMap",
1950 maskWidth, maskHeight);
1952 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
1953 colorMap->getBits(),colorMap->getColorSpace()->getMode());
1954 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
1957 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
1958 int width, int height,
1959 GfxImageColorMap *colorMap,
1961 int maskWidth, int maskHeight,
1962 GfxImageColorMap *maskColorMap)
1964 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
1965 colorMap?"colorMap":"no colorMap",
1966 maskWidth, maskHeight);
1967 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
1968 colorMap?"colorMap":"no colorMap",
1969 maskWidth, maskHeight);
1971 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
1972 colorMap->getBits(),colorMap->getColorSpace()->getMode());
1973 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
1976 void GFXOutputDev::stroke(GfxState *state)
1980 GfxPath * path = state->getPath();
1981 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
1982 strokeGfxline(state, line, 0);
1986 void GFXOutputDev::fill(GfxState *state)
1988 gfxcolor_t col = getFillColor(state);
1989 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
1991 GfxPath * path = state->getPath();
1992 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1993 fillGfxLine(state, line);
1997 void GFXOutputDev::eoFill(GfxState *state)
1999 gfxcolor_t col = getFillColor(state);
2000 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2002 GfxPath * path = state->getPath();
2003 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2004 fillGfxLine(state, line);
2009 static const char* dirseparator()
2018 void addGlobalFont(const char*filename)
2021 memset(&f, 0, sizeof(fontfile_t));
2022 f.filename = filename;
2023 if(fontnum < sizeof(fonts)/sizeof(fonts[0])) {
2024 msg("<verbose> Adding font \"%s\".", filename);
2025 msg("<warning> External fonts are not supported with this version. Ignoring font %s", filename);
2026 fonts[fontnum++] = f;
2028 msg("<error> Too many external fonts. Not adding font file \"%s\".", filename);
2032 void addGlobalLanguageDir(const char*dir)
2034 msg("<notice> Adding %s to language pack directories", dir);
2038 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2039 strcpy(config_file, dir);
2040 strcat(config_file, dirseparator());
2041 strcat(config_file, "add-to-xpdfrc");
2043 fi = fopen(config_file, "rb");
2045 msg("<error> Could not open %s", config_file);
2048 globalParams->parseFile(new GString(config_file), fi);
2052 void addGlobalFontDir(const char*dirname)
2054 msg("<warning> External fonts are not supported with this version. Ignoring directory %s", dirname);
2056 #ifdef HAVE_DIRENT_H
2057 msg("<notice> Adding %s to font directories", dirname);
2058 lastfontdir = strdup(dirname);
2059 DIR*dir = opendir(dirname);
2061 msg("<warning> Couldn't open directory %s\n", dirname);
2066 ent = readdir (dir);
2070 char*name = ent->d_name;
2076 if(!strncasecmp(&name[l-4], ".pfa", 4))
2078 if(!strncasecmp(&name[l-4], ".pfb", 4))
2080 if(!strncasecmp(&name[l-4], ".ttf", 4))
2084 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2085 strcpy(fontname, dirname);
2086 strcat(fontname, dirseparator());
2087 strcat(fontname, name);
2088 addGlobalFont(fontname);
2093 msg("<warning> No dirent.h- unable to add font dir %s", dir);
2097 void GFXOutputDev::preparePage(int pdfpage, int outputpage)
2103 this->pagebuflen = 1024;
2104 this->pages = (int*)malloc(this->pagebuflen*sizeof(int));
2105 memset(this->pages, -1, this->pagebuflen*sizeof(int));
2107 while(pdfpage >= this->pagebuflen)
2109 int oldlen = this->pagebuflen;
2110 this->pagebuflen+=1024;
2111 this->pages = (int*)realloc(this->pages, this->pagebuflen*sizeof(int));
2112 memset(&this->pages[oldlen], -1, (this->pagebuflen-oldlen)*sizeof(int));
2115 this->pages[pdfpage] = outputpage;
2116 if(pdfpage>this->pagepos)
2117 this->pagepos = pdfpage;
2123 double width,height;
2126 BBox mkBBox(GfxState*state, double*bbox, double width, double height)
2128 double xMin, yMin, xMax, yMax, x, y;
2129 double tx, ty, w, h;
2130 // transform the bbox
2131 state->transform(bbox[0], bbox[1], &x, &y);
2134 state->transform(bbox[0], bbox[3], &x, &y);
2137 } else if (x > xMax) {
2142 } else if (y > yMax) {
2145 state->transform(bbox[2], bbox[1], &x, &y);
2148 } else if (x > xMax) {
2153 } else if (y > yMax) {
2156 state->transform(bbox[2], bbox[3], &x, &y);
2159 } else if (x > xMax) {
2164 } else if (y > yMax) {
2167 tx = (int)floor(xMin);
2170 } else if (tx > width) {
2173 ty = (int)floor(yMin);
2176 } else if (ty > height) {
2179 w = (int)ceil(xMax) - tx + 1;
2180 if (tx + w > width) {
2186 h = (int)ceil(yMax) - ty + 1;
2187 if (ty + h > height) {
2201 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2202 GfxColorSpace *blendingColorSpace,
2203 GBool isolated, GBool knockout,
2206 const char*colormodename = "";
2207 BBox rect = mkBBox(state, bbox, this->width, this->height);
2209 if(blendingColorSpace) {
2210 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2212 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);
2213 dbg("using clipping rect %f/%f/%f/%f\n", rect.posx,rect.posy,rect.width,rect.height);
2214 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);
2216 states[statepos].createsoftmask |= forSoftMask;
2217 states[statepos].transparencygroup = !forSoftMask;
2218 states[statepos].isolated = isolated;
2220 states[statepos].olddevice = this->device;
2221 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2223 gfxdevice_record_init(this->device);
2225 /*if(!forSoftMask) { ////???
2226 state->setFillOpacity(0.0);
2231 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2234 dbg("endTransparencyGroup");
2235 msg("<verbose> endTransparencyGroup");
2237 gfxdevice_t*r = this->device;
2239 this->device = states[statepos].olddevice;
2241 if(states[statepos].createsoftmask) {
2242 states[statepos-1].softmaskrecording = r->finish(r);
2244 states[statepos-1].grouprecording = r->finish(r);
2247 states[statepos].createsoftmask = 0;
2248 states[statepos].transparencygroup = 0;
2252 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2254 const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2255 "colordodge","colorburn","hardlight","softlight","difference",
2256 "exclusion","hue","saturation","color","luminosity"};
2258 dbg("paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2259 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2261 if(state->getBlendMode() == gfxBlendNormal)
2262 infofeature("transparency groups");
2265 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2266 warnfeature(buffer, 0);
2269 gfxresult_t*grouprecording = states[statepos].grouprecording;
2271 if(state->getBlendMode() == gfxBlendNormal) {
2273 gfxdevice_ops_init(&ops, this->device, (unsigned char)(state->getFillOpacity()*255));
2274 gfxresult_record_replay(grouprecording, &ops);
2277 grouprecording->destroy(grouprecording);
2279 states[statepos].grouprecording = 0;
2282 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2284 /* alpha = 1: retrieve mask values from alpha layer
2285 alpha = 0: retrieve mask values from luminance */
2286 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2287 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2288 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2289 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2291 infofeature("soft masks");
2293 warnfeature("soft masks from alpha channel",0);
2295 states[statepos].olddevice = this->device;
2296 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2297 gfxdevice_record_init(this->device);
2299 dbg("softmaskrecording is %08x at statepos %d\n", states[statepos].softmaskrecording, statepos);
2301 states[statepos].softmask = 1;
2302 states[statepos].softmask_alpha = alpha;
2305 static inline Guchar div255(int x) {
2306 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2309 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2311 if(c < min) c = min;
2312 if(c > max) c = max;
2316 void GFXOutputDev::clearSoftMask(GfxState *state)
2318 if(!states[statepos].softmask)
2320 states[statepos].softmask = 0;
2321 dbg("clearSoftMask statepos=%d", statepos);
2322 msg("<verbose> clearSoftMask");
2324 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2325 msg("<error> Error in softmask/tgroup ordering");
2329 gfxresult_t*mask = states[statepos].softmaskrecording;
2330 gfxresult_t*below = this->device->finish(this->device);
2331 this->device = states[statepos].olddevice;
2333 /* get outline of all objects below the soft mask */
2334 gfxdevice_t uniondev;
2335 gfxdevice_union_init(&uniondev, 0);
2336 gfxresult_record_replay(below, &uniondev);
2337 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2338 uniondev.finish(&uniondev);
2340 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2342 this->device->startclip(this->device, belowoutline);
2343 gfxresult_record_replay(below, this->device);
2344 gfxresult_record_replay(mask, this->device);
2345 this->device->endclip(this->device);
2346 gfxline_free(belowoutline);
2349 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2350 if(width<=0 || height<=0)
2353 gfxdevice_t belowrender;
2354 gfxdevice_render_init(&belowrender);
2355 if(states[statepos+1].isolated) {
2356 belowrender.setparameter(&belowrender, "fillwhite", "1");
2358 belowrender.setparameter(&belowrender, "antialize", "2");
2359 belowrender.startpage(&belowrender, width, height);
2360 gfxresult_record_replay(below, &belowrender);
2361 belowrender.endpage(&belowrender);
2362 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2363 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2364 //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2366 gfxdevice_t maskrender;
2367 gfxdevice_render_init(&maskrender);
2368 maskrender.startpage(&maskrender, width, height);
2369 gfxresult_record_replay(mask, &maskrender);
2370 maskrender.endpage(&maskrender);
2371 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2372 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2374 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2375 msg("<fatal> Internal error in mask drawing");
2380 for(y=0;y<height;y++) {
2381 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2382 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2383 for(x=0;x<width;x++) {
2385 if(states[statepos].softmask_alpha) {
2388 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2391 l2->a = div255(alpha*l2->a);
2393 /* DON'T premultiply alpha- this is done by fillbitmap,
2394 depending on the output device */
2395 //l2->r = div255(alpha*l2->r);
2396 //l2->g = div255(alpha*l2->g);
2397 //l2->b = div255(alpha*l2->b);
2403 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2406 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2407 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2409 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2411 mask->destroy(mask);
2412 below->destroy(below);
2413 maskresult->destroy(maskresult);
2414 belowresult->destroy(belowresult);
2415 states[statepos].softmaskrecording = 0;
2420 // public: ~MemCheck()
2422 // delete globalParams;globalParams=0;
2423 // Object::memCheck(stderr);
2424 // gMemReport(stderr);