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 */
28 #include "../../config.h"
33 #ifdef HAVE_SYS_STAT_H
51 #include "OutputDev.h"
54 #include "CharCodeToUnicode.h"
55 #include "NameToUnicodeTable.h"
56 #include "GlobalParams.h"
57 #include "FoFiType1C.h"
58 #include "FoFiTrueType.h"
60 #include "GFXOutputDev.h"
62 // swftools header files
64 #include "../gfxdevice.h"
65 #include "../gfxtools.h"
66 #include "../gfxfont.h"
67 #include "../devices/record.h"
68 #include "../devices/ops.h"
69 #include "../devices/arts.h"
70 #include "../devices/render.h"
72 #include "../art/libart.h"
73 #include "../devices/artsutils.h"
80 typedef struct _fontfile
83 int len; // basename length
85 struct _fontfile*next;
90 static fontfile_t* global_fonts = 0;
91 static fontfile_t* global_fonts_next = 0;
93 static int fontnum = 0;
97 static char* lastfontdir = 0;
108 {"Times-Roman", "n021003l", n021003l_afm, n021003l_afm_len, n021003l_pfb, n021003l_pfb_len},
109 {"Times-Italic", "n021023l", n021023l_afm, n021023l_afm_len, n021023l_pfb, n021023l_pfb_len},
110 {"Times-Bold", "n021004l", n021004l_afm, n021004l_afm_len, n021004l_pfb, n021004l_pfb_len},
111 {"Times-BoldItalic", "n021024l", n021024l_afm, n021024l_afm_len, n021024l_pfb, n021024l_pfb_len},
112 {"Helvetica", "n019003l", n019003l_afm, n019003l_afm_len, n019003l_pfb, n019003l_pfb_len},
113 {"Helvetica-Oblique", "n019023l", n019023l_afm, n019023l_afm_len, n019023l_pfb, n019023l_pfb_len},
114 {"Helvetica-Bold", "n019004l", n019004l_afm, n019004l_afm_len, n019004l_pfb, n019004l_pfb_len},
115 {"Helvetica-BoldOblique", "n019024l", n019024l_afm, n019024l_afm_len, n019024l_pfb, n019024l_pfb_len},
116 {"Courier", "n022003l", n022003l_afm, n022003l_afm_len, n022003l_pfb, n022003l_pfb_len},
117 {"Courier-Oblique", "n022023l", n022023l_afm, n022023l_afm_len, n022023l_pfb, n022023l_pfb_len},
118 {"Courier-Bold", "n022004l", n022004l_afm, n022004l_afm_len, n022004l_pfb, n022004l_pfb_len},
119 {"Courier-BoldOblique", "n022024l", n022024l_afm, n022024l_afm_len, n022024l_pfb, n022024l_pfb_len},
120 {"Symbol", "s050000l", s050000l_afm, s050000l_afm_len, s050000l_pfb, s050000l_pfb_len},
121 {"ZapfDingbats", "d050000l", d050000l_afm, d050000l_afm_len, d050000l_pfb, d050000l_pfb_len}};
124 static int verbose = 0;
125 static int dbgindent = 0;
126 static void dbg(const char*format, ...)
133 va_start(arglist, format);
134 vsprintf(buf, format, arglist);
137 while(l && buf[l-1]=='\n') {
142 int indent = dbgindent;
152 typedef struct _feature
155 struct _feature*next;
157 feature_t*featurewarnings = 0;
159 void GFXOutputDev::showfeature(const char*feature,char fully, char warn)
161 feature_t*f = featurewarnings;
163 if(!strcmp(feature, f->string))
167 f = (feature_t*)malloc(sizeof(feature_t));
168 f->string = strdup(feature);
169 f->next = featurewarnings;
172 msg("<warning> %s not yet %ssupported!",feature,fully?"fully ":"");
173 if(this->config_break_on_warning) {
174 msg("<fatal> Aborting conversion due to unsupported feature");
178 msg("<notice> File contains %s",feature);
181 void GFXOutputDev::warnfeature(const char*feature,char fully)
183 showfeature(feature,fully,1);
185 void GFXOutputDev::infofeature(const char*feature)
187 showfeature(feature,0,0);
190 GFXOutputState::GFXOutputState() {
192 this->createsoftmask = 0;
193 this->transparencygroup = 0;
195 this->grouprecording = 0;
199 GBool GFXOutputDev::interpretType3Chars()
204 typedef struct _drawnchar
222 chars = (drawnchar_t*)malloc(sizeof(drawnchar_t)*buf_size);
223 memset(chars, 0, sizeof(drawnchar_t)*buf_size);
228 free(chars);chars = 0;
235 chars = (drawnchar_t*)realloc(chars, sizeof(drawnchar_t)*buf_size);
239 void addChar(int charid, gfxcoord_t x, gfxcoord_t y, gfxcolor_t color)
242 chars[num_chars].x = x;
243 chars[num_chars].y = y;
244 chars[num_chars].color = color;
245 chars[num_chars].charid = charid;
249 char* writeOutStdFont(fontentry* f)
254 char* tmpFileName = mktmpname(namebuf1);
256 sprintf(namebuf2, "%s.afm", tmpFileName);
257 fi = fopen(namebuf2, "wb");
260 fwrite(f->afm, 1, f->afmlen, fi);
263 sprintf(namebuf2, "%s.pfb", tmpFileName);
264 fi = fopen(namebuf2, "wb");
267 fwrite(f->pfb, 1, f->pfblen, fi);
269 return strdup(namebuf2);
271 void unlinkfont(char* filename)
276 msg("<verbose> Removing temporary font file %s", filename);
279 if(!strncmp(&filename[l-4],".afm",4)) {
280 memcpy(&filename[l-4],".pfb",4); unlink(filename);
281 memcpy(&filename[l-4],".pfa",4); unlink(filename);
282 memcpy(&filename[l-4],".afm",4);
285 if(!strncmp(&filename[l-4],".pfa",4)) {
286 memcpy(&filename[l-4],".afm",4); unlink(filename);
287 memcpy(&filename[l-4],".pfa",4);
290 if(!strncmp(&filename[l-4],".pfb",4)) {
291 memcpy(&filename[l-4],".afm",4); unlink(filename);
292 memcpy(&filename[l-4],".pfb",4);
298 GFXGlobalParams::GFXGlobalParams()
301 //setupBaseFonts(char *dir); //not tested yet
303 GFXGlobalParams::~GFXGlobalParams()
305 msg("<verbose> Performing cleanups");
307 for(t=0;t<sizeof(pdf2t1map)/sizeof(fontentry);t++) {
308 if(pdf2t1map[t].fullfilename) {
309 unlinkfont(pdf2t1map[t].fullfilename);
313 DisplayFontParam *GFXGlobalParams::getDisplayFont(GString *fontName)
315 msg("<verbose> looking for font %s in global params\n", fontName->getCString());
317 char*name = fontName->getCString();
318 /* see if it is a pdf standard font */
320 for(t=0;t<sizeof(pdf2t1map)/sizeof(fontentry);t++) {
321 if(!strcmp(name, pdf2t1map[t].pdffont)) {
322 if(!pdf2t1map[t].fullfilename) {
323 pdf2t1map[t].fullfilename = writeOutStdFont(&pdf2t1map[t]);
324 if(!pdf2t1map[t].fullfilename) {
325 msg("<error> Couldn't save default font- is the Temp Directory writable?");
327 msg("<verbose> Storing standard PDF font %s at %s", name, pdf2t1map[t].fullfilename);
330 DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), displayFontT1);
331 dfp->t1.fileName = new GString(pdf2t1map[t].fullfilename);
336 int bestlen = 0x7fffffff;
337 const char*bestfilename = 0;
341 if(strstr(f->filename, name)) {
342 if(f->len < bestlen) {
344 bestfilename = f->filename;
350 DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), displayFontT1);
351 dfp->t1.fileName = new GString(bestfilename);
354 return GlobalParams::getDisplayFont(fontName);
357 GFXOutputDev::GFXOutputDev(InfoOutputDev*info, PDFDoc*doc)
361 this->xref = doc->getXRef();
364 this->textmodeinfo = 0;
367 this->type3active = 0;
370 this->substitutepos = 0;
371 this->type3Warning = 0;
372 this->user_movex = 0;
373 this->user_movey = 0;
376 this->user_clipx1 = 0;
377 this->user_clipy1 = 0;
378 this->user_clipx2 = 0;
379 this->user_clipy2 = 0;
380 this->current_text_stroke = 0;
381 this->current_text_clip = 0;
382 this->outer_clip_box = 0;
384 this->pagebuflen = 0;
386 this->config_break_on_warning=0;
387 this->config_remapunicode=0;
388 this->config_transparent=0;
389 this->config_extrafontdata = 0;
390 this->config_fontquality = 10;
392 this->gfxfontlist = gfxfontlist_create();
394 memset(states, 0, sizeof(states));
397 void GFXOutputDev::setParameter(const char*key, const char*value)
399 if(!strcmp(key,"breakonwarning")) {
400 this->config_break_on_warning = atoi(value);
401 } else if(!strcmp(key,"remapunicode")) {
402 this->config_remapunicode = atoi(value);
403 } else if(!strcmp(key,"transparent")) {
404 this->config_transparent = atoi(value);
405 } else if(!strcmp(key,"extrafontdata")) {
406 this->config_extrafontdata = atoi(value);
407 } else if(!strcmp(key,"fontquality")) {
408 this->config_fontquality = atof(value);
409 if(this->config_fontquality<=1)
410 this->config_fontquality=1;
411 } else if(!strcmp(key,"help")) {
412 printf("\nPDF layer options:\n");
413 printf("breakonwarning=0/1 Abort conversion if graphic objects are found which\n");
414 printf(" are not 100%% supported\n");
415 printf("transparent=0/1 Make PDF transparent (alpha background)\n");
416 printf("extrafontdata=0/1 Store Type3 characters and capture characters\n");
417 printf("fontquality=1..100 Curve approximation quality of the fonts\n");
422 void GFXOutputDev::setDevice(gfxdevice_t*dev)
427 void GFXOutputDev::setMove(int x,int y)
429 this->user_movex = x;
430 this->user_movey = y;
433 void GFXOutputDev::setClip(int x1,int y1,int x2,int y2)
435 if(x2<x1) {int x3=x1;x1=x2;x2=x3;}
436 if(y2<y1) {int y3=y1;y1=y2;y2=y3;}
438 this->user_clipx1 = x1;
439 this->user_clipy1 = y1;
440 this->user_clipx2 = x2;
441 this->user_clipy2 = y2;
444 static char*getFontName(GfxFont*font)
447 GString*gstr = font->getName();
448 char* fname = gstr==0?0:gstr->getCString();
452 sprintf(buf, "UFONT%d", r->num);
453 fontid = strdup(buf);
455 fontid = strdup(fname);
459 char* plus = strchr(fontid, '+');
460 if(plus && plus < &fontid[strlen(fontid)-1]) {
461 fontname = strdup(plus+1);
463 fontname = strdup(fontid);
469 static void dumpFontInfo(const char*loglevel, GfxFont*font);
470 static int lastdumps[1024];
471 static int lastdumppos = 0;
476 static void showFontError(GfxFont*font, int nr)
480 for(t=0;t<lastdumppos;t++)
481 if(lastdumps[t] == r->num)
485 if(lastdumppos<sizeof(lastdumps)/sizeof(int))
486 lastdumps[lastdumppos++] = r->num;
488 msg("<warning> The following font caused problems:");
490 msg("<warning> The following font caused problems (substituting):");
492 msg("<warning> The following Type 3 Font will be rendered as graphics:");
493 dumpFontInfo("<warning>", font);
496 static void dumpFontInfo(const char*loglevel, GfxFont*font)
498 char* id = getFontID(font);
499 char* name = getFontName(font);
500 Ref* r=font->getID();
501 msg("%s=========== %s (ID:%d,%d) ==========\n", loglevel, name, r->num,r->gen);
503 GString*gstr = font->getTag();
505 msg("%s| Tag: %s\n", loglevel, id);
507 if(font->isCIDFont()) msg("%s| is CID font\n", loglevel);
509 GfxFontType type=font->getType();
511 case fontUnknownType:
512 msg("%s| Type: unknown\n",loglevel);
515 msg("%s| Type: 1\n",loglevel);
518 msg("%s| Type: 1C\n",loglevel);
521 msg("%s| Type: 3\n",loglevel);
524 msg("%s| Type: TrueType\n",loglevel);
527 msg("%s| Type: CIDType0\n",loglevel);
530 msg("%s| Type: CIDType0C\n",loglevel);
533 msg("%s| Type: CIDType2\n",loglevel);
538 GBool embedded = font->getEmbeddedFontID(&embRef);
540 if(font->getEmbeddedFontName()) {
541 embeddedName = font->getEmbeddedFontName()->getCString();
544 msg("%s| Embedded id: %s id: %d\n",loglevel, FIXNULL(embeddedName), embRef.num);
546 gstr = font->getExtFontFile();
548 msg("%s| External Font file: %s\n", loglevel, FIXNULL(gstr->getCString()));
550 // Get font descriptor flags.
551 if(font->isFixedWidth()) msg("%s| is fixed width\n", loglevel);
552 if(font->isSerif()) msg("%s| is serif\n", loglevel);
553 if(font->isSymbolic()) msg("%s| is symbolic\n", loglevel);
554 if(font->isItalic()) msg("%s| is italic\n", loglevel);
555 if(font->isBold()) msg("%s| is bold\n", loglevel);
561 //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");}
562 //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");}
564 void dump_outline(gfxline_t*line)
567 if(line->type == gfx_moveTo) {
568 msg("<debug> | moveTo %.2f %.2f", line->x,line->y);
569 } else if(line->type == gfx_lineTo) {
570 msg("<debug> | lineTo %.2f %.2f", line->x,line->y);
571 } else if(line->type == gfx_splineTo) {
572 msg("<debug> | splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y);
578 gfxline_t* gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user_movex, int user_movey)
580 int num = path->getNumSubpaths();
583 double lastx=0,lasty=0,posx=0,posy=0;
586 msg("<warning> empty path");
590 gfxdrawer_target_gfxline(&draw);
592 for(t = 0; t < num; t++) {
593 GfxSubpath *subpath = path->getSubpath(t);
594 int subnum = subpath->getNumPoints();
595 double bx=0,by=0,cx=0,cy=0;
597 for(s=0;s<subnum;s++) {
600 state->transform(subpath->getX(s),subpath->getY(s),&x,&y);
605 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
606 draw.lineTo(&draw, lastx, lasty);
608 draw.moveTo(&draw, x,y);
613 } else if(subpath->getCurve(s) && cpos==0) {
617 } else if(subpath->getCurve(s) && cpos==1) {
625 draw.lineTo(&draw, x,y);
627 gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y, 0.05);
634 /* fix non-closed lines */
635 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
636 draw.lineTo(&draw, lastx, lasty);
638 gfxline_t*result = (gfxline_t*)draw.result(&draw);
640 gfxline_optimize(result);
645 GBool GFXOutputDev::useTilingPatternFill()
647 infofeature("tiled patterns");
651 GBool GFXOutputDev::useShadedFills()
653 infofeature("shaded fills");
657 GBool GFXOutputDev::useDrawForm()
659 infofeature("forms");
662 void GFXOutputDev::drawForm(Ref id)
664 msg("<error> drawForm not implemented");
666 GBool GFXOutputDev::needNonText()
670 void GFXOutputDev::endPage()
672 msg("<verbose> endPage (GfxOutputDev)");
674 device->endclip(device);
679 #define STROKE_FILL 1
680 #define STROKE_CLIP 2
681 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line, int flags)
683 int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
684 int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
685 double miterLimit = state->getMiterLimit();
686 double width = state->getTransformedLineWidth();
689 double opaq = state->getStrokeOpacity();
691 state->getFillRGB(&rgb);
693 state->getStrokeRGB(&rgb);
695 col.r = colToByte(rgb.r);
696 col.g = colToByte(rgb.g);
697 col.b = colToByte(rgb.b);
698 col.a = (unsigned char)(opaq*255);
700 gfx_capType capType = gfx_capRound;
701 if(lineCap == 0) capType = gfx_capButt;
702 else if(lineCap == 1) capType = gfx_capRound;
703 else if(lineCap == 2) capType = gfx_capSquare;
705 gfx_joinType joinType = gfx_joinRound;
706 if(lineJoin == 0) joinType = gfx_joinMiter;
707 else if(lineJoin == 1) joinType = gfx_joinRound;
708 else if(lineJoin == 2) joinType = gfx_joinBevel;
711 double dashphase = 0;
713 state->getLineDash(&ldash, &dashnum, &dashphase);
717 if(dashnum && ldash) {
718 float * dash = (float*)malloc(sizeof(float)*(dashnum+1));
720 msg("<trace> %d dashes", dashnum);
721 msg("<trace> | phase: %f", dashphase);
722 for(t=0;t<dashnum;t++) {
723 dash[t] = (float)ldash[t];
724 msg("<trace> | d%-3d: %f", t, ldash[t]);
727 if(getLogLevel() >= LOGLEVEL_TRACE) {
731 line2 = gfxtool_dash_line(line, dash, (float)dashphase);
734 msg("<trace> After dashing:");
737 if(getLogLevel() >= LOGLEVEL_TRACE) {
738 msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x\n",
740 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
741 lineCap==0?"butt": (lineJoin==1?"round":"square"),
743 col.r,col.g,col.b,col.a
748 if(flags&STROKE_FILL) {
749 ArtSVP* svp = gfxstrokeToSVP(line, width, capType, joinType, miterLimit);
750 gfxline_t*gfxline = SVPtogfxline(svp);
751 if(getLogLevel() >= LOGLEVEL_TRACE) {
752 dump_outline(gfxline);
755 msg("<warning> Empty polygon (resulting from stroked line)");
757 if(flags&STROKE_CLIP) {
758 device->startclip(device, gfxline);
759 states[statepos].clipping++;
761 device->fill(device, gfxline, &col);
766 if(flags&STROKE_CLIP)
767 msg("<error> Stroke&clip not supported at the same time");
768 device->stroke(device, line, width, &col, capType, joinType, miterLimit);
775 gfxcolor_t getFillColor(GfxState * state)
778 double opaq = state->getFillOpacity();
779 state->getFillRGB(&rgb);
781 col.r = colToByte(rgb.r);
782 col.g = colToByte(rgb.g);
783 col.b = colToByte(rgb.b);
784 col.a = (unsigned char)(opaq*255);
788 void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line)
790 gfxcolor_t col = getFillColor(state);
792 if(getLogLevel() >= LOGLEVEL_TRACE) {
793 msg("<trace> fill %02x%02x%02x%02x\n", col.r, col.g, col.b, col.a);
796 device->fill(device, line, &col);
799 void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line)
801 if(getLogLevel() >= LOGLEVEL_TRACE) {
802 msg("<trace> clip\n");
806 device->startclip(device, line);
807 states[statepos].clipping++;
810 void GFXOutputDev::clip(GfxState *state)
812 GfxPath * path = state->getPath();
813 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
814 clipToGfxLine(state, line);
818 void GFXOutputDev::eoClip(GfxState *state)
820 GfxPath * path = state->getPath();
821 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
823 if(getLogLevel() >= LOGLEVEL_TRACE) {
824 msg("<trace> eoclip\n");
828 device->startclip(device, line);
829 states[statepos].clipping++;
832 void GFXOutputDev::clipToStrokePath(GfxState *state)
834 GfxPath * path = state->getPath();
835 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
837 if(getLogLevel() >= LOGLEVEL_TRACE) {
838 msg("<trace> cliptostrokepath\n");
842 strokeGfxline(state, line, STROKE_FILL|STROKE_CLIP);
846 void GFXOutputDev::finish()
850 device->endclip(device);
856 GFXOutputDev::~GFXOutputDev()
861 free(this->pages); this->pages = 0;
864 gfxfontlist_free(this->gfxfontlist, 1);this->gfxfontlist = 0;
866 GBool GFXOutputDev::upsideDown()
870 GBool GFXOutputDev::useDrawChar()
875 const char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
876 "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
878 static char tmp_printstr[4096];
879 char* makeStringPrintable(char*str)
881 int len = strlen(str);
896 tmp_printstr[len++] = '.';
897 tmp_printstr[len++] = '.';
898 tmp_printstr[len++] = '.';
900 tmp_printstr[len] = 0;
903 #define INTERNAL_FONT_SIZE 1024.0
904 void GFXOutputDev::updateFontMatrix(GfxState*state)
906 double* ctm = state->getCTM();
907 double fontSize = state->getFontSize();
908 double*textMat = state->getTextMat();
910 /* taking the absolute value of horizScaling seems to be required for
911 some italic fonts. FIXME: SplashOutputDev doesn't need this- why? */
912 double hscale = fabs(state->getHorizScaling());
914 // from xpdf-3.02/SplashOutputDev:updateFont
915 double mm11 = textMat[0] * fontSize * hscale;
916 double mm12 = textMat[1] * fontSize * hscale;
917 double mm21 = textMat[2] * fontSize;
918 double mm22 = textMat[3] * fontSize;
920 // multiply with ctm, like state->getFontTransMat() does
921 this->current_font_matrix.m00 = (ctm[0]*mm11 + ctm[2]*mm12) / INTERNAL_FONT_SIZE;
922 this->current_font_matrix.m01 = (ctm[1]*mm11 + ctm[3]*mm12) / INTERNAL_FONT_SIZE;
923 this->current_font_matrix.m10 = (ctm[0]*mm21 + ctm[2]*mm22) / INTERNAL_FONT_SIZE;
924 this->current_font_matrix.m11 = (ctm[1]*mm21 + ctm[3]*mm22) / INTERNAL_FONT_SIZE;
925 this->current_font_matrix.tx = 0;
926 this->current_font_matrix.ty = 0;
929 void GFXOutputDev::beginString(GfxState *state, GString *s)
931 int render = state->getRender();
932 if(current_text_stroke) {
933 msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
936 msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
939 static gfxline_t* mkEmptyGfxShape(double x, double y)
941 gfxline_t*line = (gfxline_t*)malloc(sizeof(gfxline_t));
942 line->x = x;line->y = y;line->type = gfx_moveTo;line->next = 0;
946 static char isValidUnicode(int c)
948 if(c>=32 && c<0x2fffe)
953 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
954 double dx, double dy,
955 double originX, double originY,
956 CharCode charid, int nBytes, Unicode *_u, int uLen)
958 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
959 msg("<error> Invalid charid %d for font (%d characters)", charid, current_fontinfo?current_fontinfo->num_glyphs:0);
963 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
965 int render = state->getRender();
966 gfxcolor_t col = getFillColor(state);
968 // check for invisible text -- this is used by Acrobat Capture
969 if (render == RENDER_INVISIBLE) {
971 if(!config_extrafontdata)
975 GfxFont*font = state->getFont();
977 if(font->getType() == fontType3) {
978 /* type 3 chars are passed as graphics */
979 msg("<debug> type3 char at %f/%f", x, y);
983 Unicode u = uLen?(_u[0]):0;
984 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);
986 gfxmatrix_t m = this->current_font_matrix;
987 state->transform(x, y, &m.tx, &m.ty);
988 m.tx += user_movex + clipmovex;
989 m.ty += user_movey + clipmovey;
991 if(render == RENDER_FILL || render == RENDER_INVISIBLE) {
992 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
994 msg("<debug> Drawing glyph %d as shape", charid);
996 msg("<notice> Some texts will be rendered as shape");
999 gfxline_t*glyph = current_gfxfont->glyphs[glyphid].line;
1000 gfxline_t*tglyph = gfxline_clone(glyph);
1001 gfxline_transform(tglyph, &m);
1002 if((render&3) != RENDER_INVISIBLE) {
1003 gfxline_t*add = gfxline_clone(tglyph);
1004 current_text_stroke = gfxline_append(current_text_stroke, add);
1006 if(render&RENDER_CLIP) {
1007 gfxline_t*add = gfxline_clone(tglyph);
1008 current_text_clip = gfxline_append(current_text_clip, add);
1009 if(!current_text_clip) {
1010 current_text_clip = mkEmptyGfxShape(m.tx, m.ty);
1013 gfxline_free(tglyph);
1017 void GFXOutputDev::endString(GfxState *state)
1019 int render = state->getRender();
1020 msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
1022 if(current_text_stroke) {
1023 /* fillstroke and stroke text rendering objects we can process right
1024 now (as there may be texts of other rendering modes in this
1025 text object)- clipping objects have to wait until endTextObject,
1027 device->setparameter(device, "mark","TXT");
1028 if((render&3) == RENDER_FILL) {
1029 fillGfxLine(state, current_text_stroke);
1030 gfxline_free(current_text_stroke);
1031 current_text_stroke = 0;
1032 } else if((render&3) == RENDER_FILLSTROKE) {
1033 fillGfxLine(state, current_text_stroke);
1034 strokeGfxline(state, current_text_stroke,0);
1035 gfxline_free(current_text_stroke);
1036 current_text_stroke = 0;
1037 } else if((render&3) == RENDER_STROKE) {
1038 strokeGfxline(state, current_text_stroke,0);
1039 gfxline_free(current_text_stroke);
1040 current_text_stroke = 0;
1042 device->setparameter(device, "mark","");
1046 void GFXOutputDev::endTextObject(GfxState *state)
1048 int render = state->getRender();
1049 msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1051 if(current_text_clip) {
1052 device->setparameter(device, "mark","TXT");
1053 clipToGfxLine(state, current_text_clip);
1054 device->setparameter(device, "mark","");
1055 gfxline_free(current_text_clip);
1056 current_text_clip = 0;
1060 /* the logic seems to be as following:
1061 first, beginType3Char is called, with the charcode and the coordinates.
1062 if this function returns true, it already knew about the char and has now drawn it.
1063 if the function returns false, it's a new char, and type3D0 and/or type3D1 might be
1064 called with some parameters.
1065 Afterwards, all draw operations until endType3Char are part of the char (which in this moment is
1066 at the position first passed to beginType3Char). the char ends with endType3Char.
1068 The drawing operations between beginType3Char and endType3Char are somewhat different to
1069 the normal ones. For example, the fillcolor equals the stroke color. (Because the stroke
1070 color determines the color of a font)
1073 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode charid, Unicode *u, int uLen)
1075 msg("<debug> beginType3Char %d u=%d", charid, uLen?u[0]:0);
1078 if(config_extrafontdata && current_fontinfo) {
1080 gfxmatrix_t m = this->current_font_matrix;
1081 state->transform(0, 0, &m.tx, &m.ty);
1082 m.m00*=INTERNAL_FONT_SIZE;
1083 m.m01*=INTERNAL_FONT_SIZE;
1084 m.m10*=INTERNAL_FONT_SIZE;
1085 m.m11*=INTERNAL_FONT_SIZE;
1086 m.tx += user_movex + clipmovex;
1087 m.ty += user_movey + clipmovey;
1089 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1090 msg("<error> Invalid charid %d for font", charid);
1093 gfxcolor_t col={0,0,0,0};
1094 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1095 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1099 /* the character itself is going to be passed using the draw functions */
1100 return gFalse; /* gTrue= is_in_cache? */
1103 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1105 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1108 void GFXOutputDev::endType3Char(GfxState *state)
1111 msg("<debug> endType3Char");
1114 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
1116 this->currentpage = pageNum;
1118 int rot = doc->getPageRotate(1);
1119 gfxcolor_t white = {255,255,255,255};
1120 gfxcolor_t black = {255,0,0,0};
1122 gfxline_t clippath[5];
1124 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1125 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1126 Use CropBox, not MediaBox, as page size
1133 state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1134 state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1136 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1137 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1139 this->clipmovex = -(int)x1;
1140 this->clipmovey = -(int)y1;
1142 /* apply user clip box */
1143 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1144 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1145 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1146 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1147 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1148 msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1151 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1153 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);
1155 msg("<verbose> page is rotated %d degrees\n", rot);
1157 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1158 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1159 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1160 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1161 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1162 device->startclip(device, clippath); outer_clip_box = 1;
1163 if(!config_transparent) {
1164 device->fill(device, clippath, &white);
1169 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1171 double x1, y1, x2, y2;
1172 gfxline_t points[5];
1175 msg("<debug> drawlink\n");
1177 link->getRect(&x1, &y1, &x2, &y2);
1178 cvtUserToDev(x1, y1, &x, &y);
1179 points[0].type = gfx_moveTo;
1180 points[0].x = points[4].x = x + user_movex + clipmovex;
1181 points[0].y = points[4].y = y + user_movey + clipmovey;
1182 points[0].next = &points[1];
1183 cvtUserToDev(x2, y1, &x, &y);
1184 points[1].type = gfx_lineTo;
1185 points[1].x = x + user_movex + clipmovex;
1186 points[1].y = y + user_movey + clipmovey;
1187 points[1].next = &points[2];
1188 cvtUserToDev(x2, y2, &x, &y);
1189 points[2].type = gfx_lineTo;
1190 points[2].x = x + user_movex + clipmovex;
1191 points[2].y = y + user_movey + clipmovey;
1192 points[2].next = &points[3];
1193 cvtUserToDev(x1, y2, &x, &y);
1194 points[3].type = gfx_lineTo;
1195 points[3].x = x + user_movex + clipmovex;
1196 points[3].y = y + user_movey + clipmovey;
1197 points[3].next = &points[4];
1198 cvtUserToDev(x1, y1, &x, &y);
1199 points[4].type = gfx_lineTo;
1200 points[4].x = x + user_movex + clipmovex;
1201 points[4].y = y + user_movey + clipmovey;
1204 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f\n",
1205 points[0].x, points[0].y,
1206 points[1].x, points[1].y,
1207 points[2].x, points[2].y,
1208 points[3].x, points[3].y);
1210 if(getLogLevel() >= LOGLEVEL_TRACE) {
1211 dump_outline(points);
1214 LinkAction*action=link->getAction();
1217 const char*type = "-?-";
1220 msg("<trace> drawlink action=%d\n", action->getKind());
1221 switch(action->getKind())
1225 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1226 LinkDest *dest=NULL;
1227 if (ha->getDest()==NULL)
1228 dest=catalog->findDest(ha->getNamedDest());
1229 else dest=ha->getDest();
1231 if (dest->isPageRef()){
1232 Ref pageref=dest->getPageRef();
1233 page=catalog->findPage(pageref.num,pageref.gen);
1235 else page=dest->getPageNum();
1236 sprintf(buf, "%d", page);
1243 LinkGoToR*l = (LinkGoToR*)action;
1244 GString*g = l->getFileName();
1246 s = strdup(g->getCString());
1248 /* if the GoToR link has no filename, then
1249 try to find a refernce in the *local*
1251 GString*g = l->getNamedDest();
1253 s = strdup(g->getCString());
1259 LinkNamed*l = (LinkNamed*)action;
1260 GString*name = l->getName();
1262 s = strdup(name->lowerCase()->getCString());
1263 named = name->getCString();
1266 if(strstr(s, "next") || strstr(s, "forward"))
1268 page = currentpage + 1;
1270 else if(strstr(s, "prev") || strstr(s, "back"))
1272 page = currentpage - 1;
1274 else if(strstr(s, "last") || strstr(s, "end"))
1276 if(pages && pagepos>0)
1277 page = pages[pagepos-1];
1279 else if(strstr(s, "first") || strstr(s, "top"))
1287 case actionLaunch: {
1289 LinkLaunch*l = (LinkLaunch*)action;
1290 GString * str = new GString(l->getFileName());
1291 GString * params = l->getParams();
1293 str->append(params);
1294 s = strdup(str->getCString());
1301 LinkURI*l = (LinkURI*)action;
1302 GString*g = l->getURI();
1304 url = g->getCString();
1309 case actionUnknown: {
1311 LinkUnknown*l = (LinkUnknown*)action;
1316 msg("<error> Unknown link type!\n");
1321 if(!s) s = strdup("-?-");
1323 msg("<trace> drawlink s=%s\n", s);
1325 if(!linkinfo && (page || s))
1327 msg("<notice> File contains links");
1335 for(t=1;t<=pagepos;t++) {
1336 if(pages[t]==page) {
1345 sprintf(buf, "page%d", lpage);
1346 device->drawlink(device, points, buf);
1350 device->drawlink(device, points, s);
1353 msg("<verbose> \"%s\" link to \"%s\" (%d)\n", type, FIXNULL(s), page);
1357 void GFXOutputDev::saveState(GfxState *state) {
1358 dbg("saveState");dbgindent+=2;
1360 msg("<trace> saveState\n");
1363 msg("<error> Too many nested states in pdf.");
1367 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1368 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1369 states[statepos].clipping = 0;
1372 void GFXOutputDev::restoreState(GfxState *state) {
1373 dbgindent-=2; dbg("restoreState");
1376 msg("<error> Invalid restoreState");
1379 msg("<trace> restoreState%s%s", states[statepos].softmask?" (end softmask)":"",
1380 states[statepos].clipping?" (end clipping)":"");
1381 if(states[statepos].softmask) {
1382 clearSoftMask(state);
1385 while(states[statepos].clipping) {
1386 device->endclip(device);
1387 states[statepos].clipping--;
1392 void GFXOutputDev::updateLineWidth(GfxState *state)
1394 double width = state->getTransformedLineWidth();
1395 //swfoutput_setlinewidth(&device, width);
1398 void GFXOutputDev::updateLineCap(GfxState *state)
1400 int c = state->getLineCap();
1403 void GFXOutputDev::updateLineJoin(GfxState *state)
1405 int j = state->getLineJoin();
1408 void GFXOutputDev::updateFillColor(GfxState *state)
1411 double opaq = state->getFillOpacity();
1412 state->getFillRGB(&rgb);
1414 void GFXOutputDev::updateFillOpacity(GfxState *state)
1417 double opaq = state->getFillOpacity();
1418 state->getFillRGB(&rgb);
1419 dbg("update fillopaq %f", opaq);
1421 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1423 double opaq = state->getFillOpacity();
1424 dbg("update strokeopaq %f", opaq);
1426 void GFXOutputDev::updateFillOverprint(GfxState *state)
1428 double opaq = state->getFillOverprint();
1429 dbg("update filloverprint %f", opaq);
1431 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1433 double opaq = state->getStrokeOverprint();
1434 dbg("update strokeoverprint %f", opaq);
1436 void GFXOutputDev::updateTransfer(GfxState *state)
1438 dbg("update transfer");
1442 void GFXOutputDev::updateStrokeColor(GfxState *state)
1445 double opaq = state->getStrokeOpacity();
1446 state->getStrokeRGB(&rgb);
1450 gfxfont_t* createGfxFont(GfxFont*xpdffont, FontInfo*src, double config_fontquality)
1452 gfxfont_t*font = (gfxfont_t*)malloc(sizeof(gfxfont_t));
1453 memset(font, 0, sizeof(gfxfont_t));
1455 font->glyphs = (gfxglyph_t*)malloc(sizeof(gfxglyph_t)*src->num_glyphs);
1456 memset(font->glyphs, 0, sizeof(gfxglyph_t)*src->num_glyphs);
1457 font->id = strdup(getFontID(xpdffont));
1460 double quality = (INTERNAL_FONT_SIZE * 200 / config_fontquality) / src->max_size;
1462 //printf("%d glyphs\n", font->num_glyphs);
1463 font->num_glyphs = 0;
1464 for(t=0;t<src->num_glyphs;t++) {
1465 if(src->glyphs[t]) {
1466 SplashPath*path = src->glyphs[t]->path;
1467 int len = path?path->getLength():0;
1468 //printf("glyph %d) %08x (%d line segments)\n", t, path, len);
1469 gfxglyph_t*glyph = &font->glyphs[font->num_glyphs];
1470 src->glyphs[t]->glyphid = font->num_glyphs;
1471 glyph->unicode = src->glyphs[t]->unicode;
1472 if(glyph->unicode >= font->max_unicode)
1473 font->max_unicode = glyph->unicode+1;
1475 gfxdrawer_target_gfxline(&drawer);
1479 for(s=0;s<len;s++) {
1482 path->getPoint(s, &x, &y, &f);
1485 if(f&splashPathFirst) {
1486 drawer.moveTo(&drawer, x*scale, y*scale);
1488 if(f&splashPathCurve) {
1490 path->getPoint(++s, &x2, &y2, &f);
1491 if(f&splashPathCurve) {
1493 path->getPoint(++s, &x3, &y3, &f);
1494 gfxdraw_cubicTo(&drawer, x*scale, y*scale, x2*scale, y2*scale, x3*scale, y3*scale, quality);
1496 drawer.splineTo(&drawer, x*scale, y*scale, x2*scale, y2*scale);
1499 drawer.lineTo(&drawer, x*scale, y*scale);
1501 // printf("%f %f %s %s\n", x, y, (f&splashPathCurve)?"curve":"",
1502 // (f&splashPathFirst)?"first":"",
1503 // (f&splashPathLast)?"last":"");
1505 glyph->line = (gfxline_t*)drawer.result(&drawer);
1506 glyph->advance = xmax*scale; // we don't know the real advance value, so this'll have to do
1510 font->unicode2glyph = (int*)malloc(sizeof(int)*font->max_unicode);
1511 memset(font->unicode2glyph, -1, sizeof(int)*font->max_unicode);
1512 for(t=0;t<font->num_glyphs;t++) {
1513 if(font->glyphs[t].unicode>0 && font->glyphs[t].unicode<font->max_unicode) {
1514 font->unicode2glyph[font->glyphs[t].unicode] = t;
1518 msg("<trace> %d glyphs.", t, font->num_glyphs);
1522 void GFXOutputDev::updateFont(GfxState *state)
1524 GfxFont* gfxFont = state->getFont();
1528 char*id = getFontID(gfxFont);
1529 msg("<verbose> Updating font to %s", id);
1530 if(gfxFont->getType() == fontType3) {
1531 infofeature("Type3 fonts");
1532 if(!config_extrafontdata) {
1537 msg("<error> Internal Error: FontID is null");
1541 this->current_fontinfo = this->info->getFont(id);
1542 if(!this->current_fontinfo) {
1543 msg("<error> Internal Error: no fontinfo for font %s\n", id);
1546 if(!this->current_fontinfo->seen) {
1547 dumpFontInfo("<verbose>", gfxFont);
1550 gfxfont_t*font = gfxfontlist_findfont(this->gfxfontlist,id);
1552 font = createGfxFont(gfxFont, current_fontinfo, this->config_fontquality);
1553 font->id = strdup(id);
1554 this->gfxfontlist = gfxfontlist_addfont(this->gfxfontlist, font);
1555 device->addfont(device, font);
1557 current_gfxfont = font;
1560 updateFontMatrix(state);
1563 #define SQR(x) ((x)*(x))
1565 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
1567 if((newwidth<2 || newheight<2) ||
1568 (width<=newwidth || height<=newheight))
1570 unsigned char*newdata;
1572 newdata= (unsigned char*)malloc(newwidth*newheight);
1573 double fx = (double)(width)/newwidth;
1574 double fy = (double)(height)/newheight;
1576 int blocksize = (int)(8192/(fx*fy));
1577 int r = 8192*256/palettesize;
1578 for(x=0;x<newwidth;x++) {
1579 double ex = px + fx;
1580 int fromx = (int)px;
1582 int xweight1 = (int)(((fromx+1)-px)*256);
1583 int xweight2 = (int)((ex-tox)*256);
1585 for(y=0;y<newheight;y++) {
1586 double ey = py + fy;
1587 int fromy = (int)py;
1589 int yweight1 = (int)(((fromy+1)-py)*256);
1590 int yweight2 = (int)((ey-toy)*256);
1593 for(xx=fromx;xx<=tox;xx++)
1594 for(yy=fromy;yy<=toy;yy++) {
1595 int b = 1-data[width*yy+xx];
1597 if(xx==fromx) weight = (weight*xweight1)/256;
1598 if(xx==tox) weight = (weight*xweight2)/256;
1599 if(yy==fromy) weight = (weight*yweight1)/256;
1600 if(yy==toy) weight = (weight*yweight2)/256;
1603 //if(a) a=(palettesize-1)*r/blocksize;
1604 newdata[y*newwidth+x] = (a*blocksize)/r;
1612 #define IMAGE_TYPE_JPEG 0
1613 #define IMAGE_TYPE_LOSSLESS 1
1615 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
1616 double x1,double y1,
1617 double x2,double y2,
1618 double x3,double y3,
1619 double x4,double y4, int type)
1621 gfxcolor_t*newpic=0;
1623 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
1624 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
1626 gfxline_t p1,p2,p3,p4,p5;
1627 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
1628 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
1629 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
1630 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
1631 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
1633 {p1.x = (int)(p1.x*20)/20.0;
1634 p1.y = (int)(p1.y*20)/20.0;
1635 p2.x = (int)(p2.x*20)/20.0;
1636 p2.y = (int)(p2.y*20)/20.0;
1637 p3.x = (int)(p3.x*20)/20.0;
1638 p3.y = (int)(p3.y*20)/20.0;
1639 p4.x = (int)(p4.x*20)/20.0;
1640 p4.y = (int)(p4.y*20)/20.0;
1641 p5.x = (int)(p5.x*20)/20.0;
1642 p5.y = (int)(p5.y*20)/20.0;
1646 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
1647 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
1652 img.data = (gfxcolor_t*)data;
1656 if(type == IMAGE_TYPE_JPEG)
1657 /* TODO: pass image_dpi to device instead */
1658 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
1660 dev->fillbitmap(dev, &p1, &img, &m, 0);
1663 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
1664 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
1666 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG);
1669 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
1670 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
1672 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS);
1676 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
1677 int width, int height, GfxImageColorMap*colorMap, GBool invert,
1678 GBool inlineImg, int mask, int*maskColors,
1679 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
1681 double x1,y1,x2,y2,x3,y3,x4,y4;
1682 ImageStream *imgStr;
1687 unsigned char* maskbitmap = 0;
1690 ncomps = colorMap->getNumPixelComps();
1691 bits = colorMap->getBits();
1696 unsigned char buf[8];
1697 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
1699 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
1700 imgMaskStr->reset();
1701 unsigned char pal[256];
1702 int n = 1 << colorMap->getBits();
1707 maskColorMap->getGray(pixBuf, &gray);
1708 pal[t] = colToByte(gray);
1710 for (y = 0; y < maskHeight; y++) {
1711 for (x = 0; x < maskWidth; x++) {
1712 imgMaskStr->getPixel(buf);
1713 maskbitmap[y*maskWidth+x] = pal[buf[0]];
1718 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
1719 imgMaskStr->reset();
1720 for (y = 0; y < maskHeight; y++) {
1721 for (x = 0; x < maskWidth; x++) {
1722 imgMaskStr->getPixel(buf);
1724 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
1732 imgStr = new ImageStream(str, width, ncomps,bits);
1735 if(!width || !height || (height<=1 && width<=1))
1737 msg("<verbose> Ignoring %d by %d image", width, height);
1738 unsigned char buf[8];
1740 for (y = 0; y < height; ++y)
1741 for (x = 0; x < width; ++x) {
1742 imgStr->getPixel(buf);
1750 state->transform(0, 1, &x1, &y1); x1 += user_movex + clipmovex; y1 += user_movey + clipmovey;
1751 state->transform(0, 0, &x2, &y2); x2 += user_movex + clipmovex; y2 += user_movey + clipmovey;
1752 state->transform(1, 0, &x3, &y3); x3 += user_movex + clipmovex; y3 += user_movey + clipmovey;
1753 state->transform(1, 1, &x4, &y4); x4 += user_movex + clipmovex; y4 += user_movey + clipmovey;
1755 if(!pbminfo && !(str->getKind()==strDCT)) {
1757 msg("<notice> file contains pbm pictures %s",mask?"(masked)":"");
1761 msg("<verbose> drawing %d by %d masked picture\n", width, height);
1763 if(!jpeginfo && (str->getKind()==strDCT)) {
1764 msg("<notice> file contains jpeg pictures");
1769 unsigned char buf[8];
1771 unsigned char*pic = new unsigned char[width*height];
1772 gfxcolor_t pal[256];
1774 state->getFillRGB(&rgb);
1776 memset(pal,255,sizeof(pal));
1777 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
1778 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
1779 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
1780 pal[0].a = 255; pal[1].a = 0;
1783 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
1784 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
1785 for (y = 0; y < height; ++y)
1786 for (x = 0; x < width; ++x)
1788 imgStr->getPixel(buf);
1791 pic[width*y+x] = buf[0];
1794 /* the size of the drawn image is added to the identifier
1795 as the same image may require different bitmaps if displayed
1796 at different sizes (due to antialiasing): */
1799 unsigned char*pic2 = 0;
1802 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
1811 height = realheight;
1815 /* make a black/white palette */
1817 float r = 255./(float)(numpalette-1);
1819 for(t=0;t<numpalette;t++) {
1820 pal[t].r = colToByte(rgb.r);
1821 pal[t].g = colToByte(rgb.g);
1822 pal[t].b = colToByte(rgb.b);
1823 pal[t].a = (unsigned char)(t*r);
1827 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
1828 for (y = 0; y < height; ++y) {
1829 for (x = 0; x < width; ++x) {
1830 pic2[width*y+x] = pal[pic[y*width+x]];
1833 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1837 if(maskbitmap) free(maskbitmap);
1843 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
1844 gfxcolor_t*pic=new gfxcolor_t[width*height];
1845 for (y = 0; y < height; ++y) {
1846 for (x = 0; x < width; ++x) {
1847 imgStr->getPixel(pixBuf);
1848 colorMap->getRGB(pixBuf, &rgb);
1849 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
1850 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
1851 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
1852 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
1854 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
1858 if(str->getKind()==strDCT)
1859 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1861 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1864 if(maskbitmap) free(maskbitmap);
1867 gfxcolor_t*pic=new gfxcolor_t[width*height];
1868 gfxcolor_t pal[256];
1869 int n = 1 << colorMap->getBits();
1871 for(t=0;t<256;t++) {
1873 colorMap->getRGB(pixBuf, &rgb);
1875 {/*if(maskColors && *maskColors==t) {
1876 msg("<notice> Color %d is transparent", t);
1877 if (imgData->maskColors) {
1879 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
1880 if (pix[i] < imgData->maskColors[2*i] ||
1881 pix[i] > imgData->maskColors[2*i+1]) {
1896 pal[t].r = (unsigned char)(colToByte(rgb.r));
1897 pal[t].g = (unsigned char)(colToByte(rgb.g));
1898 pal[t].b = (unsigned char)(colToByte(rgb.b));
1899 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
1902 for (y = 0; y < height; ++y) {
1903 for (x = 0; x < width; ++x) {
1904 imgStr->getPixel(pixBuf);
1905 pic[width*y+x] = pal[pixBuf[0]];
1907 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
1911 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1915 if(maskbitmap) free(maskbitmap);
1920 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
1921 int width, int height, GBool invert,
1924 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
1925 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
1926 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
1929 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
1930 int width, int height, GfxImageColorMap *colorMap,
1931 int *maskColors, GBool inlineImg)
1933 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
1934 colorMap?"colorMap":"no colorMap",
1935 maskColors?"maskColors":"no maskColors",
1937 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
1938 colorMap?"colorMap":"no colorMap",
1939 maskColors?"maskColors":"no maskColors",
1942 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
1943 colorMap->getBits(),colorMap->getColorSpace()->getMode());
1944 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
1947 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
1948 int width, int height,
1949 GfxImageColorMap *colorMap,
1950 Stream *maskStr, int maskWidth, int maskHeight,
1953 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
1954 colorMap?"colorMap":"no colorMap",
1955 maskWidth, maskHeight);
1956 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
1957 colorMap?"colorMap":"no colorMap",
1958 maskWidth, maskHeight);
1960 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
1961 colorMap->getBits(),colorMap->getColorSpace()->getMode());
1962 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
1965 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
1966 int width, int height,
1967 GfxImageColorMap *colorMap,
1969 int maskWidth, int maskHeight,
1970 GfxImageColorMap *maskColorMap)
1972 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
1973 colorMap?"colorMap":"no colorMap",
1974 maskWidth, maskHeight);
1975 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
1976 colorMap?"colorMap":"no colorMap",
1977 maskWidth, maskHeight);
1979 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
1980 colorMap->getBits(),colorMap->getColorSpace()->getMode());
1981 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
1984 void GFXOutputDev::stroke(GfxState *state)
1988 GfxPath * path = state->getPath();
1989 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
1990 strokeGfxline(state, line, 0);
1994 void GFXOutputDev::fill(GfxState *state)
1996 gfxcolor_t col = getFillColor(state);
1997 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
1999 GfxPath * path = state->getPath();
2000 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2001 fillGfxLine(state, line);
2005 void GFXOutputDev::eoFill(GfxState *state)
2007 gfxcolor_t col = getFillColor(state);
2008 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2010 GfxPath * path = state->getPath();
2011 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2012 fillGfxLine(state, line);
2017 static const char* dirseparator()
2026 void addGlobalFont(const char*filename)
2028 fontfile_t* f = (fontfile_t*)malloc(sizeof(fontfile_t));
2029 memset(f, 0, sizeof(fontfile_t));
2030 f->filename = filename;
2031 int len = strlen(filename);
2032 char*r1 = strrchr(filename, '/');
2033 char*r2 = strrchr(filename, '\\');
2041 msg("<notice> Adding font \"%s\".", filename);
2042 if(global_fonts_next) {
2043 global_fonts_next->next = f;
2044 global_fonts_next = global_fonts_next->next;
2046 global_fonts_next = global_fonts = f;
2050 void addGlobalLanguageDir(const char*dir)
2052 msg("<notice> Adding %s to language pack directories", dir);
2055 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2056 strcpy(config_file, dir);
2057 strcat(config_file, dirseparator());
2058 strcat(config_file, "add-to-xpdfrc");
2060 fi = fopen(config_file, "rb");
2062 msg("<error> Could not open %s", config_file);
2065 globalParams->parseFile(new GString(config_file), fi);
2069 void addGlobalFontDir(const char*dirname)
2071 #ifdef HAVE_DIRENT_H
2072 msg("<notice> Adding %s to font directories", dirname);
2073 lastfontdir = strdup(dirname);
2074 DIR*dir = opendir(dirname);
2076 msg("<warning> Couldn't open directory %s\n", dirname);
2081 ent = readdir (dir);
2085 char*name = ent->d_name;
2091 if(!strncasecmp(&name[l-4], ".pfa", 4))
2093 if(!strncasecmp(&name[l-4], ".pfb", 4))
2095 if(!strncasecmp(&name[l-4], ".ttf", 4))
2098 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2099 strcpy(fontname, dirname);
2100 strcat(fontname, dirseparator());
2101 strcat(fontname, name);
2102 addGlobalFont(fontname);
2107 msg("<warning> No dirent.h- unable to add font dir %s", dirname);
2111 void GFXOutputDev::preparePage(int pdfpage, int outputpage)
2117 this->pagebuflen = 1024;
2118 this->pages = (int*)malloc(this->pagebuflen*sizeof(int));
2119 memset(this->pages, -1, this->pagebuflen*sizeof(int));
2121 while(pdfpage >= this->pagebuflen)
2123 int oldlen = this->pagebuflen;
2124 this->pagebuflen+=1024;
2125 this->pages = (int*)realloc(this->pages, this->pagebuflen*sizeof(int));
2126 memset(&this->pages[oldlen], -1, (this->pagebuflen-oldlen)*sizeof(int));
2129 this->pages[pdfpage] = outputpage;
2130 if(pdfpage>this->pagepos)
2131 this->pagepos = pdfpage;
2137 double width,height;
2140 BBox mkBBox(GfxState*state, double*bbox, double width, double height)
2142 double xMin, yMin, xMax, yMax, x, y;
2143 double tx, ty, w, h;
2144 // transform the bbox
2145 state->transform(bbox[0], bbox[1], &x, &y);
2148 state->transform(bbox[0], bbox[3], &x, &y);
2151 } else if (x > xMax) {
2156 } else if (y > yMax) {
2159 state->transform(bbox[2], bbox[1], &x, &y);
2162 } else if (x > xMax) {
2167 } else if (y > yMax) {
2170 state->transform(bbox[2], bbox[3], &x, &y);
2173 } else if (x > xMax) {
2178 } else if (y > yMax) {
2181 tx = (int)floor(xMin);
2184 } else if (tx > width) {
2187 ty = (int)floor(yMin);
2190 } else if (ty > height) {
2193 w = (int)ceil(xMax) - tx + 1;
2194 if (tx + w > width) {
2200 h = (int)ceil(yMax) - ty + 1;
2201 if (ty + h > height) {
2215 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2216 GfxColorSpace *blendingColorSpace,
2217 GBool isolated, GBool knockout,
2220 const char*colormodename = "";
2222 if(blendingColorSpace) {
2223 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2225 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);
2226 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);
2228 //states[statepos].createsoftmask |= forSoftMask;
2229 states[statepos].createsoftmask = forSoftMask;
2230 states[statepos].transparencygroup = !forSoftMask;
2231 states[statepos].isolated = isolated;
2233 states[statepos].olddevice = this->device;
2234 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2236 gfxdevice_record_init(this->device);
2238 /*if(!forSoftMask) { ////???
2239 state->setFillOpacity(0.0);
2244 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2247 dbg("endTransparencyGroup");
2248 msg("<verbose> endTransparencyGroup");
2250 gfxdevice_t*r = this->device;
2252 this->device = states[statepos].olddevice;
2254 gfxresult_t*recording = r->finish(r);
2255 if(states[statepos].createsoftmask) {
2256 states[statepos-1].softmaskrecording = recording;
2258 states[statepos-1].grouprecording = recording;
2261 states[statepos].createsoftmask = 0;
2262 states[statepos].transparencygroup = 0;
2266 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2268 const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2269 "colordodge","colorburn","hardlight","softlight","difference",
2270 "exclusion","hue","saturation","color","luminosity"};
2272 dbg("paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2273 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2275 if(state->getBlendMode() == gfxBlendNormal)
2276 infofeature("transparency groups");
2279 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2280 warnfeature(buffer, 0);
2283 gfxresult_t*grouprecording = states[statepos].grouprecording;
2285 if(state->getBlendMode() == gfxBlendNormal) {
2287 gfxdevice_ops_init(&ops, this->device, (unsigned char)(state->getFillOpacity()*255));
2288 gfxresult_record_replay(grouprecording, &ops);
2291 grouprecording->destroy(grouprecording);
2293 states[statepos].grouprecording = 0;
2296 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2298 /* alpha = 1: retrieve mask values from alpha layer
2299 alpha = 0: retrieve mask values from luminance */
2300 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2301 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2302 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2303 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2305 infofeature("soft masks");
2307 warnfeature("soft masks from alpha channel",0);
2309 states[statepos].olddevice = this->device;
2310 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2311 gfxdevice_record_init(this->device);
2313 dbg("softmaskrecording is %08x at statepos %d\n", states[statepos].softmaskrecording, statepos);
2315 states[statepos].softmask = 1;
2316 states[statepos].softmask_alpha = alpha;
2319 static inline Guchar div255(int x) {
2320 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2323 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2325 if(c < min) c = min;
2326 if(c > max) c = max;
2330 void GFXOutputDev::clearSoftMask(GfxState *state)
2332 if(!states[statepos].softmask)
2334 states[statepos].softmask = 0;
2335 dbg("clearSoftMask statepos=%d", statepos);
2336 msg("<verbose> clearSoftMask");
2338 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2339 msg("<error> Error in softmask/tgroup ordering");
2343 gfxresult_t*mask = states[statepos].softmaskrecording;
2344 gfxresult_t*below = this->device->finish(this->device);
2345 this->device = states[statepos].olddevice;
2347 /* get outline of all objects below the soft mask */
2348 gfxdevice_t uniondev;
2349 gfxdevice_union_init(&uniondev, 0);
2350 gfxresult_record_replay(below, &uniondev);
2351 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2352 uniondev.finish(&uniondev);
2354 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2356 this->device->startclip(this->device, belowoutline);
2357 gfxresult_record_replay(below, this->device);
2358 gfxresult_record_replay(mask, this->device);
2359 this->device->endclip(this->device);
2360 gfxline_free(belowoutline);
2363 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2364 if(width<=0 || height<=0)
2367 gfxdevice_t belowrender;
2368 gfxdevice_render_init(&belowrender);
2369 if(states[statepos+1].isolated) {
2370 belowrender.setparameter(&belowrender, "fillwhite", "1");
2372 belowrender.setparameter(&belowrender, "antialize", "2");
2373 belowrender.startpage(&belowrender, width, height);
2374 gfxresult_record_replay(below, &belowrender);
2375 belowrender.endpage(&belowrender);
2376 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2377 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2378 //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2380 gfxdevice_t maskrender;
2381 gfxdevice_render_init(&maskrender);
2382 maskrender.startpage(&maskrender, width, height);
2383 gfxresult_record_replay(mask, &maskrender);
2384 maskrender.endpage(&maskrender);
2385 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2386 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2388 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2389 msg("<fatal> Internal error in mask drawing");
2394 for(y=0;y<height;y++) {
2395 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2396 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2397 for(x=0;x<width;x++) {
2399 if(states[statepos].softmask_alpha) {
2402 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2405 l2->a = div255(alpha*l2->a);
2407 /* DON'T premultiply alpha- this is done by fillbitmap,
2408 depending on the output device */
2409 //l2->r = div255(alpha*l2->r);
2410 //l2->g = div255(alpha*l2->g);
2411 //l2->b = div255(alpha*l2->b);
2417 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2420 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2421 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2423 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2425 mask->destroy(mask);
2426 below->destroy(below);
2427 maskresult->destroy(maskresult);
2428 belowresult->destroy(belowresult);
2429 states[statepos].softmaskrecording = 0;
2434 // public: ~MemCheck()
2436 // delete globalParams;globalParams=0;
2437 // Object::memCheck(stderr);
2438 // gMemReport(stderr);