X-Git-Url: http://git.asbjorn.it/?a=blobdiff_plain;f=pdf2swf%2FSWFOutputDev.cc;h=0769800f9a77208412e793e76528714e97968a7a;hb=ae8a7acad94dd9c9d7b5d477ce96c5674935ff9b;hp=9486441d77b512b506fe872c7f7b91c13fee49e0;hpb=cf5e3fe142c55ec2ca1850087c173c7f6ee4eb9b;p=swftools.git diff --git a/pdf2swf/SWFOutputDev.cc b/pdf2swf/SWFOutputDev.cc index 9486441..0769800 100644 --- a/pdf2swf/SWFOutputDev.cc +++ b/pdf2swf/SWFOutputDev.cc @@ -23,7 +23,13 @@ #include #include #include "../config.h" -#ifdef HAVE_FONTCONFIG_H +#ifdef HAVE_DIRENT_H +#include +#endif +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_FONTCONFIG #include #endif //xpdf header files @@ -58,6 +64,9 @@ //swftools header files #include "swfoutput.h" #include "../lib/log.h" +#include "../lib/gfxdevice.h" +#include "../lib/gfxtools.h" +#include "../lib/gfxfont.h" #include @@ -71,6 +80,8 @@ typedef struct _fontfile static fontfile_t fonts[2048]; static int fontnum = 0; +static int config_use_fontconfig = 1; + // swf <-> pdf pages // TODO: move into pdf_doc_t static int*pages = 0; @@ -79,7 +90,8 @@ static int pagepos = 0; /* config */ static double caplinewidth = 3.0; -static int zoom = 72; /* xpdf: 86 */ +static double zoom = 72; /* xpdf: 86 */ +static int forceType0Fonts = 1; static void printInfoString(Dict *infoDict, char *key, char *fmt); static void printInfoDate(Dict *infoDict, char *key, char *fmt); @@ -103,9 +115,27 @@ struct mapping { {"Symbol", "s050000l"}, {"ZapfDingbats", "d050000l"}}; +class SWFOutputState { + public: + int clipping; + int textRender; + SWFOutputState() { + this->clipping = 0; + this->textRender = 0; + } +}; + +typedef struct _fontlist +{ + char*id; + char*filename; + gfxfont_t*font; + _fontlist*next; +} fontlist_t; + class SWFOutputDev: public OutputDev { - struct swfoutput output; int outputstarted; + struct swfoutput output; public: // Constructor. @@ -113,8 +143,15 @@ public: // Destructor. virtual ~SWFOutputDev() ; + + void setMove(int x,int y); + void setClip(int x1,int y1,int x2,int y2); - void save(char*filename); + int save(char*filename); + void pagefeed(); + void* getSWF(); + + void getDimensions(int*x1,int*y1,int*x2,int*y2); //----- get info about output device @@ -175,6 +212,7 @@ public: //----- text drawing virtual void beginString(GfxState *state, GString *s) ; virtual void endString(GfxState *state) ; + virtual void endTextObject(GfxState *state); virtual void drawChar(GfxState *state, double x, double y, double dx, double dy, double originX, double originY, @@ -188,16 +226,23 @@ public: int width, int height, GfxImageColorMap *colorMap, int *maskColors, GBool inlineImg); - virtual GBool beginType3Char(GfxState *state, - CharCode code, Unicode *u, int uLen); + virtual GBool beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode code, Unicode *u, int uLen); virtual void endType3Char(GfxState *state); + virtual void type3D0(GfxState *state, double wx, double wy); + virtual void type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury); + private: void drawGeneralImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap*colorMap, GBool invert, - GBool inlineImg, int mask); - int clipping[64]; - int clippos; + GBool inlineImg, int mask, int *maskColors); + int SWFOutputDev::setGfxFont(char*id, char*filename); + void strokeGfxline(GfxState *state, gfxline_t*line); + void clipToGfxLine(GfxState *state, gfxline_t*line); + void fillGfxLine(GfxState *state, gfxline_t*line); + + SWFOutputState states[64]; + int statepos; int currentpage; @@ -218,18 +263,22 @@ public: GfxState *laststate; - int pic_xids[1024]; - int pic_yids[1024]; - int pic_ids[1024]; - int pic_width[1024]; - int pic_height[1024]; - int picpos; - int pic_id; char type3Warning; char* substitutetarget[256]; char* substitutesource[256]; int substitutepos; + + int user_movex,user_movey; + int user_clipx1,user_clipx2,user_clipy1,user_clipy2; + + gfxline_t* current_text_stroke; + gfxline_t* current_text_clip; + char* current_font_id; + gfxfont_t* current_gfxfont; + gfxmatrix_t current_font_matrix; + + fontlist_t* fontlist; }; static char*getFontID(GfxFont*font); @@ -238,15 +287,19 @@ class InfoOutputDev: public OutputDev { public: int x1,y1,x2,y2; - int has_links; - int has_images; + int num_links; + int num_images; + int num_fonts; InfoOutputDev() { - has_links = 0; - has_images = 0; + num_links = 0; + num_images = 0; + num_fonts = 0; + } + virtual ~InfoOutputDev() + { } - virtual ~InfoOutputDev() {} virtual GBool upsideDown() {return gTrue;} virtual GBool useDrawChar() {return gTrue;} virtual GBool useGradients() {return gTrue;} @@ -265,23 +318,28 @@ class InfoOutputDev: public OutputDev } virtual void drawLink(Link *link, Catalog *catalog) { - has_links = 1; + num_links++; } virtual void updateFont(GfxState *state) { - char*id = getFontID(state->getFont()); + GfxFont*font = state->getFont(); + if(!font) + return; + /*char*id = getFontID(font);*/ + /* FIXME*/ + num_fonts++; } virtual void drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, GBool invert, GBool inlineImg) { - has_images = 1; + num_images++; } virtual void drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, int *maskColors, GBool inlineImg) { - has_images = 1; + num_images++; } }; @@ -292,16 +350,44 @@ SWFOutputDev::SWFOutputDev() linkinfo = 0; pbminfo = 0; type3active = 0; - clippos = 0; - clipping[clippos] = 0; + statepos = 0; outputstarted = 0; xref = 0; - picpos = 0; - pic_id = 0; substitutepos = 0; type3Warning = 0; + user_movex = 0; + user_movey = 0; + user_clipx1 = 0; + user_clipy1 = 0; + user_clipx2 = 0; + user_clipy2 = 0; + current_text_stroke = 0; + current_text_clip = 0; + fontlist = 0; + memset(&output, 0, sizeof(output)); // printf("SWFOutputDev::SWFOutputDev() \n"); }; + +void SWFOutputDev::setMove(int x,int y) +{ + this->user_movex = x; + this->user_movey = y; +} + +void SWFOutputDev::setClip(int x1,int y1,int x2,int y2) +{ + if(x2user_clipx1 = x1; + this->user_clipy1 = y1; + this->user_clipx2 = x2; + this->user_clipy2 = y2; +} +void SWFOutputDev::getDimensions(int*x1,int*y1,int*x2,int*y2) +{ + return swfoutput_getdimensions(&output, x1,y1,x2,y2); +} static char*getFontID(GfxFont*font) { @@ -466,13 +552,14 @@ static void showFontError(GfxFont*font, int nr) static void dumpFontInfo(char*loglevel, GfxFont*font) { - char* name = getFontID(font); + char* id = getFontID(font); + char* name = getFontName(font); Ref* r=font->getID(); - msg("%s=========== %s (ID:%d,%d) ==========\n", loglevel, getFontName(font), r->num,r->gen); + msg("%s=========== %s (ID:%d,%d) ==========\n", loglevel, name, r->num,r->gen); GString*gstr = font->getTag(); - msg("%s| Tag: %s\n", loglevel, name); + msg("%s| Tag: %s\n", loglevel, id); if(font->isCIDFont()) msg("%s| is CID font\n", loglevel); @@ -506,10 +593,12 @@ static void dumpFontInfo(char*loglevel, GfxFont*font) Ref embRef; GBool embedded = font->getEmbeddedFontID(&embRef); - if(font->getEmbeddedFontName()) - name = font->getEmbeddedFontName()->getCString(); + char*embeddedName=0; + if(font->getEmbeddedFontName()) { + embeddedName = font->getEmbeddedFontName()->getCString(); + } if(embedded) - msg("%s| Embedded name: %s id: %d\n",loglevel, FIXNULL(name), embRef.num); + msg("%s| Embedded id: %s id: %d\n",loglevel, FIXNULL(embeddedName), embRef.num); gstr = font->getExtFontFile(); if(gstr) @@ -521,111 +610,89 @@ static void dumpFontInfo(char*loglevel, GfxFont*font) if(font->isSymbolic()) msg("%s| is symbolic\n", loglevel); if(font->isItalic()) msg("%s| is italic\n", loglevel); if(font->isBold()) msg("%s| is bold\n", loglevel); + + free(id); + free(name); } //void SWFOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, GBool invert, GBool inlineImg) {printf("void SWFOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, GBool invert, GBool inlineImg) \n");} //void SWFOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, GBool inlineImg) {printf("void SWFOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, GBool inlineImg) \n");} -static void free_outline(SWF_OUTLINE*outline) -{ - while(outline) { - SWF_OUTLINE*next = outline->link; - free(outline); - outline = next; - } -} -static void dump_outline(SWF_OUTLINE*outline) +void dump_outline(gfxline_t*line) { - double x=0,y=0; - while(outline) { - double lastx=x,lasty=y; - x += (outline->dest.x/(float)0xffff); - y += (outline->dest.y/(float)0xffff); - if(outline->type == SWF_PATHTYPE_MOVE) { - msg(" | moveto %f,%f", x,y); - } else if(outline->type == SWF_PATHTYPE_LINE) { - msg(" | lineto: %f,%f\n",x,y); - } else if(outline->type == SWF_PATHTYPE_BEZIER) { - SWF_BEZIERSEGMENT*o2 = (SWF_BEZIERSEGMENT*)outline; - float x1 = o2->C.x/(float)0xffff+lastx; - float y1 = o2->C.y/(float)0xffff+lasty; - float x2 = o2->B.x/(float)0xffff+lastx; - float y2 = o2->B.y/(float)0xffff+lasty; - msg(" | spline: %f,%f -> %f,%f -> %f,%f\n",x1,y1,x2,y2,x,y); - } - outline = outline->link; + while(line) { + if(line->type == gfx_moveTo) { + msg(" | moveTo %.2f %.2f", line->x,line->y); + } else if(line->type == gfx_lineTo) { + msg(" | lineTo %.2f %.2f", line->x,line->y); + } else if(line->type == gfx_splineTo) { + msg(" | splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y); + } + line = line->next; } } -SWF_OUTLINE* gfxPath_to_SWF_OUTLINE(GfxState*state, GfxPath*path) +gfxline_t* gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed) { int num = path->getNumSubpaths(); int s,t; - bezierpathsegment*start,*last=0; - bezierpathsegment*outline = start = (bezierpathsegment*)malloc(sizeof(bezierpathsegment)); int cpos = 0; - double lastx=0,lasty=0; + double lastx=0,lasty=0,posx=0,posy=0; + int needsfix=0; if(!num) { msg(" empty path"); - outline->type = SWF_PATHTYPE_MOVE; - outline->dest.x = 0; - outline->dest.y = 0; - outline->link = 0; - return (SWF_OUTLINE*)outline; + return 0; } + gfxdrawer_t draw; + gfxdrawer_target_gfxline(&draw); + for(t = 0; t < num; t++) { GfxSubpath *subpath = path->getSubpath(t); int subnum = subpath->getNumPoints(); + double bx=0,by=0,cx=0,cy=0; for(s=0;stransform(subpath->getX(s),subpath->getY(s),&nx,&ny); - int x = (int)((nx-lastx)*0xffff); - int y = (int)((ny-lasty)*0xffff); - if(s==0) - { - last = outline; - outline->type = SWF_PATHTYPE_MOVE; - outline->dest.x = x; - outline->dest.y = y; - outline->link = (SWF_OUTLINE*)malloc(sizeof(bezierpathsegment)); - outline = (bezierpathsegment*)outline->link; + double x,y; + state->transform(subpath->getX(s),subpath->getY(s),&x,&y); + if(s==0) { + if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) { + draw.lineTo(&draw, lastx, lasty); + } + draw.moveTo(&draw, x,y); + posx = lastx = x; + posy = lasty = y; cpos = 0; - lastx = nx; - lasty = ny; - } - else if(subpath->getCurve(s) && !cpos) - { - outline->B.x = x; - outline->B.y = y; + needsfix = 0; + } else if(subpath->getCurve(s) && cpos==0) { + bx = x; + by = y; cpos = 1; - } - else if(subpath->getCurve(s) && cpos) - { - outline->C.x = x; - outline->C.y = y; + } else if(subpath->getCurve(s) && cpos==1) { + cx = x; + cy = y; cpos = 2; - } - else - { - last = outline; - outline->dest.x = x; - outline->dest.y = y; - outline->type = cpos?SWF_PATHTYPE_BEZIER:SWF_PATHTYPE_LINE; - outline->link = (SWF_OUTLINE*)malloc(sizeof(bezierpathsegment)); - outline = (bezierpathsegment*)outline->link; + } else { + posx = x; + posy = y; + if(cpos==0) { + draw.lineTo(&draw, x,y); + } else { + gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y); + } + needsfix = 1; cpos = 0; - lastx = nx; - lasty = ny; } } } - if(last->link) {free(last->link);} - last->link = 0; - - return (SWF_OUTLINE*)start; + /* fix non-closed lines */ + if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) { + draw.lineTo(&draw, lastx, lasty); + } + gfxline_t*result = (gfxline_t*)draw.result(&draw); + return result; } + /*---------------------------------------------------------------------------- * Primitive Graphic routines *----------------------------------------------------------------------------*/ @@ -633,125 +700,196 @@ SWF_OUTLINE* gfxPath_to_SWF_OUTLINE(GfxState*state, GfxPath*path) void SWFOutputDev::stroke(GfxState *state) { GfxPath * path = state->getPath(); + gfxline_t*line= gfxPath_to_gfxline(state, path, 0); + strokeGfxline(state, line); + gfxline_free(line); +} + +void SWFOutputDev::strokeGfxline(GfxState *state, gfxline_t*line) +{ int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel double miterLimit = state->getMiterLimit(); double width = state->getTransformedLineWidth(); - struct swfmatrix m; + GfxRGB rgb; double opaq = state->getStrokeOpacity(); - state->getStrokeRGB(&rgb); + if(type3active) + state->getFillRGB(&rgb); + else + state->getStrokeRGB(&rgb); + gfxcolor_t col; + col.r = (unsigned char)(rgb.r*255); + col.g = (unsigned char)(rgb.g*255); + col.b = (unsigned char)(rgb.b*255); + col.a = (unsigned char)(opaq*255); + + gfx_capType capType = gfx_capRound; + if(lineCap == 0) capType = gfx_capButt; + else if(lineCap == 1) capType = gfx_capRound; + else if(lineCap == 2) capType = gfx_capSquare; - m.m11 = 1; m.m21 = 0; m.m22 = 1; - m.m12 = 0; m.m13 = 0; m.m23 = 0; - SWF_OUTLINE*outline = gfxPath_to_SWF_OUTLINE(state, path); + gfx_joinType joinType = gfx_joinRound; + if(lineJoin == 0) joinType = gfx_joinMiter; + else if(lineJoin == 1) joinType = gfx_joinRound; + else if(lineJoin == 2) joinType = gfx_joinBevel; + + int dashnum = 0; + double dashphase = 0; + double * ldash = 0; + state->getLineDash(&ldash, &dashnum, &dashphase); + + gfxline_t*line2 = 0; + + if(dashnum && ldash) { + float * dash = (float*)malloc(sizeof(float)*(dashnum+1)); + int t; + double cut = 0; + int fixzero = 0; + msg(" %d dashes", dashnum); + msg(" | phase: %f", dashphase); + for(t=0;t | d%-3d: %f", t, ldash[t]); + } + dash[dashnum] = -1; + if(getLogLevel() >= LOGLEVEL_TRACE) { + dump_outline(line); + } + + line2 = gfxtool_dash_line(line, dash, dashphase); + line = line2; + msg(" After dashing:"); + } - if(screenloglevel >= LOGLEVEL_TRACE) { - msg(" stroke\n"); - dump_outline(outline); + if(getLogLevel() >= LOGLEVEL_TRACE) { + double gray; + state->getStrokeGray(&gray); + msg(" stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x gray=%f\n", + width, + lineJoin==0?"miter": (lineJoin==1?"round":"bevel"), + lineCap==0?"butt": (lineJoin==1?"round":"square"), + dashnum, + col.r,col.g,col.b,col.a, + gray + ); + dump_outline(line); } + + swfoutput_drawgfxline(&output, line, width, &col, capType, joinType, miterLimit); + + if(line2) + gfxline_free(line2); +} - lineJoin = 1; // other line joins are not yet supported by the swf encoder - // TODO: support bevel joints - - if(((lineCap==1) && (lineJoin==1)) || width<=caplinewidth) { - /* FIXME- if the path is smaller than 2 segments, we could ignore - lineJoin */ - swfoutput_setdrawmode(&output, DRAWMODE_STROKE); - swfoutput_drawpath(&output, outline, &m); - } else { - swfoutput_setfillcolor(&output, (char)(rgb.r*255), (char)(rgb.g*255), - (char)(rgb.b*255), (char)(opaq*255)); +gfxcolor_t getFillColor(GfxState * state) +{ + GfxRGB rgb; + double opaq = state->getFillOpacity(); + state->getFillRGB(&rgb); + gfxcolor_t col; + col.r = (unsigned char)(rgb.r*255); + col.g = (unsigned char)(rgb.g*255); + col.b = (unsigned char)(rgb.b*255); + col.a = (unsigned char)(opaq*255); + return col; +} - //swfoutput_setlinewidth(&output, 1.0); //only for debugging - //swfoutput_setstrokecolor(&output, 0, 255, 0, 255); //likewise, see below - //swfoutput_setfillcolor(&output, 255, 0, 0, 255); //likewise, see below +void SWFOutputDev::fillGfxLine(GfxState *state, gfxline_t*line) +{ + gfxcolor_t col = getFillColor(state); - swfoutput_drawpath2poly(&output, outline, &m, lineJoin, lineCap, width, miterLimit); - updateLineWidth(state); //reset - updateStrokeColor(state); //reset - updateFillColor(state); //reset + if(getLogLevel() >= LOGLEVEL_TRACE) { + msg(" fill %02x%02x%02x%02x\n", col.r, col.g, col.b, col.a); + dump_outline(line); } - free_outline(outline); + swfoutput_fillgfxline(&output, line, &col); } void SWFOutputDev::fill(GfxState *state) { GfxPath * path = state->getPath(); - struct swfmatrix m; - m.m11 = 1; m.m21 = 0; m.m22 = 1; - m.m12 = 0; m.m13 = 0; m.m23 = 0; - SWF_OUTLINE*outline = gfxPath_to_SWF_OUTLINE(state, path); - - if(screenloglevel >= LOGLEVEL_TRACE) { - msg(" fill\n"); - dump_outline(outline); - } - - swfoutput_setdrawmode(&output, DRAWMODE_FILL); - swfoutput_drawpath(&output, outline, &m); - free_outline(outline); + gfxline_t*line= gfxPath_to_gfxline(state, path, 1); + fillGfxLine(state, line); + gfxline_free(line); } void SWFOutputDev::eoFill(GfxState *state) { GfxPath * path = state->getPath(); - struct swfmatrix m; - m.m11 = 1; m.m21 = 0; m.m22 = 1; - m.m12 = 0; m.m13 = 0; m.m23 = 0; - SWF_OUTLINE*outline = gfxPath_to_SWF_OUTLINE(state, path); + gfxcolor_t col = getFillColor(state); + + gfxline_t*line= gfxPath_to_gfxline(state, path, 1); - if(screenloglevel >= LOGLEVEL_TRACE) { + if(getLogLevel() >= LOGLEVEL_TRACE) { msg(" eofill\n"); - dump_outline(outline); + dump_outline(line); } - swfoutput_setdrawmode(&output, DRAWMODE_EOFILL); - swfoutput_drawpath(&output, outline, &m); - free_outline(outline); + swfoutput_fillgfxline(&output, line, &col); + gfxline_free(line); } + void SWFOutputDev::clip(GfxState *state) { GfxPath * path = state->getPath(); - struct swfmatrix m; - m.m11 = 1; m.m22 = 1; - m.m12 = 0; m.m21 = 0; - m.m13 = 0; m.m23 = 0; - SWF_OUTLINE*outline = gfxPath_to_SWF_OUTLINE(state, path); + gfxline_t*line = gfxPath_to_gfxline(state, path, 1); + clipToGfxLine(state, line); + gfxline_free(line); +} - if(screenloglevel >= LOGLEVEL_TRACE) { +void SWFOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line) +{ + if(getLogLevel() >= LOGLEVEL_TRACE) { msg(" clip\n"); - dump_outline(outline); + dump_outline(line); } - swfoutput_startclip(&output, outline, &m); - clipping[clippos] ++; - free_outline(outline); + swfoutput_startclip(&output, line); + states[statepos].clipping++; } void SWFOutputDev::eoClip(GfxState *state) { GfxPath * path = state->getPath(); - struct swfmatrix m; - m.m11 = 1; m.m21 = 0; m.m22 = 1; - m.m12 = 0; m.m13 = 0; m.m23 = 0; - SWF_OUTLINE*outline = gfxPath_to_SWF_OUTLINE(state, path); + gfxline_t*line = gfxPath_to_gfxline(state, path, 1); - if(screenloglevel >= LOGLEVEL_TRACE) { + if(getLogLevel() >= LOGLEVEL_TRACE) { msg(" eoclip\n"); - dump_outline(outline); + dump_outline(line); } - swfoutput_startclip(&output, outline, &m); - clipping[clippos] ++; - free_outline(outline); + swfoutput_startclip(&output, line); + states[statepos].clipping++; + gfxline_free(line); +} + +/* pass through functions for swf_output */ +int SWFOutputDev::save(char*filename) +{ + return swfoutput_save(&output, filename); +} +void SWFOutputDev::pagefeed() +{ + swfoutput_pagefeed(&output); } -void SWFOutputDev::save(char*filename) +void* SWFOutputDev::getSWF() { - swfoutput_save(&output, filename); + return (void*)swfoutput_get(&output); } SWFOutputDev::~SWFOutputDev() { swfoutput_destroy(&output); outputstarted = 0; + + fontlist_t*l = this->fontlist; + while(l) { + fontlist_t*next = l->next; + l->next = 0; + gfxfont_free(l->font); + free(l->id); + free(l->filename); + free(l); + l = next; + } }; GBool SWFOutputDev::upsideDown() { @@ -772,14 +910,103 @@ GBool SWFOutputDev::useGradients() return gTrue; } +char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible", + "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"}; + +#define RENDER_FILL 0 +#define RENDER_STROKE 1 +#define RENDER_FILLSTROKE 2 +#define RENDER_INVISIBLE 3 +#define RENDER_CLIP 4 + +static char tmp_printstr[4096]; +char* makeStringPrintable(char*str) +{ + int len = strlen(str); + int dots = 0; + if(len>=80) { + len = 80; + dots = 1; + } + int t; + for(t=0;t124) { + c = '.'; + } + tmp_printstr[t] = c; + } + if(dots) { + tmp_printstr[len++] = '.'; + tmp_printstr[len++] = '.'; + tmp_printstr[len++] = '.'; + } + tmp_printstr[len] = 0; + return tmp_printstr; +} + + +int getGfxCharID(gfxfont_t*font, int charnr, char *charname, int u) +{ + int t; + if(charname) { + for(t=0;tnum_glyphs;t++) { + if(font->glyphs[t].name && !strcmp(font->glyphs[t].name,charname)) { + msg(" Char [%d,>%s<,%d] maps to %d\n", charnr, charname, u, t); + return t; + } + } + /* if we didn't find the character, maybe + we can find the capitalized version */ + for(t=0;tnum_glyphs;t++) { + if(font->glyphs[t].name && !strcasecmp(font->glyphs[t].name,charname)) { + msg(" Char [%d,>>%s<<,%d] maps to %d\n", charnr, charname, u, t); + return t; + } + } + } + + /* try to use the unicode id */ + if(u>=0 && umax_unicode && font->unicode2glyph[u]>=0) { + msg(" Char [%d,%s,>%d<] maps to %d\n", charnr, charname, u, font->unicode2glyph[u]); + return font->unicode2glyph[u]; + } + + if(charnr>=0 && charnrnum_glyphs) { + msg(" Char [>%d<,%s,%d] maps to %d\n", charnr, charname, u, charnr); + return charnr; + } + + return -1; +} + + void SWFOutputDev::beginString(GfxState *state, GString *s) { + int render = state->getRender(); + if(current_text_stroke) { + msg(" Error: Incompatible change of text rendering to %d while inside cliptext", render); + } + + msg(" beginString(%s) render=%d", makeStringPrintable(s->getCString()), render); double m11,m21,m12,m22; // msg(" %s beginstring \"%s\"\n", gfxstate2str(state), s->getCString()); state->getFontTransMat(&m11, &m12, &m21, &m22); m11 *= state->getHorizScaling(); m21 *= state->getHorizScaling(); - swfoutput_setfontmatrix(&output, m11, -m21, m12, -m22); + + this->current_font_matrix.m00 = m11 / 1024.0; + this->current_font_matrix.m01 = m12 / 1024.0; + this->current_font_matrix.m10 = -m21 / 1024.0; + this->current_font_matrix.m11 = -m22 / 1024.0; + this->current_font_matrix.tx = 0; + this->current_font_matrix.ty = 0; + + gfxmatrix_t m = this->current_font_matrix; + + /*if(render != 3 && render != 0) + msg(" Text rendering mode %d (%s) not fully supported yet (for text \"%s\")", render, renderModeDesc[render&7], makeStringPrintable(s->getCString()));*/ + states[statepos].textRender = render; } void SWFOutputDev::drawChar(GfxState *state, double x, double y, @@ -787,37 +1014,49 @@ void SWFOutputDev::drawChar(GfxState *state, double x, double y, double originX, double originY, CharCode c, Unicode *_u, int uLen) { + int render = state->getRender(); // check for invisible text -- this is used by Acrobat Capture - if ((state->getRender() & 3) == 3) + if (render == 3) return; + if(states[statepos].textRender != render) + msg(" Internal error: drawChar.render!=beginString.render"); + + gfxcolor_t col = getFillColor(state); + + Gushort *CIDToGIDMap = 0; GfxFont*font = state->getFont(); if(font->getType() == fontType3) { /* type 3 chars are passed as graphics */ + msg(" type3 char at %f/%f", x, y); return; } - double x1,y1; - x1 = x; - y1 = y; - state->transform(x, y, &x1, &y1); Unicode u=0; - if(_u && uLen) - u = *_u; - - /* find out the character name */ char*name=0; - if(font->isCIDFont() && u) { - GfxCIDFont*cfont = (GfxCIDFont*)font; - int t; - for(t=0;tisCIDFont()) { + GfxCIDFont*cfont = (GfxCIDFont*)font; + + if(font->getType() == fontCIDType2) + CIDToGIDMap = cfont->getCIDToGID(); } else { Gfx8BitFont*font8; font8 = (Gfx8BitFont*)font; @@ -825,29 +1064,112 @@ void SWFOutputDev::drawChar(GfxState *state, double x, double y, if(enc && enc[c]) name = enc[c]; } + if (CIDToGIDMap) { + msg(" drawChar(%f, %f, c='%c' (%d), GID=%d, u=%d <%d>) CID=%d name=\"%s\" render=%d\n", x, y, (c&127)>=32?c:'?', c, CIDToGIDMap[c], u, uLen, font->isCIDFont(), FIXNULL(name), render); + c = CIDToGIDMap[c]; + } else { + msg(" drawChar(%f,%f,c='%c' (%d), u=%d <%d>) CID=%d name=\"%s\" render=%d\n",x,y,(c&127)>=32?c:'?',c,u, uLen, font->isCIDFont(), FIXNULL(name), render); + } + + int charid = getGfxCharID(current_gfxfont, c, name, u); + if(charid<0) { + msg(" Didn't find character '%s' (c=%d,u=%d) in current charset (%s, %d characters)", + FIXNULL(name),c, u, FIXNULL((char*)current_font_id), current_gfxfont->num_glyphs); + return; + } + + gfxmatrix_t m = this->current_font_matrix; + state->transform(x, y, &m.tx, &m.ty); + + if(render == RENDER_FILL) { + swfoutput_gfxdrawchar(&output, current_font_id, charid, &col, &m); + } else { + msg(" Drawing glyph %d as shape", charid); + gfxline_t*glyph = current_gfxfont->glyphs[charid].line; + gfxline_t*tglyph = gfxline_clone(glyph); + gfxline_transform(tglyph, &m); + if((render&3) != RENDER_INVISIBLE) { + gfxline_t*add = gfxline_clone(tglyph); + current_text_stroke = gfxline_append(current_text_stroke, add); + } + if(render&RENDER_CLIP) { + gfxline_t*add = gfxline_clone(tglyph); + current_text_clip = gfxline_append(current_text_clip, add); + } + gfxline_free(tglyph); + } +} + +void SWFOutputDev::endString(GfxState *state) +{ + int render = state->getRender(); + msg(" endString() render=%d textstroke=%08x", render, current_text_stroke); + if(states[statepos].textRender != render) + msg(" Internal error: drawChar.render!=beginString.render"); - msg(" drawChar(%f,%f,c='%c' (%d),u=%d <%d>) CID=%d name=\"%s\"\n",x,y,(c&127)>=32?c:'?',c,u, uLen, font->isCIDFont(), FIXNULL(name)); + if(current_text_stroke) { + /* fillstroke and stroke text rendering objects we can process right + now (as there may be texts of other rendering modes in this + text object)- clipping objects have to wait until endTextObject, + however */ + if((render&3) == RENDER_FILL) { + fillGfxLine(state, current_text_stroke); + gfxline_free(current_text_stroke); + current_text_stroke = 0; + } else if((render&3) == RENDER_FILLSTROKE) { + fillGfxLine(state, current_text_stroke); + strokeGfxline(state, current_text_stroke); + gfxline_free(current_text_stroke); + current_text_stroke = 0; + } else if((render&3) == RENDER_STROKE) { + strokeGfxline(state, current_text_stroke); + gfxline_free(current_text_stroke); + current_text_stroke = 0; + } + } +} - /*x1 = (int)(x1+0.5); - y1 = (int)(y1+0.5);*/ +void SWFOutputDev::endTextObject(GfxState *state) +{ + int render = state->getRender(); + msg(" endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip); + if(states[statepos].textRender != render) + msg(" Internal error: drawChar.render!=beginString.render"); - int ret = swfoutput_drawchar(&output, x1, y1, name, c, u); + if(current_text_clip) { + clipToGfxLine(state, current_text_clip); + gfxline_free(current_text_clip); + current_text_clip = 0; + } } -void SWFOutputDev::endString(GfxState *state) { -} +/* the logic seems to be as following: + first, beginType3Char is called, with the charcode and the coordinates. + if this function returns true, it already knew about the char and has now drawn it. + if the function returns false, it's a new char, and type3D1 is called with some parameters- + the all draw operations until endType3Char are part of the char (which in this moment is + at the position first passed to beginType3Char). the char ends with endType3Char. - -GBool SWFOutputDev::beginType3Char(GfxState *state, - CharCode code, Unicode *u, int uLen) + The drawing operations between beginType3Char and endType3Char are somewhat different to + the normal ones. For example, the fillcolor equals the stroke color. +*/ + +GBool SWFOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode code, Unicode *u, int uLen) { msg(" beginType3Char %d, %08x, %d", code, *u, uLen); type3active = 1; - /* the character itself is going to be passed using - drawImageMask() */ + /* the character itself is going to be passed using the draw functions */ return gFalse; /* gTrue= is_in_cache? */ } +void SWFOutputDev::type3D0(GfxState *state, double wx, double wy) { + msg(" type3D0 width=%f height=%f", wx, wy); +} +void SWFOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) { + msg(" type3D1 width=%f height=%f bbox=(%f,%f,%f,%f)", wx, wy, + llx,lly,urx,ury); +} + void SWFOutputDev::endType3Char(GfxState *state) { type3active = 0; @@ -879,13 +1201,21 @@ void SWFOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, doubl if(x2 x1)*/ x1 = user_clipx1; + /*if(user_clipx2 < x2)*/ x2 = user_clipx2; + /*if(user_clipy1 > y1)*/ y1 = user_clipy1; + /*if(user_clipy2 < y2)*/ y2 = user_clipy2; + } + if(!outputstarted) { msg(" Bounding box is (%f,%f)-(%f,%f)", x1,y1,x2,y2); swfoutput_init(&output); outputstarted = 1; } - swfoutput_newpage(&output, pageNum, (int)x1, (int)y1, (int)x2, (int)y2); + swfoutput_newpage(&output, pageNum, user_movex, user_movey, (int)x1, (int)y1, (int)x2, (int)y2); } void SWFOutputDev::drawLink(Link *link, Catalog *catalog) @@ -1040,23 +1370,25 @@ void SWFOutputDev::drawLink(Link *link, Catalog *catalog) } void SWFOutputDev::saveState(GfxState *state) { - msg(" saveState\n"); + msg(" saveState\n"); updateAll(state); - if(clippos<64) - clippos ++; - else + if(statepos>=64) { msg(" Too many nested states in pdf."); - clipping[clippos] = 0; + return; + } + statepos ++; + states[statepos].clipping = 0; //? shouldn't this be the current value? + states[statepos].textRender = states[statepos-1].textRender; }; void SWFOutputDev::restoreState(GfxState *state) { - msg(" restoreState\n"); + msg(" restoreState\n"); updateAll(state); - while(clipping[clippos]) { + while(states[statepos].clipping) { swfoutput_endclip(&output); - clipping[clippos]--; + states[statepos].clipping--; } - clippos--; + statepos--; } char* SWFOutputDev::searchFont(char*name) @@ -1097,7 +1429,7 @@ char* SWFOutputDev::searchFont(char*name) void SWFOutputDev::updateLineWidth(GfxState *state) { double width = state->getTransformedLineWidth(); - swfoutput_setlinewidth(&output, width); + //swfoutput_setlinewidth(&output, width); } void SWFOutputDev::updateLineCap(GfxState *state) @@ -1116,8 +1448,7 @@ void SWFOutputDev::updateFillColor(GfxState *state) double opaq = state->getFillOpacity(); state->getFillRGB(&rgb); - swfoutput_setfillcolor(&output, (char)(rgb.r*255), (char)(rgb.g*255), - (char)(rgb.b*255), (char)(opaq*255)); + //swfoutput_setfillcolor(&output, (char)(rgb.r*255), (char)(rgb.g*255), (char)(rgb.b*255), (char)(opaq*255)); } void SWFOutputDev::updateStrokeColor(GfxState *state) @@ -1125,9 +1456,7 @@ void SWFOutputDev::updateStrokeColor(GfxState *state) GfxRGB rgb; double opaq = state->getStrokeOpacity(); state->getStrokeRGB(&rgb); - - swfoutput_setstrokecolor(&output, (char)(rgb.r*255), (char)(rgb.g*255), - (char)(rgb.b*255), (char)(opaq*255)); + //swfoutput_setstrokecolor(&output, (char)(rgb.r*255), (char)(rgb.g*255), (char)(rgb.b*255), (char)(opaq*255)); } void FoFiWrite(void *stream, char *data, int len) @@ -1168,8 +1497,8 @@ char*SWFOutputDev::writeEmbeddedFontToFile(XRef*ref, GfxFont*font) msg(" Collection: %s", c.getCString()); }*/ - if (font->getType() == fontType1C || - font->getType() == fontCIDType0C) { + //if (font->getType() == fontType1C) { + if (0) { //font->getType() == fontType1C) { if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) { fclose(f); msg(" Couldn't read embedded font file"); @@ -1177,9 +1506,11 @@ char*SWFOutputDev::writeEmbeddedFontToFile(XRef*ref, GfxFont*font) } #ifdef XPDF_101 Type1CFontFile *cvt = new Type1CFontFile(fontBuf, fontLen); + if(!cvt) return 0; cvt->convertToType1(f); #else FoFiType1C *cvt = FoFiType1C::make(fontBuf, fontLen); + if(!cvt) return 0; cvt->convertToType1(NULL, gTrue, FoFiWrite, f); #endif //cvt->convertToCIDType0("test", f); @@ -1246,6 +1577,9 @@ char* searchForSuitableFont(GfxFont*gfxFont) char*name = getFontName(gfxFont); char*fontname = 0; char*filename = 0; + + if(!config_use_fontconfig) + return 0; #ifdef HAVE_FONTCONFIG FcPattern *pattern, *match; @@ -1253,33 +1587,47 @@ char* searchForSuitableFont(GfxFont*gfxFont) FcChar8 *v; static int fcinitcalled = false; + + msg(" searchForSuitableFont(%s)", name); // call init ony once if (!fcinitcalled) { + msg(" Initializing FontConfig..."); fcinitcalled = true; - FcInit(); //leaks + if(!FcInit()) { + msg(" FontConfig Initialization failed. Disabling."); + config_use_fontconfig = 0; + return 0; + } + msg(" ...initialized FontConfig"); } + msg(" FontConfig: Create \"%s\" Family Pattern", name); pattern = FcPatternBuild(NULL, FC_FAMILY, FcTypeString, name, NULL); if (gfxFont->isItalic()) // check for italic + msg(" FontConfig: Adding Italic Slant"); FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC); if (gfxFont->isBold()) // check for bold + msg(" FontConfig: Adding Bold Weight"); FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD); + msg(" FontConfig: Try to match..."); // configure and match using the original font name FcConfigSubstitute(0, pattern, FcMatchPattern); FcDefaultSubstitute(pattern); match = FcFontMatch(0, pattern, &result); if (FcPatternGetString(match, "family", 0, &v) == FcResultMatch) { + msg(" FontConfig: family=%s", (char*)v); // if we get an exact match if (strcmp((char *)v, name) == 0) { if (FcPatternGetString(match, "file", 0, &v) == FcResultMatch) { - filename = strdup((char*)v); + filename = strdup((char*)v); // mem leak char *nfn = strrchr(filename, '/'); if(nfn) fontname = strdup(nfn+1); else fontname = filename; } + msg(" FontConfig: Returning \"%s\"", fontname); } else { // initialize patterns FcPatternDestroy(pattern); @@ -1287,33 +1635,40 @@ char* searchForSuitableFont(GfxFont*gfxFont) // now match against serif etc. if (gfxFont->isSerif()) { + msg(" FontConfig: Create Serif Family Pattern"); pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "serif", NULL); } else if (gfxFont->isFixedWidth()) { + msg(" FontConfig: Create Monospace Family Pattern"); pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "monospace", NULL); } else { + msg(" FontConfig: Create Sans Family Pattern"); pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "sans", NULL); } // check for italic if (gfxFont->isItalic()) { + msg(" FontConfig: Adding Italic Slant"); int bb = FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC); } // check for bold if (gfxFont->isBold()) { + msg(" FontConfig: Adding Bold Weight"); int bb = FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD); } + msg(" FontConfig: Try to match... (2)"); // configure and match using serif etc FcConfigSubstitute (0, pattern, FcMatchPattern); FcDefaultSubstitute (pattern); match = FcFontMatch (0, pattern, &result); if (FcPatternGetString(match, "file", 0, &v) == FcResultMatch) { - filename = strdup((char*)v); + filename = strdup((char*)v); // mem leak char *nfn = strrchr(filename, '/'); if(nfn) fontname = strdup(nfn+1); else fontname = filename; } + msg(" FontConfig: Returning \"%s\"", fontname); } } @@ -1341,18 +1696,22 @@ char* SWFOutputDev::substituteFont(GfxFont*gfxFont, char* oldname) fontname = "Times-Roman"; } filename = searchFont(fontname); + if(!filename) { + msg(" Couldn't find font %s- did you install the default fonts?", fontname); + return 0; + } if(substitutepos>=sizeof(substitutesource)/sizeof(char*)) { msg(" Too many fonts in file."); exit(1); } if(oldname) { - substitutesource[substitutepos] = oldname; + substitutesource[substitutepos] = strdup(oldname); //mem leak substitutetarget[substitutepos] = fontname; msg(" substituting %s -> %s", FIXNULL(oldname), FIXNULL(fontname)); substitutepos ++; } - return strdup(filename); + return strdup(filename); //mem leak } void unlinkfont(char* filename) @@ -1390,6 +1749,40 @@ void SWFOutputDev::setXRef(PDFDoc*doc, XRef *xref) this->xref = xref; } +int SWFOutputDev::setGfxFont(char*id, char*filename) +{ + gfxfont_t*font = 0; + fontlist_t*last=0,*l = this->fontlist; + + /* TODO: should this be part of the state? */ + while(l) { + last = l; + if(!strcmp(l->id, id)) { + current_font_id = l->id; + current_gfxfont = l->font; + font = l->font; + swfoutput_gfxaddfont(&this->output, id, current_gfxfont); + return 1; + } + l = l->next; + } + if(!filename) return 0; + font = gfxfont_load(filename); + l = new fontlist_t; + l->font = font; + l->filename = strdup(filename); + l->id = strdup(id); + l->next = 0; + current_font_id = l->id; + current_gfxfont = l->font; + if(last) { + last->next = l; + } else { + this->fontlist = l; + } + swfoutput_gfxaddfont(&this->output, id, current_gfxfont); + return 1; +} void SWFOutputDev::updateFont(GfxState *state) { @@ -1406,20 +1799,24 @@ void SWFOutputDev::updateFont(GfxState *state) too often */ for(t=0;t updateFont(%s) [cached]", fontid); return; - } + }*/ // look for Type 3 font if (gfxFont->getType() == fontType3) { @@ -1427,6 +1824,7 @@ void SWFOutputDev::updateFont(GfxState *state) type3Warning = gTrue; showFontError(gfxFont, 2); } + free(fontid); return; } @@ -1440,7 +1838,7 @@ void SWFOutputDev::updateFont(GfxState *state) if(embedded && (gfxFont->getType() == fontType1 || gfxFont->getType() == fontType1C || - //gfxFont->getType() == fontCIDType0C || + (gfxFont->getType() == fontCIDType0C && forceType0Fonts) || gfxFont->getType() == fontTrueType || gfxFont->getType() == fontCIDType2 )) @@ -1458,24 +1856,30 @@ void SWFOutputDev::updateFont(GfxState *state) msg(" Font %s %scould not be loaded.", fontname, embedded?"":"(not embedded) "); msg(" Try putting a TTF version of that font (named \"%s.ttf\") into /swftools/fonts", fontname); fileName = substituteFont(gfxFont, fontid); - if(fontid) { fontid = substitutetarget[substitutepos-1]; /*ugly hack*/}; + if(fontid) { free(fontid);fontid = strdup(substitutetarget[substitutepos-1]); /*ugly hack*/}; msg(" Font is now %s (%s)", fontid, fileName); } if(!fileName) { msg(" Couldn't set font %s\n", fontid); + free(fontid); return; } msg(" updateFont(%s) -> %s", fontid, fileName); dumpFontInfo("", gfxFont); - swfoutput_setfont(&output, fontid, fileName); + //swfoutput_setfont(&output, fontid, fileName); + + if(!setGfxFont(fontid, 0)) { + setGfxFont(fontid, fileName); + } if(fileName && del) unlinkfont(fileName); if(fileName) free(fileName); + free(fontid); } #define SQR(x) ((x)*(x)) @@ -1530,7 +1934,7 @@ unsigned char* antialize(unsigned char*data, int width, int height, int newwidth void SWFOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap*colorMap, GBool invert, - GBool inlineImg, int mask) + GBool inlineImg, int mask, int*maskColors) { FILE *fi; int c; @@ -1581,22 +1985,21 @@ void SWFOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str, } if(mask) { - int yes=0,i,j; + int i,j; unsigned char buf[8]; - int xid = 0; - int yid = 0; int x,y; unsigned char*pic = new unsigned char[width*height]; RGBA pal[256]; GfxRGB rgb; state->getFillRGB(&rgb); + memset(pal,255,sizeof(pal)); - pal[0].r = (int)(rgb.r*255); pal[0].g = (int)(rgb.g*255); - pal[0].b = (int)(rgb.b*255); pal[0].a = 255; - pal[1].r = 0; pal[1].g = 0; pal[1].b = 0; pal[1].a = 0; + pal[0].r = (int)(rgb.r*255); pal[1].r = 0; + pal[0].g = (int)(rgb.g*255); pal[1].g = 0; + pal[0].b = (int)(rgb.b*255); pal[1].b = 0; + pal[0].a = 255; pal[1].a = 0; + int numpalette = 2; - xid += pal[1].r*3 + pal[1].g*11 + pal[1].b*17; - yid += pal[1].r*7 + pal[1].g*5 + pal[1].b*23; int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3)); int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2)); for (y = 0; y < height; ++y) @@ -1606,173 +2009,120 @@ void SWFOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str, if(invert) buf[0]=1-buf[0]; pic[width*y+x] = buf[0]; - xid+=x*buf[0]+1; - yid+=y*buf[0]*3+1; } /* the size of the drawn image is added to the identifier as the same image may require different bitmaps if displayed at different sizes (due to antialiasing): */ - if(type3active) { - xid += realwidth; - yid += realheight; - } int t,found = -1; - for(t=0;tgetNumPixelComps()!=1 || str->getKind()==strDCT) - { + + if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) { RGBA*pic=new RGBA[width*height]; - int xid = 0; - int yid = 0; for (y = 0; y < height; ++y) { for (x = 0; x < width; ++x) { - int r,g,b,a; imgStr->getPixel(pixBuf); colorMap->getRGB(pixBuf, &rgb); - pic[width*y+x].r = r = (U8)(rgb.r * 255 + 0.5); - pic[width*y+x].g = g = (U8)(rgb.g * 255 + 0.5); - pic[width*y+x].b = b = (U8)(rgb.b * 255 + 0.5); - pic[width*y+x].a = a = 255;//(U8)(rgb.a * 255 + 0.5); - xid += x*r+x*b*3+x*g*7+x*a*11; - yid += y*r*3+y*b*17+y*g*19+y*a*11; + pic[width*y+x].r = (U8)(rgb.r * 255 + 0.5); + pic[width*y+x].g = (U8)(rgb.g * 255 + 0.5); + pic[width*y+x].b = (U8)(rgb.b * 255 + 0.5); + pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5); } } - int t,found = -1; - for(t=0;tgetKind()==strDCT) - pic_ids[picpos] = swfoutput_drawimagejpeg(&output, pic, width, height, - x1,y1,x2,y2,x3,y3,x4,y4); - else - pic_ids[picpos] = swfoutput_drawimagelossless(&output, pic, width, height, - x1,y1,x2,y2,x3,y3,x4,y4); - pic_xids[picpos] = xid; - pic_yids[picpos] = yid; - pic_width[picpos] = width; - pic_height[picpos] = height; - if(picpos<1024) - picpos++; - } else { - swfoutput_drawimageagain(&output, pic_ids[found], width, height, - x1,y1,x2,y2,x3,y3,x4,y4); - } + if(str->getKind()==strDCT) + swfoutput_drawimagejpeg(&output, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4); + else + swfoutput_drawimagelossless(&output, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4); delete pic; delete imgStr; return; - } - else - { - U8*pic = new U8[width*height]; + } else { + RGBA*pic=new RGBA[width*height]; RGBA pal[256]; int t; - int xid=0,yid=0; - for(t=0;t<256;t++) - { - int r,g,b,a; + for(t=0;t<256;t++) { pixBuf[0] = t; colorMap->getRGB(pixBuf, &rgb); - pal[t].r = r = (U8)(rgb.r * 255 + 0.5); - pal[t].g = g = (U8)(rgb.g * 255 + 0.5); - pal[t].b = b = (U8)(rgb.b * 255 + 0.5); - pal[t].a = a = 255;//(U8)(rgb.b * 255 + 0.5); - xid += t*r+t*b*3+t*g*7+t*a*11; - xid += (~t)*r+t*b*3+t*g*7+t*a*11; + /*if(maskColors && *maskColors==t) { + msg(" Color %d is transparent", t); + if (imgData->maskColors) { + *alpha = 0; + for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) { + if (pix[i] < imgData->maskColors[2*i] || + pix[i] > imgData->maskColors[2*i+1]) { + *alpha = 1; + break; + } + } + } else { + *alpha = 1; + } + if(!*alpha) { + pal[t].r = 0; + pal[t].g = 0; + pal[t].b = 0; + pal[t].a = 0; + } + } else*/ { + pal[t].r = (U8)(rgb.r * 255 + 0.5); + pal[t].g = (U8)(rgb.g * 255 + 0.5); + pal[t].b = (U8)(rgb.b * 255 + 0.5); + pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5); + } } for (y = 0; y < height; ++y) { for (x = 0; x < width; ++x) { imgStr->getPixel(pixBuf); - pic[width*y+x] = pixBuf[0]; - xid += x*pixBuf[0]*7; - yid += y*pixBuf[0]*3; + pic[width*y+x] = pal[pixBuf[0]]; } } - int found = -1; - for(t=0;t drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg); - drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1); + drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0); } void SWFOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, int *maskColors, GBool inlineImg) { + if(states[statepos].textRender & 4) //clipped + return; + msg(" drawImage %dx%d, %s %s, inline=%d", width, height, colorMap?"colorMap":"no colorMap", maskColors?"maskColors":"no maskColors", @@ -1798,7 +2153,7 @@ void SWFOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, if(colorMap) msg(" colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(), colorMap->getBits(),colorMap->getColorSpace()->getMode()); - drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0); + drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors); } SWFOutputDev*output = 0; @@ -1845,12 +2200,39 @@ static void printInfoDate(Dict *infoDict, char *key, char *fmt) { obj.free(); } +int jpeg_dpi = 0; +int ppm_dpi = 0; + void pdfswf_setparameter(char*name, char*value) { + msg(" setting parameter %s to \"%s\"", name, value); if(!strcmp(name, "caplinewidth")) { caplinewidth = atof(value); } else if(!strcmp(name, "zoom")) { - zoom = atoi(value); + char buf[80]; + zoom = atof(value); + sprintf(buf, "%f", (double)jpeg_dpi/(double)zoom); + swfoutput_setparameter("jpegsubpixels", buf); + sprintf(buf, "%f", (double)ppm_dpi/(double)zoom); + swfoutput_setparameter("ppmsubpixels", buf); + } else if(!strcmp(name, "jpegdpi")) { + char buf[80]; + jpeg_dpi = atoi(value); + sprintf(buf, "%f", (double)jpeg_dpi/(double)zoom); + swfoutput_setparameter("jpegsubpixels", buf); + } else if(!strcmp(name, "ppmdpi")) { + char buf[80]; + ppm_dpi = atoi(value); + sprintf(buf, "%f", (double)ppm_dpi/(double)zoom); + swfoutput_setparameter("ppmsubpixels", buf); + } else if(!strcmp(name, "forceType0Fonts")) { + forceType0Fonts = atoi(value); + } else if(!strcmp(name, "fontdir")) { + pdfswf_addfontdir(value); + } else if(!strcmp(name, "languagedir")) { + pdfswf_addlanguagedir(value); + } else if(!strcmp(name, "fontconfig")) { + config_use_fontconfig = atoi(value); } else { swfoutput_setparameter(name, value); } @@ -1867,6 +2249,82 @@ void pdfswf_addfont(char*filename) } } +static char* dirseparator() +{ +#ifdef WIN32 + return "\\"; +#else + return "/"; +#endif +} + +void pdfswf_addlanguagedir(char*dir) +{ + if(!globalParams) + globalParams = new GlobalParams(""); + + msg(" Adding %s to language pack directories", dir); + + int l; + FILE*fi = 0; + char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc")); + strcpy(config_file, dir); + strcat(config_file, dirseparator()); + strcat(config_file, "add-to-xpdfrc"); + + fi = fopen(config_file, "rb"); + if(!fi) { + msg(" Could not open %s", config_file); + return; + } + globalParams->parseFile(new GString(config_file), fi); + fclose(fi); +} + +void pdfswf_addfontdir(char*dirname) +{ +#ifdef HAVE_DIRENT_H + msg(" Adding %s to font directories", dirname); + DIR*dir = opendir(dirname); + if(!dir) { + msg(" Couldn't open directory %s\n", dirname); + return; + } + struct dirent*ent; + while(1) { + ent = readdir (dir); + if (!ent) + break; + int l; + char*name = ent->d_name; + char type = 0; + if(!name) continue; + l=strlen(name); + if(l<4) + continue; + if(!strncasecmp(&name[l-4], ".pfa", 4)) + type=1; + if(!strncasecmp(&name[l-4], ".pfb", 4)) + type=3; + if(!strncasecmp(&name[l-4], ".ttf", 4)) + type=2; + if(type) + { + char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2); + strcpy(fontname, dirname); + strcat(fontname, dirseparator()); + strcat(fontname, name); + msg(" Adding %s to fonts", fontname); + pdfswf_addfont(fontname); + } + } + closedir(dir); +#else + msg(" No dirent.h- unable to add font dir %s", dir); +#endif +} + + typedef struct _pdf_doc_internal { int protect; @@ -1893,7 +2351,8 @@ pdf_doc_t* pdf_init(char*filename, char*userPassword) Object info; // read config file - globalParams = new GlobalParams(""); + if(!globalParams) + globalParams = new GlobalParams(""); // open PDF file if (userPassword && userPassword[0]) { @@ -1912,7 +2371,7 @@ pdf_doc_t* pdf_init(char*filename, char*userPassword) // print doc info i->doc->getDocInfo(&info); if (info.isDict() && - (screenloglevel>=LOGLEVEL_NOTICE)) { + (getScreenLogLevel()>=LOGLEVEL_NOTICE)) { printInfoString(info.getDict(), "Title", "Title: %s\n"); printInfoString(info.getDict(), "Subject", "Subject: %s\n"); printInfoString(info.getDict(), "Keywords", "Keywords: %s\n"); @@ -1940,8 +2399,8 @@ pdf_doc_t* pdf_init(char*filename, char*userPassword) i->protect = 0; if (i->doc->isEncrypted()) { if(!i->doc->okToCopy()) { - printf("PDF disallows copying. Terminating.\n"); - exit(1); //bail out + printf("PDF disallows copying.\n"); + return 0; } if(!i->doc->okToChange() || !i->doc->okToAddNotes()) i->protect = 1; @@ -1970,7 +2429,7 @@ class MemCheck { public: ~MemCheck() { - delete globalParams; + delete globalParams;globalParams=0; Object::memCheck(stderr); gMemReport(stderr); } @@ -1992,6 +2451,9 @@ void pdf_destroy(pdf_doc_t*pdf_doc) pdf_page_t* pdf_getpage(pdf_doc_t*pdf_doc, int page) { pdf_doc_internal_t*di= (pdf_doc_internal_t*)pdf_doc->internal; + + if(page < 1 || page > pdf_doc->num_pages) + return 0; pdf_page_t* pdf_page = (pdf_page_t*)malloc(sizeof(pdf_page_t)); pdf_page_internal_t*pi= (pdf_page_internal_t*)malloc(sizeof(pdf_page_internal_t)); @@ -2028,10 +2490,27 @@ void swf_output_setparameter(swf_output_t*swf_output, char*name, char*value) pdfswf_setparameter(name, value); } -void swf_output_save(swf_output_t*swf, char*filename) +void swf_output_pagefeed(swf_output_t*swf) { swf_output_internal_t*i= (swf_output_internal_t*)swf->internal; - i->outputDev->save(filename); + i->outputDev->pagefeed(); + i->outputDev->getDimensions(&swf->x1, &swf->y1, &swf->x2, &swf->y2); +} + +int swf_output_save(swf_output_t*swf, char*filename) +{ + swf_output_internal_t*i= (swf_output_internal_t*)swf->internal; + int ret = i->outputDev->save(filename); + i->outputDev->getDimensions(&swf->x1, &swf->y1, &swf->x2, &swf->y2); + return ret; +} + +void* swf_output_get(swf_output_t*swf) +{ + swf_output_internal_t*i= (swf_output_internal_t*)swf->internal; + void* ret = i->outputDev->getSWF(); + i->outputDev->getDimensions(&swf->x1, &swf->y1, &swf->x2, &swf->y2); + return ret; } void swf_output_destroy(swf_output_t*output) @@ -2042,11 +2521,10 @@ void swf_output_destroy(swf_output_t*output) free(output); } - -void pdf_page_render(pdf_page_t*page, swf_output_t*output) +void pdf_page_render2(pdf_page_t*page, swf_output_t*swf) { pdf_doc_internal_t*pi = (pdf_doc_internal_t*)page->parent->internal; - swf_output_internal_t*si = (swf_output_internal_t*)output->internal; + swf_output_internal_t*si = (swf_output_internal_t*)swf->internal; if(pi->protect) { swfoutput_setparameter("protect", "1"); @@ -2057,13 +2535,61 @@ void pdf_page_render(pdf_page_t*page, swf_output_t*output) #else pi->doc->displayPage((OutputDev*)si->outputDev, page->nr, zoom, zoom, /*rotate*/0, true, /*doLinks*/(int)1); #endif + si->outputDev->getDimensions(&swf->x1, &swf->y1, &swf->x2, &swf->y2); +} + +void pdf_page_rendersection(pdf_page_t*page, swf_output_t*output, int x, int y, int x1, int y1, int x2, int y2) +{ + pdf_doc_internal_t*pi = (pdf_doc_internal_t*)page->parent->internal; + swf_output_internal_t*si = (swf_output_internal_t*)output->internal; + + si->outputDev->setMove(x,y); + if((x1|y1|x2|y2)==0) x2++; + si->outputDev->setClip(x1,y1,x2,y2); + + pdf_page_render2(page, output); +} +void pdf_page_render(pdf_page_t*page, swf_output_t*output) +{ + pdf_doc_internal_t*pi = (pdf_doc_internal_t*)page->parent->internal; + swf_output_internal_t*si = (swf_output_internal_t*)output->internal; + + si->outputDev->setMove(0,0); + si->outputDev->setClip(0,0,0,0); + + pdf_page_render2(page, output); } + pdf_page_info_t* pdf_page_getinfo(pdf_page_t*page) { + pdf_doc_internal_t*pi = (pdf_doc_internal_t*)page->parent->internal; pdf_page_internal_t*i= (pdf_page_internal_t*)page->internal; pdf_page_info_t*info = (pdf_page_info_t*)malloc(sizeof(pdf_page_info_t)); memset(info, 0, sizeof(pdf_page_info_t)); + + InfoOutputDev*output = new InfoOutputDev; + +#ifdef XPDF_101 + pi->doc->displayPage((OutputDev*)output, page->nr, /*zoom*/zoom, /*rotate*/0, /*doLinks*/(int)1); +#else + pi->doc->displayPage((OutputDev*)output, page->nr, zoom, zoom, /*rotate*/0, true, /*doLinks*/(int)1); +#endif + + info->xMin = output->x1; + info->yMin = output->y1; + info->xMax = output->x2; + info->yMax = output->y2; + info->number_of_images = output->num_images; + info->number_of_links = output->num_links; + info->number_of_fonts = output->num_fonts; + + delete output; + return info; } +void pdf_page_info_destroy(pdf_page_info_t*info) +{ + free(info); +}