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\n",x,y,(charid&127)>=32?charid:'?', charid, u, uLen, font->isCIDFont(), render);
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(getFontName(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;
1513 void GFXOutputDev::updateFont(GfxState *state)
1515 GfxFont* gfxFont = state->getFont();
1519 char*id = getFontID(gfxFont);
1520 msg("<verbose> Updating font to %s", id);
1521 if(gfxFont->getType() == fontType3) {
1522 infofeature("Type3 fonts");
1523 if(!config_extrafontdata) {
1528 msg("<error> Internal Error: FontID is null");
1532 this->current_fontinfo = this->info->getFont(id);
1533 if(!this->current_fontinfo->seen) {
1534 dumpFontInfo("<verbose>", gfxFont);
1537 gfxfont_t*font = gfxfontlist_findfont(this->gfxfontlist,id);
1539 font = createGfxFont(gfxFont, current_fontinfo);
1540 gfxfontlist_addfont(this->gfxfontlist, font);
1541 device->addfont(device, font);
1543 current_gfxfont = font;
1546 updateFontMatrix(state);
1549 #define SQR(x) ((x)*(x))
1551 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
1553 if((newwidth<2 || newheight<2) ||
1554 (width<=newwidth || height<=newheight))
1556 unsigned char*newdata;
1558 newdata= (unsigned char*)malloc(newwidth*newheight);
1560 double fx = (double)(width)/newwidth;
1561 double fy = (double)(height)/newheight;
1563 int blocksize = (int)(8192/(fx*fy));
1564 int r = 8192*256/palettesize;
1565 for(x=0;x<newwidth;x++) {
1566 double ex = px + fx;
1567 int fromx = (int)px;
1569 int xweight1 = (int)(((fromx+1)-px)*256);
1570 int xweight2 = (int)((ex-tox)*256);
1572 for(y=0;y<newheight;y++) {
1573 double ey = py + fy;
1574 int fromy = (int)py;
1576 int yweight1 = (int)(((fromy+1)-py)*256);
1577 int yweight2 = (int)((ey-toy)*256);
1580 for(xx=fromx;xx<=tox;xx++)
1581 for(yy=fromy;yy<=toy;yy++) {
1582 int b = 1-data[width*yy+xx];
1584 if(xx==fromx) weight = (weight*xweight1)/256;
1585 if(xx==tox) weight = (weight*xweight2)/256;
1586 if(yy==fromy) weight = (weight*yweight1)/256;
1587 if(yy==toy) weight = (weight*yweight2)/256;
1590 //if(a) a=(palettesize-1)*r/blocksize;
1591 newdata[y*newwidth+x] = (a*blocksize)/r;
1599 #define IMAGE_TYPE_JPEG 0
1600 #define IMAGE_TYPE_LOSSLESS 1
1602 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
1603 double x1,double y1,
1604 double x2,double y2,
1605 double x3,double y3,
1606 double x4,double y4, int type)
1608 gfxcolor_t*newpic=0;
1610 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
1611 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
1613 gfxline_t p1,p2,p3,p4,p5;
1614 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
1615 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
1616 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
1617 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
1618 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
1620 {p1.x = (int)(p1.x*20)/20.0;
1621 p1.y = (int)(p1.y*20)/20.0;
1622 p2.x = (int)(p2.x*20)/20.0;
1623 p2.y = (int)(p2.y*20)/20.0;
1624 p3.x = (int)(p3.x*20)/20.0;
1625 p3.y = (int)(p3.y*20)/20.0;
1626 p4.x = (int)(p4.x*20)/20.0;
1627 p4.y = (int)(p4.y*20)/20.0;
1628 p5.x = (int)(p5.x*20)/20.0;
1629 p5.y = (int)(p5.y*20)/20.0;
1636 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
1637 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
1642 img.data = (gfxcolor_t*)data;
1646 if(type == IMAGE_TYPE_JPEG)
1647 /* TODO: pass image_dpi to device instead */
1648 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
1650 dev->fillbitmap(dev, &p1, &img, &m, 0);
1653 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
1654 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
1656 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG);
1659 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
1660 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
1662 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS);
1666 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
1667 int width, int height, GfxImageColorMap*colorMap, GBool invert,
1668 GBool inlineImg, int mask, int*maskColors,
1669 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
1671 double x1,y1,x2,y2,x3,y3,x4,y4;
1672 ImageStream *imgStr;
1677 unsigned char* maskbitmap = 0;
1680 ncomps = colorMap->getNumPixelComps();
1681 bits = colorMap->getBits();
1686 unsigned char buf[8];
1687 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
1689 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
1690 imgMaskStr->reset();
1691 unsigned char pal[256];
1692 int n = 1 << colorMap->getBits();
1697 maskColorMap->getGray(pixBuf, &gray);
1698 pal[t] = colToByte(gray);
1700 for (y = 0; y < maskHeight; y++) {
1701 for (x = 0; x < maskWidth; x++) {
1702 imgMaskStr->getPixel(buf);
1703 maskbitmap[y*maskWidth+x] = pal[buf[0]];
1708 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
1709 imgMaskStr->reset();
1710 for (y = 0; y < maskHeight; y++) {
1711 for (x = 0; x < maskWidth; x++) {
1712 imgMaskStr->getPixel(buf);
1714 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
1722 imgStr = new ImageStream(str, width, ncomps,bits);
1725 if(!width || !height || (height<=1 && width<=1))
1727 msg("<verbose> Ignoring %d by %d image", width, height);
1728 unsigned char buf[8];
1730 for (y = 0; y < height; ++y)
1731 for (x = 0; x < width; ++x) {
1732 imgStr->getPixel(buf);
1740 state->transform(0, 1, &x1, &y1); x1 += user_movex + clipmovex; y1 += user_movey + clipmovey;
1741 state->transform(0, 0, &x2, &y2); x2 += user_movex + clipmovex; y2 += user_movey + clipmovey;
1742 state->transform(1, 0, &x3, &y3); x3 += user_movex + clipmovex; y3 += user_movey + clipmovey;
1743 state->transform(1, 1, &x4, &y4); x4 += user_movex + clipmovex; y4 += user_movey + clipmovey;
1745 if(!pbminfo && !(str->getKind()==strDCT)) {
1747 msg("<notice> file contains pbm pictures %s",mask?"(masked)":"");
1751 msg("<verbose> drawing %d by %d masked picture\n", width, height);
1753 if(!jpeginfo && (str->getKind()==strDCT)) {
1754 msg("<notice> file contains jpeg pictures");
1760 unsigned char buf[8];
1762 unsigned char*pic = new unsigned char[width*height];
1763 gfxcolor_t pal[256];
1765 state->getFillRGB(&rgb);
1767 memset(pal,255,sizeof(pal));
1768 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
1769 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
1770 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
1771 pal[0].a = 255; pal[1].a = 0;
1774 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
1775 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
1776 for (y = 0; y < height; ++y)
1777 for (x = 0; x < width; ++x)
1779 imgStr->getPixel(buf);
1782 pic[width*y+x] = buf[0];
1785 /* the size of the drawn image is added to the identifier
1786 as the same image may require different bitmaps if displayed
1787 at different sizes (due to antialiasing): */
1790 unsigned char*pic2 = 0;
1793 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
1802 height = realheight;
1806 /* make a black/white palette */
1808 float r = 255/(numpalette-1);
1810 for(t=0;t<numpalette;t++) {
1811 pal[t].r = colToByte(rgb.r);
1812 pal[t].g = colToByte(rgb.g);
1813 pal[t].b = colToByte(rgb.b);
1814 pal[t].a = (unsigned char)(t*r);
1818 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
1819 for (y = 0; y < height; ++y) {
1820 for (x = 0; x < width; ++x) {
1821 pic2[width*y+x] = pal[pic[y*width+x]];
1824 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1828 if(maskbitmap) free(maskbitmap);
1834 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
1835 gfxcolor_t*pic=new gfxcolor_t[width*height];
1836 for (y = 0; y < height; ++y) {
1837 for (x = 0; x < width; ++x) {
1838 imgStr->getPixel(pixBuf);
1839 colorMap->getRGB(pixBuf, &rgb);
1840 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
1841 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
1842 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
1843 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
1845 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
1849 if(str->getKind()==strDCT)
1850 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1852 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1855 if(maskbitmap) free(maskbitmap);
1858 gfxcolor_t*pic=new gfxcolor_t[width*height];
1859 gfxcolor_t pal[256];
1860 int n = 1 << colorMap->getBits();
1862 for(t=0;t<256;t++) {
1864 colorMap->getRGB(pixBuf, &rgb);
1866 {/*if(maskColors && *maskColors==t) {
1867 msg("<notice> Color %d is transparent", t);
1868 if (imgData->maskColors) {
1870 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
1871 if (pix[i] < imgData->maskColors[2*i] ||
1872 pix[i] > imgData->maskColors[2*i+1]) {
1887 pal[t].r = (unsigned char)(colToByte(rgb.r));
1888 pal[t].g = (unsigned char)(colToByte(rgb.g));
1889 pal[t].b = (unsigned char)(colToByte(rgb.b));
1890 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
1893 for (y = 0; y < height; ++y) {
1894 for (x = 0; x < width; ++x) {
1895 imgStr->getPixel(pixBuf);
1896 pic[width*y+x] = pal[pixBuf[0]];
1898 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
1902 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1906 if(maskbitmap) free(maskbitmap);
1911 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
1912 int width, int height, GBool invert,
1915 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
1916 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
1917 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
1920 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
1921 int width, int height, GfxImageColorMap *colorMap,
1922 int *maskColors, GBool inlineImg)
1924 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
1925 colorMap?"colorMap":"no colorMap",
1926 maskColors?"maskColors":"no maskColors",
1928 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
1929 colorMap?"colorMap":"no colorMap",
1930 maskColors?"maskColors":"no maskColors",
1933 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
1934 colorMap->getBits(),colorMap->getColorSpace()->getMode());
1935 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
1938 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
1939 int width, int height,
1940 GfxImageColorMap *colorMap,
1941 Stream *maskStr, int maskWidth, int maskHeight,
1944 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
1945 colorMap?"colorMap":"no colorMap",
1946 maskWidth, maskHeight);
1947 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
1948 colorMap?"colorMap":"no colorMap",
1949 maskWidth, maskHeight);
1951 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
1952 colorMap->getBits(),colorMap->getColorSpace()->getMode());
1953 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
1956 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
1957 int width, int height,
1958 GfxImageColorMap *colorMap,
1960 int maskWidth, int maskHeight,
1961 GfxImageColorMap *maskColorMap)
1963 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
1964 colorMap?"colorMap":"no colorMap",
1965 maskWidth, maskHeight);
1966 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
1967 colorMap?"colorMap":"no colorMap",
1968 maskWidth, maskHeight);
1970 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
1971 colorMap->getBits(),colorMap->getColorSpace()->getMode());
1972 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
1975 void GFXOutputDev::stroke(GfxState *state)
1979 GfxPath * path = state->getPath();
1980 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
1981 strokeGfxline(state, line, 0);
1985 void GFXOutputDev::fill(GfxState *state)
1987 gfxcolor_t col = getFillColor(state);
1988 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
1990 GfxPath * path = state->getPath();
1991 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1992 fillGfxLine(state, line);
1996 void GFXOutputDev::eoFill(GfxState *state)
1998 gfxcolor_t col = getFillColor(state);
1999 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2001 GfxPath * path = state->getPath();
2002 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2003 fillGfxLine(state, line);
2008 static const char* dirseparator()
2017 void addGlobalFont(const char*filename)
2020 memset(&f, 0, sizeof(fontfile_t));
2021 f.filename = filename;
2022 if(fontnum < sizeof(fonts)/sizeof(fonts[0])) {
2023 msg("<verbose> Adding font \"%s\".", filename);
2024 msg("<warning> External fonts are not supported with this version. Ignoring font %s", filename);
2025 fonts[fontnum++] = f;
2027 msg("<error> Too many external fonts. Not adding font file \"%s\".", filename);
2031 void addGlobalLanguageDir(const char*dir)
2033 msg("<notice> Adding %s to language pack directories", dir);
2037 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2038 strcpy(config_file, dir);
2039 strcat(config_file, dirseparator());
2040 strcat(config_file, "add-to-xpdfrc");
2042 fi = fopen(config_file, "rb");
2044 msg("<error> Could not open %s", config_file);
2047 globalParams->parseFile(new GString(config_file), fi);
2051 void addGlobalFontDir(const char*dirname)
2053 msg("<warning> External fonts are not supported with this version. Ignoring directory %s", dirname);
2055 #ifdef HAVE_DIRENT_H
2056 msg("<notice> Adding %s to font directories", dirname);
2057 lastfontdir = strdup(dirname);
2058 DIR*dir = opendir(dirname);
2060 msg("<warning> Couldn't open directory %s\n", dirname);
2065 ent = readdir (dir);
2069 char*name = ent->d_name;
2075 if(!strncasecmp(&name[l-4], ".pfa", 4))
2077 if(!strncasecmp(&name[l-4], ".pfb", 4))
2079 if(!strncasecmp(&name[l-4], ".ttf", 4))
2083 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2084 strcpy(fontname, dirname);
2085 strcat(fontname, dirseparator());
2086 strcat(fontname, name);
2087 addGlobalFont(fontname);
2092 msg("<warning> No dirent.h- unable to add font dir %s", dir);
2096 void GFXOutputDev::preparePage(int pdfpage, int outputpage)
2102 this->pagebuflen = 1024;
2103 this->pages = (int*)malloc(this->pagebuflen*sizeof(int));
2104 memset(this->pages, -1, this->pagebuflen*sizeof(int));
2106 while(pdfpage >= this->pagebuflen)
2108 int oldlen = this->pagebuflen;
2109 this->pagebuflen+=1024;
2110 this->pages = (int*)realloc(this->pages, this->pagebuflen*sizeof(int));
2111 memset(&this->pages[oldlen], -1, (this->pagebuflen-oldlen)*sizeof(int));
2114 this->pages[pdfpage] = outputpage;
2115 if(pdfpage>this->pagepos)
2116 this->pagepos = pdfpage;
2122 double width,height;
2125 BBox mkBBox(GfxState*state, double*bbox, double width, double height)
2127 double xMin, yMin, xMax, yMax, x, y;
2128 double tx, ty, w, h;
2129 // transform the bbox
2130 state->transform(bbox[0], bbox[1], &x, &y);
2133 state->transform(bbox[0], bbox[3], &x, &y);
2136 } else if (x > xMax) {
2141 } else if (y > yMax) {
2144 state->transform(bbox[2], bbox[1], &x, &y);
2147 } else if (x > xMax) {
2152 } else if (y > yMax) {
2155 state->transform(bbox[2], bbox[3], &x, &y);
2158 } else if (x > xMax) {
2163 } else if (y > yMax) {
2166 tx = (int)floor(xMin);
2169 } else if (tx > width) {
2172 ty = (int)floor(yMin);
2175 } else if (ty > height) {
2178 w = (int)ceil(xMax) - tx + 1;
2179 if (tx + w > width) {
2185 h = (int)ceil(yMax) - ty + 1;
2186 if (ty + h > height) {
2200 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2201 GfxColorSpace *blendingColorSpace,
2202 GBool isolated, GBool knockout,
2205 const char*colormodename = "";
2206 BBox rect = mkBBox(state, bbox, this->width, this->height);
2208 if(blendingColorSpace) {
2209 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2211 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);
2212 dbg("using clipping rect %f/%f/%f/%f\n", rect.posx,rect.posy,rect.width,rect.height);
2213 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);
2215 states[statepos].createsoftmask |= forSoftMask;
2216 states[statepos].transparencygroup = !forSoftMask;
2217 states[statepos].isolated = isolated;
2219 states[statepos].olddevice = this->device;
2220 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2222 gfxdevice_record_init(this->device);
2224 /*if(!forSoftMask) { ////???
2225 state->setFillOpacity(0.0);
2230 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2233 dbg("endTransparencyGroup");
2234 msg("<verbose> endTransparencyGroup");
2236 gfxdevice_t*r = this->device;
2238 this->device = states[statepos].olddevice;
2240 if(states[statepos].createsoftmask) {
2241 states[statepos-1].softmaskrecording = r->finish(r);
2243 states[statepos-1].grouprecording = r->finish(r);
2246 states[statepos].createsoftmask = 0;
2247 states[statepos].transparencygroup = 0;
2251 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2253 const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2254 "colordodge","colorburn","hardlight","softlight","difference",
2255 "exclusion","hue","saturation","color","luminosity"};
2257 dbg("paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2258 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2260 if(state->getBlendMode() == gfxBlendNormal)
2261 infofeature("transparency groups");
2264 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2265 warnfeature(buffer, 0);
2268 gfxresult_t*grouprecording = states[statepos].grouprecording;
2270 if(state->getBlendMode() == gfxBlendNormal) {
2272 gfxdevice_ops_init(&ops, this->device, (unsigned char)(state->getFillOpacity()*255));
2273 gfxresult_record_replay(grouprecording, &ops);
2276 grouprecording->destroy(grouprecording);
2278 states[statepos].grouprecording = 0;
2281 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2283 /* alpha = 1: retrieve mask values from alpha layer
2284 alpha = 0: retrieve mask values from luminance */
2285 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2286 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2287 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2288 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2290 infofeature("soft masks");
2292 warnfeature("soft masks from alpha channel",0);
2294 states[statepos].olddevice = this->device;
2295 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2296 gfxdevice_record_init(this->device);
2298 dbg("softmaskrecording is %08x at statepos %d\n", states[statepos].softmaskrecording, statepos);
2300 states[statepos].softmask = 1;
2301 states[statepos].softmask_alpha = alpha;
2304 static inline Guchar div255(int x) {
2305 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2308 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2310 if(c < min) c = min;
2311 if(c > max) c = max;
2315 void GFXOutputDev::clearSoftMask(GfxState *state)
2317 if(!states[statepos].softmask)
2319 states[statepos].softmask = 0;
2320 dbg("clearSoftMask statepos=%d", statepos);
2321 msg("<verbose> clearSoftMask");
2323 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2324 msg("<error> Error in softmask/tgroup ordering");
2328 gfxresult_t*mask = states[statepos].softmaskrecording;
2329 gfxresult_t*below = this->device->finish(this->device);
2330 this->device = states[statepos].olddevice;
2332 /* get outline of all objects below the soft mask */
2333 gfxdevice_t uniondev;
2334 gfxdevice_union_init(&uniondev, 0);
2335 gfxresult_record_replay(below, &uniondev);
2336 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2337 uniondev.finish(&uniondev);
2339 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2341 this->device->startclip(this->device, belowoutline);
2342 gfxresult_record_replay(below, this->device);
2343 gfxresult_record_replay(mask, this->device);
2344 this->device->endclip(this->device);
2345 gfxline_free(belowoutline);
2348 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2349 if(width<=0 || height<=0)
2352 gfxdevice_t belowrender;
2353 gfxdevice_render_init(&belowrender);
2354 if(states[statepos+1].isolated) {
2355 belowrender.setparameter(&belowrender, "fillwhite", "1");
2357 belowrender.setparameter(&belowrender, "antialize", "2");
2358 belowrender.startpage(&belowrender, width, height);
2359 gfxresult_record_replay(below, &belowrender);
2360 belowrender.endpage(&belowrender);
2361 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2362 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2363 //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2365 gfxdevice_t maskrender;
2366 gfxdevice_render_init(&maskrender);
2367 maskrender.startpage(&maskrender, width, height);
2368 gfxresult_record_replay(mask, &maskrender);
2369 maskrender.endpage(&maskrender);
2370 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2371 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2373 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2374 msg("<fatal> Internal error in mask drawing");
2379 for(y=0;y<height;y++) {
2380 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2381 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2382 for(x=0;x<width;x++) {
2384 if(states[statepos].softmask_alpha) {
2387 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2390 l2->a = div255(alpha*l2->a);
2392 /* DON'T premultiply alpha- this is done by fillbitmap,
2393 depending on the output device */
2394 //l2->r = div255(alpha*l2->r);
2395 //l2->g = div255(alpha*l2->g);
2396 //l2->b = div255(alpha*l2->b);
2402 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2405 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2406 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2408 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2410 mask->destroy(mask);
2411 below->destroy(below);
2412 maskresult->destroy(maskresult);
2413 belowresult->destroy(belowresult);
2414 states[statepos].softmaskrecording = 0;
2419 // public: ~MemCheck()
2421 // delete globalParams;globalParams=0;
2422 // Object::memCheck(stderr);
2423 // gMemReport(stderr);