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 && maskWidth<=1 && maskHeight<=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]];
1909 if(maskWidth < width && maskHeight < height) {
1910 for(y = 0; y < height; y++) {
1911 for (x = 0; x < width; x++) {
1912 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
1916 msg("<verbose> resampling %dx%d to mask size (%dx%d)", width, height, maskWidth, maskHeight);
1917 gfxcolor_t*newpic=new gfxcolor_t[maskWidth*maskHeight];
1918 double dx = width / maskWidth;
1919 double dy = height / maskHeight;
1921 for(y = 0; y < maskHeight; y++) {
1923 for (x = 0; x < maskWidth; x++) {
1924 newpic[maskWidth*y+x] = pic[int(yy)*width+int(xx)];
1925 newpic[maskWidth*y+x].a = maskbitmap[maskWidth*y+x];
1933 height = maskHeight;
1936 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1940 if(maskbitmap) free(maskbitmap);
1945 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
1946 int width, int height, GBool invert,
1949 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
1950 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
1951 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
1954 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
1955 int width, int height, GfxImageColorMap *colorMap,
1956 int *maskColors, GBool inlineImg)
1958 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
1959 colorMap?"colorMap":"no colorMap",
1960 maskColors?"maskColors":"no maskColors",
1962 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
1963 colorMap?"colorMap":"no colorMap",
1964 maskColors?"maskColors":"no maskColors",
1967 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
1968 colorMap->getBits(),colorMap->getColorSpace()->getMode());
1969 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
1972 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
1973 int width, int height,
1974 GfxImageColorMap *colorMap,
1975 Stream *maskStr, int maskWidth, int maskHeight,
1978 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
1979 colorMap?"colorMap":"no colorMap",
1980 maskWidth, maskHeight);
1981 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
1982 colorMap?"colorMap":"no colorMap",
1983 maskWidth, maskHeight);
1985 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
1986 colorMap->getBits(),colorMap->getColorSpace()->getMode());
1987 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
1990 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
1991 int width, int height,
1992 GfxImageColorMap *colorMap,
1994 int maskWidth, int maskHeight,
1995 GfxImageColorMap *maskColorMap)
1997 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
1998 colorMap?"colorMap":"no colorMap",
1999 maskWidth, maskHeight);
2000 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2001 colorMap?"colorMap":"no colorMap",
2002 maskWidth, maskHeight);
2004 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2005 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2006 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2009 void GFXOutputDev::stroke(GfxState *state)
2013 GfxPath * path = state->getPath();
2014 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2015 strokeGfxline(state, line, 0);
2019 void GFXOutputDev::fill(GfxState *state)
2021 gfxcolor_t col = getFillColor(state);
2022 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2024 GfxPath * path = state->getPath();
2025 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2026 fillGfxLine(state, line);
2030 void GFXOutputDev::eoFill(GfxState *state)
2032 gfxcolor_t col = getFillColor(state);
2033 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2035 GfxPath * path = state->getPath();
2036 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2037 fillGfxLine(state, line);
2042 static const char* dirseparator()
2051 void addGlobalFont(const char*filename)
2053 fontfile_t* f = (fontfile_t*)malloc(sizeof(fontfile_t));
2054 memset(f, 0, sizeof(fontfile_t));
2055 f->filename = filename;
2056 int len = strlen(filename);
2057 char*r1 = strrchr(filename, '/');
2058 char*r2 = strrchr(filename, '\\');
2066 msg("<notice> Adding font \"%s\".", filename);
2067 if(global_fonts_next) {
2068 global_fonts_next->next = f;
2069 global_fonts_next = global_fonts_next->next;
2071 global_fonts_next = global_fonts = f;
2075 void addGlobalLanguageDir(const char*dir)
2077 msg("<notice> Adding %s to language pack directories", dir);
2080 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2081 strcpy(config_file, dir);
2082 strcat(config_file, dirseparator());
2083 strcat(config_file, "add-to-xpdfrc");
2085 fi = fopen(config_file, "rb");
2087 msg("<error> Could not open %s", config_file);
2090 globalParams->parseFile(new GString(config_file), fi);
2094 void addGlobalFontDir(const char*dirname)
2096 #ifdef HAVE_DIRENT_H
2097 msg("<notice> Adding %s to font directories", dirname);
2098 lastfontdir = strdup(dirname);
2099 DIR*dir = opendir(dirname);
2101 msg("<warning> Couldn't open directory %s\n", dirname);
2106 ent = readdir (dir);
2110 char*name = ent->d_name;
2116 if(!strncasecmp(&name[l-4], ".pfa", 4))
2118 if(!strncasecmp(&name[l-4], ".pfb", 4))
2120 if(!strncasecmp(&name[l-4], ".ttf", 4))
2123 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2124 strcpy(fontname, dirname);
2125 strcat(fontname, dirseparator());
2126 strcat(fontname, name);
2127 addGlobalFont(fontname);
2132 msg("<warning> No dirent.h- unable to add font dir %s", dirname);
2136 void GFXOutputDev::preparePage(int pdfpage, int outputpage)
2142 this->pagebuflen = 1024;
2143 this->pages = (int*)malloc(this->pagebuflen*sizeof(int));
2144 memset(this->pages, -1, this->pagebuflen*sizeof(int));
2146 while(pdfpage >= this->pagebuflen)
2148 int oldlen = this->pagebuflen;
2149 this->pagebuflen+=1024;
2150 this->pages = (int*)realloc(this->pages, this->pagebuflen*sizeof(int));
2151 memset(&this->pages[oldlen], -1, (this->pagebuflen-oldlen)*sizeof(int));
2154 this->pages[pdfpage] = outputpage;
2155 if(pdfpage>this->pagepos)
2156 this->pagepos = pdfpage;
2162 double width,height;
2165 BBox mkBBox(GfxState*state, double*bbox, double width, double height)
2167 double xMin, yMin, xMax, yMax, x, y;
2168 double tx, ty, w, h;
2169 // transform the bbox
2170 state->transform(bbox[0], bbox[1], &x, &y);
2173 state->transform(bbox[0], bbox[3], &x, &y);
2176 } else if (x > xMax) {
2181 } else if (y > yMax) {
2184 state->transform(bbox[2], bbox[1], &x, &y);
2187 } else if (x > xMax) {
2192 } else if (y > yMax) {
2195 state->transform(bbox[2], bbox[3], &x, &y);
2198 } else if (x > xMax) {
2203 } else if (y > yMax) {
2206 tx = (int)floor(xMin);
2209 } else if (tx > width) {
2212 ty = (int)floor(yMin);
2215 } else if (ty > height) {
2218 w = (int)ceil(xMax) - tx + 1;
2219 if (tx + w > width) {
2225 h = (int)ceil(yMax) - ty + 1;
2226 if (ty + h > height) {
2240 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2241 GfxColorSpace *blendingColorSpace,
2242 GBool isolated, GBool knockout,
2245 const char*colormodename = "";
2247 if(blendingColorSpace) {
2248 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2250 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);
2251 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);
2253 //states[statepos].createsoftmask |= forSoftMask;
2254 states[statepos].createsoftmask = forSoftMask;
2255 states[statepos].transparencygroup = !forSoftMask;
2256 states[statepos].isolated = isolated;
2258 states[statepos].olddevice = this->device;
2259 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2261 gfxdevice_record_init(this->device);
2263 /*if(!forSoftMask) { ////???
2264 state->setFillOpacity(0.0);
2269 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2272 dbg("endTransparencyGroup");
2273 msg("<verbose> endTransparencyGroup");
2275 gfxdevice_t*r = this->device;
2277 this->device = states[statepos].olddevice;
2279 gfxresult_t*recording = r->finish(r);
2280 if(states[statepos].createsoftmask) {
2281 states[statepos-1].softmaskrecording = recording;
2283 states[statepos-1].grouprecording = recording;
2286 states[statepos].createsoftmask = 0;
2287 states[statepos].transparencygroup = 0;
2291 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2293 const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2294 "colordodge","colorburn","hardlight","softlight","difference",
2295 "exclusion","hue","saturation","color","luminosity"};
2297 dbg("paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2298 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2300 if(state->getBlendMode() == gfxBlendNormal)
2301 infofeature("transparency groups");
2304 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2305 warnfeature(buffer, 0);
2308 gfxresult_t*grouprecording = states[statepos].grouprecording;
2310 if(state->getBlendMode() == gfxBlendNormal) {
2312 gfxdevice_ops_init(&ops, this->device, (unsigned char)(state->getFillOpacity()*255));
2313 gfxresult_record_replay(grouprecording, &ops);
2316 grouprecording->destroy(grouprecording);
2318 states[statepos].grouprecording = 0;
2321 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2323 /* alpha = 1: retrieve mask values from alpha layer
2324 alpha = 0: retrieve mask values from luminance */
2325 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2326 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2327 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2328 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2330 infofeature("soft masks");
2332 warnfeature("soft masks from alpha channel",0);
2334 states[statepos].olddevice = this->device;
2335 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2336 gfxdevice_record_init(this->device);
2338 dbg("softmaskrecording is %08x at statepos %d\n", states[statepos].softmaskrecording, statepos);
2340 states[statepos].softmask = 1;
2341 states[statepos].softmask_alpha = alpha;
2344 static inline Guchar div255(int x) {
2345 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2348 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2350 if(c < min) c = min;
2351 if(c > max) c = max;
2355 void GFXOutputDev::clearSoftMask(GfxState *state)
2357 if(!states[statepos].softmask)
2359 states[statepos].softmask = 0;
2360 dbg("clearSoftMask statepos=%d", statepos);
2361 msg("<verbose> clearSoftMask");
2363 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2364 msg("<error> Error in softmask/tgroup ordering");
2368 gfxresult_t*mask = states[statepos].softmaskrecording;
2369 gfxresult_t*below = this->device->finish(this->device);
2370 this->device = states[statepos].olddevice;
2372 /* get outline of all objects below the soft mask */
2373 gfxdevice_t uniondev;
2374 gfxdevice_union_init(&uniondev, 0);
2375 gfxresult_record_replay(below, &uniondev);
2376 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2377 uniondev.finish(&uniondev);
2379 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2381 this->device->startclip(this->device, belowoutline);
2382 gfxresult_record_replay(below, this->device);
2383 gfxresult_record_replay(mask, this->device);
2384 this->device->endclip(this->device);
2385 gfxline_free(belowoutline);
2388 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2389 if(width<=0 || height<=0)
2392 gfxdevice_t belowrender;
2393 gfxdevice_render_init(&belowrender);
2394 if(states[statepos+1].isolated) {
2395 belowrender.setparameter(&belowrender, "fillwhite", "1");
2397 belowrender.setparameter(&belowrender, "antialize", "2");
2398 belowrender.startpage(&belowrender, width, height);
2399 gfxresult_record_replay(below, &belowrender);
2400 belowrender.endpage(&belowrender);
2401 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2402 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2403 //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2405 gfxdevice_t maskrender;
2406 gfxdevice_render_init(&maskrender);
2407 maskrender.startpage(&maskrender, width, height);
2408 gfxresult_record_replay(mask, &maskrender);
2409 maskrender.endpage(&maskrender);
2410 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2411 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2413 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2414 msg("<fatal> Internal error in mask drawing");
2419 for(y=0;y<height;y++) {
2420 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2421 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2422 for(x=0;x<width;x++) {
2424 if(states[statepos].softmask_alpha) {
2427 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2430 l2->a = div255(alpha*l2->a);
2432 /* DON'T premultiply alpha- this is done by fillbitmap,
2433 depending on the output device */
2434 //l2->r = div255(alpha*l2->r);
2435 //l2->g = div255(alpha*l2->g);
2436 //l2->b = div255(alpha*l2->b);
2442 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2445 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2446 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2448 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2450 mask->destroy(mask);
2451 below->destroy(below);
2452 maskresult->destroy(maskresult);
2453 belowresult->destroy(belowresult);
2454 states[statepos].softmaskrecording = 0;
2459 // public: ~MemCheck()
2461 // delete globalParams;globalParams=0;
2462 // Object::memCheck(stderr);
2463 // gMemReport(stderr);