2 implements a pdf output device (OutputDev).
4 This file is part of swftools.
6 Swftools is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 Swftools is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with swftools; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
26 #include "../../config.h"
31 #ifdef HAVE_SYS_STAT_H
34 #ifdef HAVE_FONTCONFIG
35 #include <fontconfig.h>
52 #include "OutputDev.h"
55 #include "CharCodeToUnicode.h"
56 #include "NameToUnicodeTable.h"
57 #include "GlobalParams.h"
58 #include "FoFiType1C.h"
59 #include "FoFiTrueType.h"
61 #include "GFXOutputDev.h"
63 //swftools header files
65 #include "../gfxdevice.h"
66 #include "../gfxtools.h"
67 #include "../gfxfont.h"
68 #include "../devices/record.h"
69 #include "../devices/ops.h"
70 #include "../devices/arts.h"
71 #include "../devices/render.h"
73 #include "../art/libart.h"
74 #include "../devices/artsutils.c"
81 typedef struct _fontfile
88 static fontfile_t fonts[2048];
89 static int fontnum = 0;
93 static char* lastfontdir = 0;
104 {"Times-Roman", "n021003l", n021003l_afm, n021003l_afm_len, n021003l_pfb, n021003l_pfb_len},
105 {"Times-Italic", "n021023l", n021023l_afm, n021023l_afm_len, n021023l_pfb, n021023l_pfb_len},
106 {"Times-Bold", "n021004l", n021004l_afm, n021004l_afm_len, n021004l_pfb, n021004l_pfb_len},
107 {"Times-BoldItalic", "n021024l", n021024l_afm, n021024l_afm_len, n021024l_pfb, n021024l_pfb_len},
108 {"Helvetica", "n019003l", n019003l_afm, n019003l_afm_len, n019003l_pfb, n019003l_pfb_len},
109 {"Helvetica-Oblique", "n019023l", n019023l_afm, n019023l_afm_len, n019023l_pfb, n019023l_pfb_len},
110 {"Helvetica-Bold", "n019004l", n019004l_afm, n019004l_afm_len, n019004l_pfb, n019004l_pfb_len},
111 {"Helvetica-BoldOblique", "n019024l", n019024l_afm, n019024l_afm_len, n019024l_pfb, n019024l_pfb_len},
112 {"Courier", "n022003l", n022003l_afm, n022003l_afm_len, n022003l_pfb, n022003l_pfb_len},
113 {"Courier-Oblique", "n022023l", n022023l_afm, n022023l_afm_len, n022023l_pfb, n022023l_pfb_len},
114 {"Courier-Bold", "n022004l", n022004l_afm, n022004l_afm_len, n022004l_pfb, n022004l_pfb_len},
115 {"Courier-BoldOblique", "n022024l", n022024l_afm, n022024l_afm_len, n022024l_pfb, n022024l_pfb_len},
116 {"Symbol", "s050000l", s050000l_afm, s050000l_afm_len, s050000l_pfb, s050000l_pfb_len},
117 {"ZapfDingbats", "d050000l", d050000l_afm, d050000l_afm_len, d050000l_pfb, d050000l_pfb_len}};
120 static int verbose = 0;
121 static int dbgindent = 0;
122 static void dbg(const char*format, ...)
129 va_start(arglist, format);
130 vsprintf(buf, format, arglist);
133 while(l && buf[l-1]=='\n') {
138 int indent = dbgindent;
148 typedef struct _feature
151 struct _feature*next;
153 feature_t*featurewarnings = 0;
155 void GFXOutputDev::showfeature(const char*feature,char fully, char warn)
157 feature_t*f = featurewarnings;
159 if(!strcmp(feature, f->string))
163 f = (feature_t*)malloc(sizeof(feature_t));
164 f->string = strdup(feature);
165 f->next = featurewarnings;
168 msg("<warning> %s not yet %ssupported!",feature,fully?"fully ":"");
169 if(this->config_break_on_warning) {
170 msg("<fatal> Aborting conversion due to unsupported feature");
174 msg("<notice> File contains %s",feature);
177 void GFXOutputDev::warnfeature(const char*feature,char fully)
179 showfeature(feature,fully,1);
181 void GFXOutputDev::infofeature(const char*feature)
183 showfeature(feature,0,0);
186 GFXOutputState::GFXOutputState() {
188 this->createsoftmask = 0;
189 this->transparencygroup = 0;
191 this->grouprecording = 0;
195 GBool GFXOutputDev::interpretType3Chars()
200 typedef struct _drawnchar
218 chars = (drawnchar_t*)malloc(sizeof(drawnchar_t)*buf_size);
219 memset(chars, 0, sizeof(drawnchar_t)*buf_size);
224 free(chars);chars = 0;
231 chars = (drawnchar_t*)realloc(chars, sizeof(drawnchar_t)*buf_size);
235 void addChar(int charid, gfxcoord_t x, gfxcoord_t y, gfxcolor_t color)
238 chars[num_chars].x = x;
239 chars[num_chars].y = y;
240 chars[num_chars].color = color;
241 chars[num_chars].charid = charid;
245 char* writeOutStdFont(fontentry* f)
250 char* tmpFileName = mktmpname(namebuf1);
252 sprintf(namebuf2, "%s.afm", tmpFileName);
253 fi = fopen(namebuf2, "wb");
256 fwrite(f->afm, 1, f->afmlen, fi);
259 sprintf(namebuf2, "%s.pfb", tmpFileName);
260 fi = fopen(namebuf2, "wb");
263 fwrite(f->pfb, 1, f->pfblen, fi);
265 return strdup(namebuf2);
267 void unlinkfont(char* filename)
272 msg("<verbose> Removing temporary font file %s", filename);
275 if(!strncmp(&filename[l-4],".afm",4)) {
276 memcpy(&filename[l-4],".pfb",4); unlink(filename);
277 memcpy(&filename[l-4],".pfa",4); unlink(filename);
278 memcpy(&filename[l-4],".afm",4);
281 if(!strncmp(&filename[l-4],".pfa",4)) {
282 memcpy(&filename[l-4],".afm",4); unlink(filename);
283 memcpy(&filename[l-4],".pfa",4);
286 if(!strncmp(&filename[l-4],".pfb",4)) {
287 memcpy(&filename[l-4],".afm",4); unlink(filename);
288 memcpy(&filename[l-4],".pfb",4);
294 GFXGlobalParams::GFXGlobalParams()
297 //setupBaseFonts(char *dir); //not tested yet
299 GFXGlobalParams::~GFXGlobalParams()
301 msg("<verbose> Performing cleanups");
303 for(t=0;t<sizeof(pdf2t1map)/sizeof(fontentry);t++) {
304 if(pdf2t1map[t].fullfilename) {
305 unlinkfont(pdf2t1map[t].fullfilename);
309 DisplayFontParam *GFXGlobalParams::getDisplayFont(GString *fontName)
311 msg("<verbose> looking for font %s in global params\n", fontName->getCString());
313 char*name = fontName->getCString();
314 /* see if it is a pdf standard font */
316 for(t=0;t<sizeof(pdf2t1map)/sizeof(fontentry);t++) {
317 if(!strcmp(name, pdf2t1map[t].pdffont)) {
318 if(!pdf2t1map[t].fullfilename) {
319 pdf2t1map[t].fullfilename = writeOutStdFont(&pdf2t1map[t]);
320 if(!pdf2t1map[t].fullfilename) {
321 msg("<error> Couldn't save default font- is the Temp Directory writable?");
323 msg("<verbose> Storing standard PDF font %s at %s", name, pdf2t1map[t].fullfilename);
326 DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), displayFontT1);
327 dfp->t1.fileName = new GString(pdf2t1map[t].fullfilename);
331 for(t=0;t<fontnum;t++) {
332 if(strstr(fonts[t].filename, name)) {
333 DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), displayFontT1);
334 dfp->t1.fileName = new GString(fonts[t].filename);
338 return GlobalParams::getDisplayFont(fontName);
341 GFXOutputDev::GFXOutputDev(parameter_t*p)
344 this->textmodeinfo = 0;
347 this->type3active = 0;
350 this->substitutepos = 0;
351 this->type3Warning = 0;
352 this->user_movex = 0;
353 this->user_movey = 0;
356 this->user_clipx1 = 0;
357 this->user_clipy1 = 0;
358 this->user_clipx2 = 0;
359 this->user_clipy2 = 0;
360 this->current_text_stroke = 0;
361 this->current_text_clip = 0;
362 this->outer_clip_box = 0;
364 this->pagebuflen = 0;
366 this->config_use_fontconfig=1;
367 this->config_break_on_warning=0;
368 this->config_remapunicode=0;
369 this->config_transparent=0;
370 this->config_extrafontdata = 0;
372 this->parameters = p;
374 this->gfxfontlist = gfxfontlist_create();
376 memset(states, 0, sizeof(states));
378 /* configure device */
380 setParameter(p->name, p->value);
385 void GFXOutputDev::setParameter(const char*key, const char*value)
387 if(!strcmp(key,"breakonwarning")) {
388 this->config_break_on_warning = atoi(value);
389 } else if(!strcmp(key,"fontconfig")) {
390 this->config_use_fontconfig = atoi(value);
391 } else if(!strcmp(key,"remapunicode")) {
392 this->config_remapunicode = atoi(value);
393 } else if(!strcmp(key,"transparent")) {
394 this->config_transparent = atoi(value);
395 } else if(!strcmp(key,"extrafontdata")) {
396 this->config_extrafontdata = atoi(value);
400 void GFXOutputDev::setDevice(gfxdevice_t*dev)
402 parameter_t*p = this->parameters;
404 /* pass parameters to output device */
408 this->device->setparameter(this->device, p->name, p->value);
414 void GFXOutputDev::setMove(int x,int y)
416 this->user_movex = x;
417 this->user_movey = y;
420 void GFXOutputDev::setClip(int x1,int y1,int x2,int y2)
422 if(x2<x1) {int x3=x1;x1=x2;x2=x3;}
423 if(y2<y1) {int y3=y1;y1=y2;y2=y3;}
425 this->user_clipx1 = x1;
426 this->user_clipy1 = y1;
427 this->user_clipx2 = x2;
428 this->user_clipy2 = y2;
431 static char*getFontName(GfxFont*font)
434 GString*gstr = font->getName();
435 char* fname = gstr==0?0:gstr->getCString();
439 sprintf(buf, "UFONT%d", r->num);
440 fontid = strdup(buf);
442 fontid = strdup(fname);
446 char* plus = strchr(fontid, '+');
447 if(plus && plus < &fontid[strlen(fontid)-1]) {
448 fontname = strdup(plus+1);
450 fontname = strdup(fontid);
456 static void dumpFontInfo(const char*loglevel, GfxFont*font);
457 static int lastdumps[1024];
458 static int lastdumppos = 0;
463 static void showFontError(GfxFont*font, int nr)
467 for(t=0;t<lastdumppos;t++)
468 if(lastdumps[t] == r->num)
472 if(lastdumppos<sizeof(lastdumps)/sizeof(int))
473 lastdumps[lastdumppos++] = r->num;
475 msg("<warning> The following font caused problems:");
477 msg("<warning> The following font caused problems (substituting):");
479 msg("<warning> The following Type 3 Font will be rendered as graphics:");
480 dumpFontInfo("<warning>", font);
483 static void dumpFontInfo(const char*loglevel, GfxFont*font)
485 char* id = getFontID(font);
486 char* name = getFontName(font);
487 Ref* r=font->getID();
488 msg("%s=========== %s (ID:%d,%d) ==========\n", loglevel, name, r->num,r->gen);
490 GString*gstr = font->getTag();
492 msg("%s| Tag: %s\n", loglevel, id);
494 if(font->isCIDFont()) msg("%s| is CID font\n", loglevel);
496 GfxFontType type=font->getType();
498 case fontUnknownType:
499 msg("%s| Type: unknown\n",loglevel);
502 msg("%s| Type: 1\n",loglevel);
505 msg("%s| Type: 1C\n",loglevel);
508 msg("%s| Type: 3\n",loglevel);
511 msg("%s| Type: TrueType\n",loglevel);
514 msg("%s| Type: CIDType0\n",loglevel);
517 msg("%s| Type: CIDType0C\n",loglevel);
520 msg("%s| Type: CIDType2\n",loglevel);
525 GBool embedded = font->getEmbeddedFontID(&embRef);
527 if(font->getEmbeddedFontName()) {
528 embeddedName = font->getEmbeddedFontName()->getCString();
531 msg("%s| Embedded id: %s id: %d\n",loglevel, FIXNULL(embeddedName), embRef.num);
533 gstr = font->getExtFontFile();
535 msg("%s| External Font file: %s\n", loglevel, FIXNULL(gstr->getCString()));
537 // Get font descriptor flags.
538 if(font->isFixedWidth()) msg("%s| is fixed width\n", loglevel);
539 if(font->isSerif()) msg("%s| is serif\n", loglevel);
540 if(font->isSymbolic()) msg("%s| is symbolic\n", loglevel);
541 if(font->isItalic()) msg("%s| is italic\n", loglevel);
542 if(font->isBold()) msg("%s| is bold\n", loglevel);
548 //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");}
549 //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");}
551 void dump_outline(gfxline_t*line)
554 if(line->type == gfx_moveTo) {
555 msg("<debug> | moveTo %.2f %.2f", line->x,line->y);
556 } else if(line->type == gfx_lineTo) {
557 msg("<debug> | lineTo %.2f %.2f", line->x,line->y);
558 } else if(line->type == gfx_splineTo) {
559 msg("<debug> | splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y);
565 gfxline_t* gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user_movex, int user_movey)
567 int num = path->getNumSubpaths();
570 double lastx=0,lasty=0,posx=0,posy=0;
573 msg("<warning> empty path");
577 gfxdrawer_target_gfxline(&draw);
579 for(t = 0; t < num; t++) {
580 GfxSubpath *subpath = path->getSubpath(t);
581 int subnum = subpath->getNumPoints();
582 double bx=0,by=0,cx=0,cy=0;
584 for(s=0;s<subnum;s++) {
587 state->transform(subpath->getX(s),subpath->getY(s),&x,&y);
592 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
593 draw.lineTo(&draw, lastx, lasty);
595 draw.moveTo(&draw, x,y);
600 } else if(subpath->getCurve(s) && cpos==0) {
604 } else if(subpath->getCurve(s) && cpos==1) {
612 draw.lineTo(&draw, x,y);
614 gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y, 0.05);
621 /* fix non-closed lines */
622 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
623 draw.lineTo(&draw, lastx, lasty);
625 gfxline_t*result = (gfxline_t*)draw.result(&draw);
627 gfxline_optimize(result);
632 GBool GFXOutputDev::useTilingPatternFill()
634 infofeature("tiled patterns");
638 GBool GFXOutputDev::useShadedFills()
640 infofeature("shaded fills");
644 GBool GFXOutputDev::useDrawForm()
646 infofeature("forms");
649 void GFXOutputDev::drawForm(Ref id)
651 msg("<error> drawForm not implemented");
653 GBool GFXOutputDev::needNonText()
657 void GFXOutputDev::endPage()
659 msg("<verbose> endPage");
662 #define STROKE_FILL 1
663 #define STROKE_CLIP 2
664 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line, int flags)
666 int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
667 int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
668 double miterLimit = state->getMiterLimit();
669 double width = state->getTransformedLineWidth();
672 double opaq = state->getStrokeOpacity();
674 state->getFillRGB(&rgb);
676 state->getStrokeRGB(&rgb);
678 col.r = colToByte(rgb.r);
679 col.g = colToByte(rgb.g);
680 col.b = colToByte(rgb.b);
681 col.a = (unsigned char)(opaq*255);
683 gfx_capType capType = gfx_capRound;
684 if(lineCap == 0) capType = gfx_capButt;
685 else if(lineCap == 1) capType = gfx_capRound;
686 else if(lineCap == 2) capType = gfx_capSquare;
688 gfx_joinType joinType = gfx_joinRound;
689 if(lineJoin == 0) joinType = gfx_joinMiter;
690 else if(lineJoin == 1) joinType = gfx_joinRound;
691 else if(lineJoin == 2) joinType = gfx_joinBevel;
694 double dashphase = 0;
696 state->getLineDash(&ldash, &dashnum, &dashphase);
700 if(dashnum && ldash) {
701 float * dash = (float*)malloc(sizeof(float)*(dashnum+1));
703 msg("<trace> %d dashes", dashnum);
704 msg("<trace> | phase: %f", dashphase);
705 for(t=0;t<dashnum;t++) {
707 msg("<trace> | d%-3d: %f", t, ldash[t]);
710 if(getLogLevel() >= LOGLEVEL_TRACE) {
714 line2 = gfxtool_dash_line(line, dash, dashphase);
717 msg("<trace> After dashing:");
720 if(getLogLevel() >= LOGLEVEL_TRACE) {
721 msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x\n",
723 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
724 lineCap==0?"butt": (lineJoin==1?"round":"square"),
726 col.r,col.g,col.b,col.a
731 if(flags&STROKE_FILL) {
732 ArtSVP* svp = gfxstrokeToSVP(line, width, capType, joinType, miterLimit);
733 gfxline_t*gfxline = SVPtogfxline(svp);
734 if(getLogLevel() >= LOGLEVEL_TRACE) {
735 dump_outline(gfxline);
738 msg("<warning> Empty polygon (resulting from stroked line)");
740 if(flags&STROKE_CLIP) {
741 device->startclip(device, gfxline);
742 states[statepos].clipping++;
744 device->fill(device, gfxline, &col);
749 if(flags&STROKE_CLIP)
750 msg("<error> Stroke&clip not supported at the same time");
751 device->stroke(device, line, width, &col, capType, joinType, miterLimit);
758 gfxcolor_t getFillColor(GfxState * state)
761 double opaq = state->getFillOpacity();
762 state->getFillRGB(&rgb);
764 col.r = colToByte(rgb.r);
765 col.g = colToByte(rgb.g);
766 col.b = colToByte(rgb.b);
767 col.a = (unsigned char)(opaq*255);
771 void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line)
773 gfxcolor_t col = getFillColor(state);
775 if(getLogLevel() >= LOGLEVEL_TRACE) {
776 msg("<trace> fill %02x%02x%02x%02x\n", col.r, col.g, col.b, col.a);
779 device->fill(device, line, &col);
782 void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line)
784 if(getLogLevel() >= LOGLEVEL_TRACE) {
785 msg("<trace> clip\n");
789 device->startclip(device, line);
790 states[statepos].clipping++;
793 void GFXOutputDev::clip(GfxState *state)
795 GfxPath * path = state->getPath();
796 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
797 clipToGfxLine(state, line);
801 void GFXOutputDev::eoClip(GfxState *state)
803 GfxPath * path = state->getPath();
804 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
806 if(getLogLevel() >= LOGLEVEL_TRACE) {
807 msg("<trace> eoclip\n");
811 device->startclip(device, line);
812 states[statepos].clipping++;
815 void GFXOutputDev::clipToStrokePath(GfxState *state)
817 GfxPath * path = state->getPath();
818 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
820 if(getLogLevel() >= LOGLEVEL_TRACE) {
821 msg("<trace> cliptostrokepath\n");
825 strokeGfxline(state, line, STROKE_FILL|STROKE_CLIP);
829 void GFXOutputDev::endframe()
832 device->endclip(device);
837 void GFXOutputDev::finish()
841 device->endclip(device);
847 GFXOutputDev::~GFXOutputDev()
852 free(this->pages); this->pages = 0;
855 gfxfontlist_free(this->gfxfontlist, 1);
857 GBool GFXOutputDev::upsideDown()
861 GBool GFXOutputDev::useDrawChar()
866 const char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
867 "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
869 #define RENDER_FILL 0
870 #define RENDER_STROKE 1
871 #define RENDER_FILLSTROKE 2
872 #define RENDER_INVISIBLE 3
873 #define RENDER_CLIP 4
875 static char tmp_printstr[4096];
876 char* makeStringPrintable(char*str)
878 int len = strlen(str);
893 tmp_printstr[len++] = '.';
894 tmp_printstr[len++] = '.';
895 tmp_printstr[len++] = '.';
897 tmp_printstr[len] = 0;
900 #define INTERNAL_FONT_SIZE 1024.0
901 void GFXOutputDev::updateFontMatrix(GfxState*state)
903 double* ctm = state->getCTM();
904 double fontSize = state->getFontSize();
905 double*textMat = state->getTextMat();
907 /* taking the absolute value of horizScaling seems to be required for
908 some italic fonts. FIXME: SplashOutputDev doesn't need this- why? */
909 double hscale = fabs(state->getHorizScaling());
911 // from xpdf-3.02/SplashOutputDev:updateFont
912 double mm11 = textMat[0] * fontSize * hscale;
913 double mm12 = textMat[1] * fontSize * hscale;
914 double mm21 = textMat[2] * fontSize;
915 double mm22 = textMat[3] * fontSize;
917 // multiply with ctm, like state->getFontTransMat() does
918 this->current_font_matrix.m00 = (ctm[0]*mm11 + ctm[2]*mm12) / INTERNAL_FONT_SIZE;
919 this->current_font_matrix.m01 = (ctm[1]*mm11 + ctm[3]*mm12) / INTERNAL_FONT_SIZE;
920 this->current_font_matrix.m10 = (ctm[0]*mm21 + ctm[2]*mm22) / INTERNAL_FONT_SIZE;
921 this->current_font_matrix.m11 = (ctm[1]*mm21 + ctm[3]*mm22) / INTERNAL_FONT_SIZE;
922 this->current_font_matrix.tx = 0;
923 this->current_font_matrix.ty = 0;
926 void GFXOutputDev::beginString(GfxState *state, GString *s)
928 int render = state->getRender();
929 if(current_text_stroke) {
930 msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
933 msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
936 static gfxline_t* mkEmptyGfxShape(double x, double y)
938 gfxline_t*line = (gfxline_t*)malloc(sizeof(gfxline_t));
939 line->x = x;line->y = y;line->type = gfx_moveTo;line->next = 0;
943 static char isValidUnicode(int c)
945 if(c>=32 && c<0x2fffe)
950 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
951 double dx, double dy,
952 double originX, double originY,
953 CharCode charid, int nBytes, Unicode *_u, int uLen)
955 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
956 msg("<error> Invalid charid %d for font %s", charid, current_font_id);
960 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
962 int render = state->getRender();
963 gfxcolor_t col = getFillColor(state);
965 // check for invisible text -- this is used by Acrobat Capture
966 if (render == RENDER_INVISIBLE) {
968 if(!config_extrafontdata)
972 GfxFont*font = state->getFont();
974 if(font->getType() == fontType3) {
975 /* type 3 chars are passed as graphics */
976 msg("<debug> type3 char at %f/%f", x, y);
980 Unicode u = uLen?(_u[0]):0;
981 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);
983 gfxmatrix_t m = this->current_font_matrix;
984 state->transform(x, y, &m.tx, &m.ty);
985 m.tx += user_movex + clipmovex;
986 m.ty += user_movey + clipmovey;
988 if(render == RENDER_FILL || render == RENDER_INVISIBLE) {
989 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
991 msg("<debug> Drawing glyph %d as shape", charid);
993 msg("<notice> Some texts will be rendered as shape");
996 gfxline_t*glyph = current_gfxfont->glyphs[glyphid].line;
997 gfxline_t*tglyph = gfxline_clone(glyph);
998 gfxline_transform(tglyph, &m);
999 if((render&3) != RENDER_INVISIBLE) {
1000 gfxline_t*add = gfxline_clone(tglyph);
1001 current_text_stroke = gfxline_append(current_text_stroke, add);
1003 if(render&RENDER_CLIP) {
1004 gfxline_t*add = gfxline_clone(tglyph);
1005 current_text_clip = gfxline_append(current_text_clip, add);
1006 if(!current_text_clip) {
1007 current_text_clip = mkEmptyGfxShape(m.tx, m.ty);
1010 gfxline_free(tglyph);
1014 void GFXOutputDev::endString(GfxState *state)
1016 int render = state->getRender();
1017 msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
1019 if(current_text_stroke) {
1020 /* fillstroke and stroke text rendering objects we can process right
1021 now (as there may be texts of other rendering modes in this
1022 text object)- clipping objects have to wait until endTextObject,
1024 device->setparameter(device, "mark","TXT");
1025 if((render&3) == RENDER_FILL) {
1026 fillGfxLine(state, current_text_stroke);
1027 gfxline_free(current_text_stroke);
1028 current_text_stroke = 0;
1029 } else if((render&3) == RENDER_FILLSTROKE) {
1030 fillGfxLine(state, current_text_stroke);
1031 strokeGfxline(state, current_text_stroke,0);
1032 gfxline_free(current_text_stroke);
1033 current_text_stroke = 0;
1034 } else if((render&3) == RENDER_STROKE) {
1035 strokeGfxline(state, current_text_stroke,0);
1036 gfxline_free(current_text_stroke);
1037 current_text_stroke = 0;
1039 device->setparameter(device, "mark","");
1043 void GFXOutputDev::endTextObject(GfxState *state)
1045 int render = state->getRender();
1046 msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1048 if(current_text_clip) {
1049 device->setparameter(device, "mark","TXT");
1050 clipToGfxLine(state, current_text_clip);
1051 device->setparameter(device, "mark","");
1052 gfxline_free(current_text_clip);
1053 current_text_clip = 0;
1057 /* the logic seems to be as following:
1058 first, beginType3Char is called, with the charcode and the coordinates.
1059 if this function returns true, it already knew about the char and has now drawn it.
1060 if the function returns false, it's a new char, and type3D0 and/or type3D1 might be
1061 called with some parameters.
1062 Afterwards, all draw operations until endType3Char are part of the char (which in this moment is
1063 at the position first passed to beginType3Char). the char ends with endType3Char.
1065 The drawing operations between beginType3Char and endType3Char are somewhat different to
1066 the normal ones. For example, the fillcolor equals the stroke color. (Because the stroke
1067 color determines the color of a font)
1070 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode charid, Unicode *u, int uLen)
1072 msg("<debug> beginType3Char %d u=%d", charid, uLen?u[0]:0);
1075 if(config_extrafontdata && current_fontinfo) {
1077 gfxmatrix_t m = this->current_font_matrix;
1078 state->transform(0, 0, &m.tx, &m.ty);
1079 m.m00*=INTERNAL_FONT_SIZE;
1080 m.m01*=INTERNAL_FONT_SIZE;
1081 m.m10*=INTERNAL_FONT_SIZE;
1082 m.m11*=INTERNAL_FONT_SIZE;
1083 m.tx += user_movex + clipmovex;
1084 m.ty += user_movey + clipmovey;
1086 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1087 msg("<error> Invalid charid %d for font %s", charid, current_font_id);
1090 gfxcolor_t col={0,0,0,0};
1091 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1092 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1096 /* the character itself is going to be passed using the draw functions */
1097 return gFalse; /* gTrue= is_in_cache? */
1100 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1102 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1105 void GFXOutputDev::endType3Char(GfxState *state)
1108 msg("<debug> endType3Char");
1111 void GFXOutputDev::startFrame(int width, int height)
1113 if(outer_clip_box) {
1114 device->endclip(device);
1118 device->startpage(device, width, height);
1119 this->width = width;
1120 this->height = height;
1123 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
1125 this->currentpage = pageNum;
1127 int rot = doc->getPageRotate(1);
1130 gfxline_t clippath[5];
1132 white.r = white.g = white.b = white.a = 255;
1134 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1135 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1136 Use CropBox, not MediaBox, as page size
1143 state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1144 state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1146 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1147 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1149 this->clipmovex = -(int)x1;
1150 this->clipmovey = -(int)y1;
1152 /* apply user clip box */
1153 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1154 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1155 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1156 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1157 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1158 msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1161 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1163 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);
1165 msg("<verbose> page is rotated %d degrees\n", rot);
1167 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1168 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1169 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1170 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1171 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1172 device->startclip(device, clippath); outer_clip_box = 1;
1173 if(!config_transparent) {
1174 device->fill(device, clippath, &white);
1179 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1181 double x1, y1, x2, y2, w;
1182 gfxline_t points[5];
1185 msg("<debug> drawlink\n");
1187 link->getRect(&x1, &y1, &x2, &y2);
1188 cvtUserToDev(x1, y1, &x, &y);
1189 points[0].type = gfx_moveTo;
1190 points[0].x = points[4].x = x + user_movex + clipmovex;
1191 points[0].y = points[4].y = y + user_movey + clipmovey;
1192 points[0].next = &points[1];
1193 cvtUserToDev(x2, y1, &x, &y);
1194 points[1].type = gfx_lineTo;
1195 points[1].x = x + user_movex + clipmovex;
1196 points[1].y = y + user_movey + clipmovey;
1197 points[1].next = &points[2];
1198 cvtUserToDev(x2, y2, &x, &y);
1199 points[2].type = gfx_lineTo;
1200 points[2].x = x + user_movex + clipmovex;
1201 points[2].y = y + user_movey + clipmovey;
1202 points[2].next = &points[3];
1203 cvtUserToDev(x1, y2, &x, &y);
1204 points[3].type = gfx_lineTo;
1205 points[3].x = x + user_movex + clipmovex;
1206 points[3].y = y + user_movey + clipmovey;
1207 points[3].next = &points[4];
1208 cvtUserToDev(x1, y1, &x, &y);
1209 points[4].type = gfx_lineTo;
1210 points[4].x = x + user_movex + clipmovex;
1211 points[4].y = y + user_movey + clipmovey;
1214 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f\n",
1215 points[0].x, points[0].y,
1216 points[1].x, points[1].y,
1217 points[2].x, points[2].y,
1218 points[3].x, points[3].y);
1220 LinkAction*action=link->getAction();
1223 const char*type = "-?-";
1226 msg("<trace> drawlink action=%d\n", action->getKind());
1227 switch(action->getKind())
1231 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1232 LinkDest *dest=NULL;
1233 if (ha->getDest()==NULL)
1234 dest=catalog->findDest(ha->getNamedDest());
1235 else dest=ha->getDest();
1237 if (dest->isPageRef()){
1238 Ref pageref=dest->getPageRef();
1239 page=catalog->findPage(pageref.num,pageref.gen);
1241 else page=dest->getPageNum();
1242 sprintf(buf, "%d", page);
1249 LinkGoToR*l = (LinkGoToR*)action;
1250 GString*g = l->getFileName();
1252 s = strdup(g->getCString());
1254 /* if the GoToR link has no filename, then
1255 try to find a refernce in the *local*
1257 GString*g = l->getNamedDest();
1259 s = strdup(g->getCString());
1265 LinkNamed*l = (LinkNamed*)action;
1266 GString*name = l->getName();
1268 s = strdup(name->lowerCase()->getCString());
1269 named = name->getCString();
1272 if(strstr(s, "next") || strstr(s, "forward"))
1274 page = currentpage + 1;
1276 else if(strstr(s, "prev") || strstr(s, "back"))
1278 page = currentpage - 1;
1280 else if(strstr(s, "last") || strstr(s, "end"))
1282 if(pages && pagepos>0)
1283 page = pages[pagepos-1];
1285 else if(strstr(s, "first") || strstr(s, "top"))
1293 case actionLaunch: {
1295 LinkLaunch*l = (LinkLaunch*)action;
1296 GString * str = new GString(l->getFileName());
1297 GString * params = l->getParams();
1299 str->append(params);
1300 s = strdup(str->getCString());
1307 LinkURI*l = (LinkURI*)action;
1308 GString*g = l->getURI();
1310 url = g->getCString();
1315 case actionUnknown: {
1317 LinkUnknown*l = (LinkUnknown*)action;
1322 msg("<error> Unknown link type!\n");
1327 if(!s) s = strdup("-?-");
1329 msg("<trace> drawlink s=%s\n", s);
1331 if(!linkinfo && (page || s))
1333 msg("<notice> File contains links");
1341 for(t=1;t<=pagepos;t++) {
1342 if(pages[t]==page) {
1351 sprintf(buf, "page%d", lpage);
1352 device->drawlink(device, points, buf);
1356 device->drawlink(device, points, s);
1359 msg("<verbose> \"%s\" link to \"%s\" (%d)\n", type, FIXNULL(s), page);
1363 void GFXOutputDev::saveState(GfxState *state) {
1364 dbg("saveState");dbgindent+=2;
1366 msg("<trace> saveState\n");
1369 msg("<error> Too many nested states in pdf.");
1373 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1374 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1375 states[statepos].clipping = 0;
1378 void GFXOutputDev::restoreState(GfxState *state) {
1379 dbgindent-=2; dbg("restoreState");
1382 msg("<error> Invalid restoreState");
1385 msg("<trace> restoreState%s%s", states[statepos].softmask?" (end softmask)":"",
1386 states[statepos].clipping?" (end clipping)":"");
1387 if(states[statepos].softmask) {
1388 clearSoftMask(state);
1391 while(states[statepos].clipping) {
1392 device->endclip(device);
1393 states[statepos].clipping--;
1398 void GFXOutputDev::updateLineWidth(GfxState *state)
1400 double width = state->getTransformedLineWidth();
1401 //swfoutput_setlinewidth(&device, width);
1404 void GFXOutputDev::updateLineCap(GfxState *state)
1406 int c = state->getLineCap();
1409 void GFXOutputDev::updateLineJoin(GfxState *state)
1411 int j = state->getLineJoin();
1414 void GFXOutputDev::updateFillColor(GfxState *state)
1417 double opaq = state->getFillOpacity();
1418 state->getFillRGB(&rgb);
1420 void GFXOutputDev::updateFillOpacity(GfxState *state)
1423 double opaq = state->getFillOpacity();
1424 state->getFillRGB(&rgb);
1425 dbg("update fillopaq %f", opaq);
1427 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1429 double opaq = state->getFillOpacity();
1430 dbg("update strokeopaq %f", opaq);
1432 void GFXOutputDev::updateFillOverprint(GfxState *state)
1434 double opaq = state->getFillOverprint();
1435 dbg("update filloverprint %f", opaq);
1437 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1439 double opaq = state->getStrokeOverprint();
1440 dbg("update strokeoverprint %f", opaq);
1442 void GFXOutputDev::updateTransfer(GfxState *state)
1444 dbg("update transfer");
1448 void GFXOutputDev::updateStrokeColor(GfxState *state)
1451 double opaq = state->getStrokeOpacity();
1452 state->getStrokeRGB(&rgb);
1455 void GFXOutputDev::setXRef(PDFDoc*doc, XRef *xref)
1461 gfxfont_t* createGfxFont(GfxFont*xpdffont, FontInfo*src)
1463 gfxfont_t*font = (gfxfont_t*)malloc(sizeof(gfxfont_t));
1464 memset(font, 0, sizeof(gfxfont_t));
1466 font->glyphs = (gfxglyph_t*)malloc(sizeof(gfxglyph_t)*src->num_glyphs);
1467 memset(font->glyphs, 0, sizeof(gfxglyph_t)*src->num_glyphs);
1468 font->id = strdup(getFontID(xpdffont));
1470 double quality = (INTERNAL_FONT_SIZE * 0.05) / src->max_size;
1472 //printf("%d glyphs\n", font->num_glyphs);
1473 font->num_glyphs = 0;
1474 for(t=0;t<src->num_glyphs;t++) {
1475 if(src->glyphs[t]) {
1476 SplashPath*path = src->glyphs[t]->path;
1477 int len = path?path->getLength():0;
1478 //printf("glyph %d) %08x (%d line segments)\n", t, path, len);
1479 gfxglyph_t*glyph = &font->glyphs[font->num_glyphs];
1480 src->glyphs[t]->glyphid = font->num_glyphs;
1481 glyph->unicode = src->glyphs[t]->unicode;
1482 if(glyph->unicode >= font->max_unicode)
1483 font->max_unicode = glyph->unicode+1;
1485 gfxdrawer_target_gfxline(&drawer);
1489 for(s=0;s<len;s++) {
1492 path->getPoint(s, &x, &y, &f);
1495 if(f&splashPathFirst) {
1496 drawer.moveTo(&drawer, x*scale, y*scale);
1498 if(f&splashPathCurve) {
1500 path->getPoint(++s, &x2, &y2, &f);
1501 if(f&splashPathCurve) {
1503 path->getPoint(++s, &x3, &y3, &f);
1504 gfxdraw_cubicTo(&drawer, x*scale, y*scale, x2*scale, y2*scale, x3*scale, y3*scale, quality);
1506 drawer.splineTo(&drawer, x*scale, y*scale, x2*scale, y2*scale);
1509 drawer.lineTo(&drawer, x*scale, y*scale);
1511 // printf("%f %f %s %s\n", x, y, (f&splashPathCurve)?"curve":"",
1512 // (f&splashPathFirst)?"first":"",
1513 // (f&splashPathLast)?"last":"");
1515 glyph->line = (gfxline_t*)drawer.result(&drawer);
1516 glyph->advance = xmax*scale; // we don't know the real advance value, so this'll have to do
1520 font->unicode2glyph = (int*)malloc(sizeof(int)*font->max_unicode);
1521 memset(font->unicode2glyph, -1, sizeof(int)*font->max_unicode);
1522 for(t=0;t<font->num_glyphs;t++) {
1523 if(font->glyphs[t].unicode>0 && font->glyphs[t].unicode<font->max_unicode) {
1524 font->unicode2glyph[font->glyphs[t].unicode] = t;
1528 msg("<trace> %d glyphs.", t, font->num_glyphs);
1532 void GFXOutputDev::updateFont(GfxState *state)
1534 GfxFont* gfxFont = state->getFont();
1538 char*id = getFontID(gfxFont);
1539 msg("<verbose> Updating font to %s", id);
1540 if(gfxFont->getType() == fontType3) {
1541 infofeature("Type3 fonts");
1542 if(!config_extrafontdata) {
1547 msg("<error> Internal Error: FontID is null");
1551 this->current_fontinfo = this->info->getFont(id);
1552 if(!this->current_fontinfo) {
1553 msg("<error> Internal Error: no fontinfo for font %s\n", id);
1555 if(!this->current_fontinfo->seen) {
1556 dumpFontInfo("<verbose>", gfxFont);
1559 gfxfont_t*font = gfxfontlist_findfont(this->gfxfontlist,id);
1561 font = createGfxFont(gfxFont, current_fontinfo);
1562 gfxfontlist_addfont(this->gfxfontlist, font);
1563 device->addfont(device, font);
1565 current_gfxfont = font;
1568 updateFontMatrix(state);
1571 #define SQR(x) ((x)*(x))
1573 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
1575 if((newwidth<2 || newheight<2) ||
1576 (width<=newwidth || height<=newheight))
1578 unsigned char*newdata;
1580 newdata= (unsigned char*)malloc(newwidth*newheight);
1582 double fx = (double)(width)/newwidth;
1583 double fy = (double)(height)/newheight;
1585 int blocksize = (int)(8192/(fx*fy));
1586 int r = 8192*256/palettesize;
1587 for(x=0;x<newwidth;x++) {
1588 double ex = px + fx;
1589 int fromx = (int)px;
1591 int xweight1 = (int)(((fromx+1)-px)*256);
1592 int xweight2 = (int)((ex-tox)*256);
1594 for(y=0;y<newheight;y++) {
1595 double ey = py + fy;
1596 int fromy = (int)py;
1598 int yweight1 = (int)(((fromy+1)-py)*256);
1599 int yweight2 = (int)((ey-toy)*256);
1602 for(xx=fromx;xx<=tox;xx++)
1603 for(yy=fromy;yy<=toy;yy++) {
1604 int b = 1-data[width*yy+xx];
1606 if(xx==fromx) weight = (weight*xweight1)/256;
1607 if(xx==tox) weight = (weight*xweight2)/256;
1608 if(yy==fromy) weight = (weight*yweight1)/256;
1609 if(yy==toy) weight = (weight*yweight2)/256;
1612 //if(a) a=(palettesize-1)*r/blocksize;
1613 newdata[y*newwidth+x] = (a*blocksize)/r;
1621 #define IMAGE_TYPE_JPEG 0
1622 #define IMAGE_TYPE_LOSSLESS 1
1624 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
1625 double x1,double y1,
1626 double x2,double y2,
1627 double x3,double y3,
1628 double x4,double y4, int type)
1630 gfxcolor_t*newpic=0;
1632 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
1633 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
1635 gfxline_t p1,p2,p3,p4,p5;
1636 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
1637 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
1638 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
1639 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
1640 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
1642 {p1.x = (int)(p1.x*20)/20.0;
1643 p1.y = (int)(p1.y*20)/20.0;
1644 p2.x = (int)(p2.x*20)/20.0;
1645 p2.y = (int)(p2.y*20)/20.0;
1646 p3.x = (int)(p3.x*20)/20.0;
1647 p3.y = (int)(p3.y*20)/20.0;
1648 p4.x = (int)(p4.x*20)/20.0;
1649 p4.y = (int)(p4.y*20)/20.0;
1650 p5.x = (int)(p5.x*20)/20.0;
1651 p5.y = (int)(p5.y*20)/20.0;
1658 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
1659 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
1664 img.data = (gfxcolor_t*)data;
1668 if(type == IMAGE_TYPE_JPEG)
1669 /* TODO: pass image_dpi to device instead */
1670 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
1672 dev->fillbitmap(dev, &p1, &img, &m, 0);
1675 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
1676 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
1678 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG);
1681 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
1682 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
1684 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS);
1688 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
1689 int width, int height, GfxImageColorMap*colorMap, GBool invert,
1690 GBool inlineImg, int mask, int*maskColors,
1691 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
1693 double x1,y1,x2,y2,x3,y3,x4,y4;
1694 ImageStream *imgStr;
1699 unsigned char* maskbitmap = 0;
1702 ncomps = colorMap->getNumPixelComps();
1703 bits = colorMap->getBits();
1708 unsigned char buf[8];
1709 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
1711 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
1712 imgMaskStr->reset();
1713 unsigned char pal[256];
1714 int n = 1 << colorMap->getBits();
1719 maskColorMap->getGray(pixBuf, &gray);
1720 pal[t] = colToByte(gray);
1722 for (y = 0; y < maskHeight; y++) {
1723 for (x = 0; x < maskWidth; x++) {
1724 imgMaskStr->getPixel(buf);
1725 maskbitmap[y*maskWidth+x] = pal[buf[0]];
1730 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
1731 imgMaskStr->reset();
1732 for (y = 0; y < maskHeight; y++) {
1733 for (x = 0; x < maskWidth; x++) {
1734 imgMaskStr->getPixel(buf);
1736 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
1744 imgStr = new ImageStream(str, width, ncomps,bits);
1747 if(!width || !height || (height<=1 && width<=1))
1749 msg("<verbose> Ignoring %d by %d image", width, height);
1750 unsigned char buf[8];
1752 for (y = 0; y < height; ++y)
1753 for (x = 0; x < width; ++x) {
1754 imgStr->getPixel(buf);
1762 state->transform(0, 1, &x1, &y1); x1 += user_movex + clipmovex; y1 += user_movey + clipmovey;
1763 state->transform(0, 0, &x2, &y2); x2 += user_movex + clipmovex; y2 += user_movey + clipmovey;
1764 state->transform(1, 0, &x3, &y3); x3 += user_movex + clipmovex; y3 += user_movey + clipmovey;
1765 state->transform(1, 1, &x4, &y4); x4 += user_movex + clipmovex; y4 += user_movey + clipmovey;
1767 if(!pbminfo && !(str->getKind()==strDCT)) {
1769 msg("<notice> file contains pbm pictures %s",mask?"(masked)":"");
1773 msg("<verbose> drawing %d by %d masked picture\n", width, height);
1775 if(!jpeginfo && (str->getKind()==strDCT)) {
1776 msg("<notice> file contains jpeg pictures");
1782 unsigned char buf[8];
1784 unsigned char*pic = new unsigned char[width*height];
1785 gfxcolor_t pal[256];
1787 state->getFillRGB(&rgb);
1789 memset(pal,255,sizeof(pal));
1790 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
1791 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
1792 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
1793 pal[0].a = 255; pal[1].a = 0;
1796 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
1797 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
1798 for (y = 0; y < height; ++y)
1799 for (x = 0; x < width; ++x)
1801 imgStr->getPixel(buf);
1804 pic[width*y+x] = buf[0];
1807 /* the size of the drawn image is added to the identifier
1808 as the same image may require different bitmaps if displayed
1809 at different sizes (due to antialiasing): */
1812 unsigned char*pic2 = 0;
1815 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
1824 height = realheight;
1828 /* make a black/white palette */
1830 float r = 255/(numpalette-1);
1832 for(t=0;t<numpalette;t++) {
1833 pal[t].r = colToByte(rgb.r);
1834 pal[t].g = colToByte(rgb.g);
1835 pal[t].b = colToByte(rgb.b);
1836 pal[t].a = (unsigned char)(t*r);
1840 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
1841 for (y = 0; y < height; ++y) {
1842 for (x = 0; x < width; ++x) {
1843 pic2[width*y+x] = pal[pic[y*width+x]];
1846 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1850 if(maskbitmap) free(maskbitmap);
1856 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
1857 gfxcolor_t*pic=new gfxcolor_t[width*height];
1858 for (y = 0; y < height; ++y) {
1859 for (x = 0; x < width; ++x) {
1860 imgStr->getPixel(pixBuf);
1861 colorMap->getRGB(pixBuf, &rgb);
1862 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
1863 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
1864 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
1865 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
1867 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
1871 if(str->getKind()==strDCT)
1872 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1874 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1877 if(maskbitmap) free(maskbitmap);
1880 gfxcolor_t*pic=new gfxcolor_t[width*height];
1881 gfxcolor_t pal[256];
1882 int n = 1 << colorMap->getBits();
1884 for(t=0;t<256;t++) {
1886 colorMap->getRGB(pixBuf, &rgb);
1888 {/*if(maskColors && *maskColors==t) {
1889 msg("<notice> Color %d is transparent", t);
1890 if (imgData->maskColors) {
1892 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
1893 if (pix[i] < imgData->maskColors[2*i] ||
1894 pix[i] > imgData->maskColors[2*i+1]) {
1909 pal[t].r = (unsigned char)(colToByte(rgb.r));
1910 pal[t].g = (unsigned char)(colToByte(rgb.g));
1911 pal[t].b = (unsigned char)(colToByte(rgb.b));
1912 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
1915 for (y = 0; y < height; ++y) {
1916 for (x = 0; x < width; ++x) {
1917 imgStr->getPixel(pixBuf);
1918 pic[width*y+x] = pal[pixBuf[0]];
1920 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
1924 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1928 if(maskbitmap) free(maskbitmap);
1933 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
1934 int width, int height, GBool invert,
1937 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
1938 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
1939 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
1942 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
1943 int width, int height, GfxImageColorMap *colorMap,
1944 int *maskColors, GBool inlineImg)
1946 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
1947 colorMap?"colorMap":"no colorMap",
1948 maskColors?"maskColors":"no maskColors",
1950 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
1951 colorMap?"colorMap":"no colorMap",
1952 maskColors?"maskColors":"no maskColors",
1955 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
1956 colorMap->getBits(),colorMap->getColorSpace()->getMode());
1957 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
1960 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
1961 int width, int height,
1962 GfxImageColorMap *colorMap,
1963 Stream *maskStr, int maskWidth, int maskHeight,
1966 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
1967 colorMap?"colorMap":"no colorMap",
1968 maskWidth, maskHeight);
1969 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
1970 colorMap?"colorMap":"no colorMap",
1971 maskWidth, maskHeight);
1973 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
1974 colorMap->getBits(),colorMap->getColorSpace()->getMode());
1975 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
1978 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
1979 int width, int height,
1980 GfxImageColorMap *colorMap,
1982 int maskWidth, int maskHeight,
1983 GfxImageColorMap *maskColorMap)
1985 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
1986 colorMap?"colorMap":"no colorMap",
1987 maskWidth, maskHeight);
1988 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
1989 colorMap?"colorMap":"no colorMap",
1990 maskWidth, maskHeight);
1992 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
1993 colorMap->getBits(),colorMap->getColorSpace()->getMode());
1994 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
1997 void GFXOutputDev::stroke(GfxState *state)
2001 GfxPath * path = state->getPath();
2002 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2003 strokeGfxline(state, line, 0);
2007 void GFXOutputDev::fill(GfxState *state)
2009 gfxcolor_t col = getFillColor(state);
2010 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2012 GfxPath * path = state->getPath();
2013 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2014 fillGfxLine(state, line);
2018 void GFXOutputDev::eoFill(GfxState *state)
2020 gfxcolor_t col = getFillColor(state);
2021 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2023 GfxPath * path = state->getPath();
2024 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2025 fillGfxLine(state, line);
2030 static const char* dirseparator()
2039 void addGlobalFont(const char*filename)
2042 memset(&f, 0, sizeof(fontfile_t));
2043 f.filename = filename;
2044 if(fontnum < sizeof(fonts)/sizeof(fonts[0])) {
2045 msg("<notice> Adding font \"%s\".", filename);
2046 fonts[fontnum++] = f;
2048 msg("<error> Too many external fonts. Not adding font file \"%s\".", filename);
2052 void addGlobalLanguageDir(const char*dir)
2054 msg("<notice> Adding %s to language pack directories", dir);
2058 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2059 strcpy(config_file, dir);
2060 strcat(config_file, dirseparator());
2061 strcat(config_file, "add-to-xpdfrc");
2063 fi = fopen(config_file, "rb");
2065 msg("<error> Could not open %s", config_file);
2068 globalParams->parseFile(new GString(config_file), fi);
2072 void addGlobalFontDir(const char*dirname)
2074 #ifdef HAVE_DIRENT_H
2075 msg("<notice> Adding %s to font directories", dirname);
2076 lastfontdir = strdup(dirname);
2077 DIR*dir = opendir(dirname);
2079 msg("<warning> Couldn't open directory %s\n", dirname);
2084 ent = readdir (dir);
2088 char*name = ent->d_name;
2094 if(!strncasecmp(&name[l-4], ".pfa", 4))
2096 if(!strncasecmp(&name[l-4], ".pfb", 4))
2098 if(!strncasecmp(&name[l-4], ".ttf", 4))
2101 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2102 strcpy(fontname, dirname);
2103 strcat(fontname, dirseparator());
2104 strcat(fontname, name);
2105 addGlobalFont(fontname);
2110 msg("<warning> No dirent.h- unable to add font dir %s", dir);
2114 void GFXOutputDev::preparePage(int pdfpage, int outputpage)
2120 this->pagebuflen = 1024;
2121 this->pages = (int*)malloc(this->pagebuflen*sizeof(int));
2122 memset(this->pages, -1, this->pagebuflen*sizeof(int));
2124 while(pdfpage >= this->pagebuflen)
2126 int oldlen = this->pagebuflen;
2127 this->pagebuflen+=1024;
2128 this->pages = (int*)realloc(this->pages, this->pagebuflen*sizeof(int));
2129 memset(&this->pages[oldlen], -1, (this->pagebuflen-oldlen)*sizeof(int));
2132 this->pages[pdfpage] = outputpage;
2133 if(pdfpage>this->pagepos)
2134 this->pagepos = pdfpage;
2140 double width,height;
2143 BBox mkBBox(GfxState*state, double*bbox, double width, double height)
2145 double xMin, yMin, xMax, yMax, x, y;
2146 double tx, ty, w, h;
2147 // transform the bbox
2148 state->transform(bbox[0], bbox[1], &x, &y);
2151 state->transform(bbox[0], bbox[3], &x, &y);
2154 } else if (x > xMax) {
2159 } else if (y > yMax) {
2162 state->transform(bbox[2], bbox[1], &x, &y);
2165 } else if (x > xMax) {
2170 } else if (y > yMax) {
2173 state->transform(bbox[2], bbox[3], &x, &y);
2176 } else if (x > xMax) {
2181 } else if (y > yMax) {
2184 tx = (int)floor(xMin);
2187 } else if (tx > width) {
2190 ty = (int)floor(yMin);
2193 } else if (ty > height) {
2196 w = (int)ceil(xMax) - tx + 1;
2197 if (tx + w > width) {
2203 h = (int)ceil(yMax) - ty + 1;
2204 if (ty + h > height) {
2218 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2219 GfxColorSpace *blendingColorSpace,
2220 GBool isolated, GBool knockout,
2223 const char*colormodename = "";
2224 BBox rect = mkBBox(state, bbox, this->width, this->height);
2226 if(blendingColorSpace) {
2227 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2229 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);
2230 dbg("using clipping rect %f/%f/%f/%f\n", rect.posx,rect.posy,rect.width,rect.height);
2231 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);
2233 states[statepos].createsoftmask |= forSoftMask;
2234 states[statepos].transparencygroup = !forSoftMask;
2235 states[statepos].isolated = isolated;
2237 states[statepos].olddevice = this->device;
2238 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2240 gfxdevice_record_init(this->device);
2242 /*if(!forSoftMask) { ////???
2243 state->setFillOpacity(0.0);
2248 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2251 dbg("endTransparencyGroup");
2252 msg("<verbose> endTransparencyGroup");
2254 gfxdevice_t*r = this->device;
2256 this->device = states[statepos].olddevice;
2258 if(states[statepos].createsoftmask) {
2259 states[statepos-1].softmaskrecording = r->finish(r);
2261 states[statepos-1].grouprecording = r->finish(r);
2264 states[statepos].createsoftmask = 0;
2265 states[statepos].transparencygroup = 0;
2269 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2271 const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2272 "colordodge","colorburn","hardlight","softlight","difference",
2273 "exclusion","hue","saturation","color","luminosity"};
2275 dbg("paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2276 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2278 if(state->getBlendMode() == gfxBlendNormal)
2279 infofeature("transparency groups");
2282 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2283 warnfeature(buffer, 0);
2286 gfxresult_t*grouprecording = states[statepos].grouprecording;
2288 if(state->getBlendMode() == gfxBlendNormal) {
2290 gfxdevice_ops_init(&ops, this->device, (unsigned char)(state->getFillOpacity()*255));
2291 gfxresult_record_replay(grouprecording, &ops);
2294 grouprecording->destroy(grouprecording);
2296 states[statepos].grouprecording = 0;
2299 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2301 /* alpha = 1: retrieve mask values from alpha layer
2302 alpha = 0: retrieve mask values from luminance */
2303 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2304 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2305 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2306 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2308 infofeature("soft masks");
2310 warnfeature("soft masks from alpha channel",0);
2312 states[statepos].olddevice = this->device;
2313 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2314 gfxdevice_record_init(this->device);
2316 dbg("softmaskrecording is %08x at statepos %d\n", states[statepos].softmaskrecording, statepos);
2318 states[statepos].softmask = 1;
2319 states[statepos].softmask_alpha = alpha;
2322 static inline Guchar div255(int x) {
2323 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2326 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2328 if(c < min) c = min;
2329 if(c > max) c = max;
2333 void GFXOutputDev::clearSoftMask(GfxState *state)
2335 if(!states[statepos].softmask)
2337 states[statepos].softmask = 0;
2338 dbg("clearSoftMask statepos=%d", statepos);
2339 msg("<verbose> clearSoftMask");
2341 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2342 msg("<error> Error in softmask/tgroup ordering");
2346 gfxresult_t*mask = states[statepos].softmaskrecording;
2347 gfxresult_t*below = this->device->finish(this->device);
2348 this->device = states[statepos].olddevice;
2350 /* get outline of all objects below the soft mask */
2351 gfxdevice_t uniondev;
2352 gfxdevice_union_init(&uniondev, 0);
2353 gfxresult_record_replay(below, &uniondev);
2354 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2355 uniondev.finish(&uniondev);
2357 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2359 this->device->startclip(this->device, belowoutline);
2360 gfxresult_record_replay(below, this->device);
2361 gfxresult_record_replay(mask, this->device);
2362 this->device->endclip(this->device);
2363 gfxline_free(belowoutline);
2366 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2367 if(width<=0 || height<=0)
2370 gfxdevice_t belowrender;
2371 gfxdevice_render_init(&belowrender);
2372 if(states[statepos+1].isolated) {
2373 belowrender.setparameter(&belowrender, "fillwhite", "1");
2375 belowrender.setparameter(&belowrender, "antialize", "2");
2376 belowrender.startpage(&belowrender, width, height);
2377 gfxresult_record_replay(below, &belowrender);
2378 belowrender.endpage(&belowrender);
2379 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2380 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2381 //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2383 gfxdevice_t maskrender;
2384 gfxdevice_render_init(&maskrender);
2385 maskrender.startpage(&maskrender, width, height);
2386 gfxresult_record_replay(mask, &maskrender);
2387 maskrender.endpage(&maskrender);
2388 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2389 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2391 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2392 msg("<fatal> Internal error in mask drawing");
2397 for(y=0;y<height;y++) {
2398 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2399 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2400 for(x=0;x<width;x++) {
2402 if(states[statepos].softmask_alpha) {
2405 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2408 l2->a = div255(alpha*l2->a);
2410 /* DON'T premultiply alpha- this is done by fillbitmap,
2411 depending on the output device */
2412 //l2->r = div255(alpha*l2->r);
2413 //l2->g = div255(alpha*l2->g);
2414 //l2->b = div255(alpha*l2->b);
2420 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2423 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2424 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2426 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2428 mask->destroy(mask);
2429 below->destroy(below);
2430 maskresult->destroy(maskresult);
2431 belowresult->destroy(belowresult);
2432 states[statepos].softmaskrecording = 0;
2437 // public: ~MemCheck()
2439 // delete globalParams;globalParams=0;
2440 // Object::memCheck(stderr);
2441 // gMemReport(stderr);