X-Git-Url: http://git.asbjorn.biz/?a=blobdiff_plain;f=pdf2swf%2FSWFOutputDev.cc;h=ca3413750e28194e3acc8c9be32a4db567d01eee;hb=db3e346ab9aa6d3e935a2cea0e438fb6e051221c;hp=2809efcb14037f96ac0843788ed4940d600f3edc;hpb=fd9d112727d70722bf2702eb14c2dd9cc2c554e1;p=swftools.git diff --git a/pdf2swf/SWFOutputDev.cc b/pdf2swf/SWFOutputDev.cc index 2809efc..ca34137 100644 --- a/pdf2swf/SWFOutputDev.cc +++ b/pdf2swf/SWFOutputDev.cc @@ -23,6 +23,15 @@ #include #include #include "../config.h" +#ifdef HAVE_DIRENT_H +#include +#endif +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_FONTCONFIG +#include +#endif //xpdf header files #include "config.h" #include "gfile.h" @@ -42,85 +51,98 @@ #include "GfxFont.h" #include "CharCodeToUnicode.h" #include "NameToUnicodeTable.h" -#include "FontFile.h" #include "GlobalParams.h" +//#define XPDF_101 +#ifdef XPDF_101 +#include "FontFile.h" +#else +#include "FoFiType1C.h" +#include "FoFiTrueType.h" +#include "GHash.h" +#endif +#include "SWFOutputDev.h" + //swftools header files #include "swfoutput.h" #include "../lib/log.h" +#include "../lib/gfxdevice.h" +#include "../lib/gfxtools.h" +#include "../lib/gfxfont.h" -#include "ttf2pt1.h" #include -static PDFDoc*doc = 0; -static char* swffilename = 0; -int numpages; -int currentpage; +typedef struct _fontfile +{ + char*filename; + int used; +} fontfile_t; + +typedef struct _parameter +{ + char*name; + char*value; + struct _parameter*next; +} parameter_t; -// swf <-> pdf pages -int*pages = 0; -int pagebuflen = 0; -int pagepos = 0; +static parameter_t* device_config = 0; +static parameter_t* device_config_next = 0; -double caplinewidth = 3.0; +// for pdfswf_addfont +static fontfile_t fonts[2048]; +static int fontnum = 0; + +static int config_use_fontconfig = 1; + +/* config */ +static double caplinewidth = 3.0; +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); -double fontsizes[] = -{ - 0.833,0.833,0.889,0.889, - 0.788,0.722,0.833,0.778, - 0.600,0.600,0.600,0.600, - 0.576,0.576,0.576,0.576, - 0.733 //? -}; -char*fontnames[]={ -"Helvetica", -"Helvetica-Bold", -"Helvetica-BoldOblique", -"Helvetica-Oblique", -"Times-Roman", -"Times-Bold", -"Times-BoldItalic", -"Times-Italic", -"Courier", -"Courier-Bold", -"Courier-BoldOblique", -"Courier-Oblique", -"Symbol", -"Symbol", -"Symbol", -"Symbol", -"ZapfDingBats" -}; - struct mapping { char*pdffont; char*filename; - int id; } pdf2t1map[] ={ -{"Times-Roman", "n021003l.pfb"}, -{"Times-Italic", "n021023l.pfb"}, -{"Times-Bold", "n021004l.pfb"}, -{"Times-BoldItalic", "n021024l.pfb"}, -{"Helvetica", "n019003l.pfb"}, -{"Helvetica-Oblique", "n019023l.pfb"}, -{"Helvetica-Bold", "n019004l.pfb"}, -{"Helvetica-BoldOblique", "n019024l.pfb"}, -{"Courier", "n022003l.pfb"}, -{"Courier-Oblique", "n022023l.pfb"}, -{"Courier-Bold", "n022004l.pfb"}, -{"Courier-BoldOblique", "n022024l.pfb"}, -{"Symbol", "s050000l.pfb"}, -{"ZapfDingbats", "d050000l.pfb"}}; - -class GfxState; -class GfxImageColorMap; +{"Times-Roman", "n021003l"}, +{"Times-Italic", "n021023l"}, +{"Times-Bold", "n021004l"}, +{"Times-BoldItalic", "n021024l"}, +{"Helvetica", "n019003l"}, +{"Helvetica-Oblique", "n019023l"}, +{"Helvetica-Bold", "n019004l"}, +{"Helvetica-BoldOblique", "n019024l"}, +{"Courier", "n022003l"}, +{"Courier-Oblique", "n022023l"}, +{"Courier-Bold", "n022004l"}, +{"Courier-BoldOblique", "n022024l"}, +{"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 InfoOutputDev; class SWFOutputDev: public OutputDev { - struct swfoutput output; - int outputstarted; public: + gfxdevice_t* output; // Constructor. SWFOutputDev(); @@ -128,6 +150,21 @@ public: // Destructor. virtual ~SWFOutputDev() ; + void setMove(int x,int y); + void setClip(int x1,int y1,int x2,int y2); + + void setInfo(InfoOutputDev*info) {this->info = info;} + + int save(char*filename); + + // Start a page. + void startFrame(int width, int height); + + virtual void startPage(int pageNum, GfxState *state, double x1, double y1, double x2, double y2) ; + + void endframe(); + void* getSWF(); + //----- get info about output device // Does this device use upside-down coordinates? @@ -137,14 +174,14 @@ public: // Does this device use drawChar() or drawString()? virtual GBool useDrawChar(); + // Can this device draw gradients? + virtual GBool useGradients(); + virtual GBool interpretType3Chars() {return gTrue;} //----- initialization and control - void startDoc(XRef *xref); - - // Start a page. - virtual void startPage(int pageNum, GfxState *state) ; + void setXRef(PDFDoc*doc, XRef *xref); //----- link borders virtual void drawLink(Link *link, Catalog *catalog) ; @@ -184,6 +221,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, @@ -197,35 +235,313 @@ 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, double quality); + void strokeGfxline(GfxState *state, gfxline_t*line); + void clipToGfxLine(GfxState *state, gfxline_t*line); + void fillGfxLine(GfxState *state, gfxline_t*line); + + void finish(); + + gfxresult_t*result; //filled when complete + + char outer_clip_box; //whether the page clip box is still on + + InfoOutputDev*info; + SWFOutputState states[64]; + int statepos; + + int currentpage; + PDFDoc*doc; XRef*xref; - int searchT1Font(char*name); + char* searchFont(char*name); char* substituteFont(GfxFont*gfxFont, char*oldname); char* writeEmbeddedFontToFile(XRef*ref, GfxFont*font); int t1id; + int textmodeinfo; // did we write "Text will be rendered as polygon" yet? int jpeginfo; // did we write "File contains jpegs" yet? int pbminfo; // did we write "File contains jpegs" yet? int linkinfo; // did we write "File contains links" yet? int ttfinfo; // did we write "File contains TrueType Fonts" yet? + int gradientinfo; // did we write "File contains Gradients yet? int type3active; // are we between beginType3()/endType3()? GfxState *laststate; + + 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; + + int*pages; + int pagebuflen; + int pagepos; + + friend void swf_output_preparepage(swf_output_t*swf, int pdfpage, int outputpage); +}; + +typedef struct _drawnchar +{ + gfxcoord_t x,y; + int charid; + gfxcolor_t color; +} drawnchar_t; + +class CharBuffer +{ + drawnchar_t * chars; + int buf_size; + int num_chars; + +public: + + CharBuffer() + { + buf_size = 32; + chars = (drawnchar_t*)malloc(sizeof(drawnchar_t)*buf_size); + memset(chars, 0, sizeof(drawnchar_t)*buf_size); + num_chars = 0; + } + ~CharBuffer() + { + free(chars);chars = 0; + } + + void grow(int size) + { + if(size>=buf_size) { + buf_size += 32; + chars = (drawnchar_t*)realloc(chars, sizeof(drawnchar_t)*buf_size); + } + } + + void addChar(int charid, gfxcoord_t x, gfxcoord_t y, gfxcolor_t color) + { + grow(num_chars); + chars[num_chars].x = x; + chars[num_chars].y = y; + chars[num_chars].color = color; + chars[num_chars].charid = charid; + } +}; + +static char*getFontID(GfxFont*font); + +struct FontInfo +{ + GfxFont*font; + double max_size; +}; + +class InfoOutputDev: public OutputDev +{ + GHash* id2font; + FontInfo* currentfont; + public: + int x1,y1,x2,y2; + int num_links; + int num_images; + int num_fonts; + + InfoOutputDev() + { + num_links = 0; + num_images = 0; + num_fonts = 0; + id2font = new GHash(); + } + virtual ~InfoOutputDev() + { + delete id2font; + } + virtual GBool upsideDown() {return gTrue;} + virtual GBool useDrawChar() {return gTrue;} + virtual GBool useGradients() {return gTrue;} + virtual GBool interpretType3Chars() {return gTrue;} + virtual void startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2) + { + double x1,y1,x2,y2; + state->transform(crop_x1,crop_y1,&x1,&y1); + state->transform(crop_x2,crop_y2,&x2,&y2); + if(x2x1 = (int)x1; + this->y1 = (int)y1; + this->x2 = (int)x2; + this->y2 = (int)y2; + } + virtual void drawLink(Link *link, Catalog *catalog) + { + num_links++; + } + virtual double getMaximumFontSize(char*id) + { + FontInfo*info = (FontInfo*)id2font->lookup(id); + if(!info) { + msg(" Unknown font id: %s", id); + return 0.0; + } + return info->max_size; + } + + virtual void updateFont(GfxState *state) + { + GfxFont*font = state->getFont(); + if(!font) + return; + char*id = getFontID(font); + + FontInfo*info = (FontInfo*)id2font->lookup(id); + if(!info) { + GString* idStr = new GString(id); + info = new FontInfo; + info->font = font; + info->max_size = 0; + id2font->add(idStr, (void*)info); + free(id); + num_fonts++; + } + currentfont = info; + } + virtual void drawChar(GfxState *state, double x, double y, + double dx, double dy, + double originX, double originY, + CharCode code, Unicode *u, int uLen) + { + int render = state->getRender(); + if (render == 3) + return; + double m11,m21,m12,m22; + state->getFontTransMat(&m11, &m12, &m21, &m22); + m11 *= state->getHorizScaling(); + m21 *= state->getHorizScaling(); + double lenx = sqrt(m11*m11 + m12*m12); + double leny = sqrt(m21*m21 + m22*m22); + double len = lenx>leny?lenx:leny; + if(currentfont && currentfont->max_size < len) { + currentfont->max_size = len; + } + } + virtual void drawImageMask(GfxState *state, Object *ref, Stream *str, + int width, int height, GBool invert, + GBool inlineImg) + { + num_images++; + } + virtual void drawImage(GfxState *state, Object *ref, Stream *str, + int width, int height, GfxImageColorMap *colorMap, + int *maskColors, GBool inlineImg) + { + num_images++; + } +}; + +SWFOutputDev::SWFOutputDev() +{ + jpeginfo = 0; + textmodeinfo = 0; + ttfinfo = 0; + linkinfo = 0; + pbminfo = 0; + type3active = 0; + statepos = 0; + xref = 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; + result = 0; + outer_clip_box = 0; + pages = 0; + pagebuflen = 0; + pagepos = 0; + output = (gfxdevice_t*)malloc(sizeof(gfxdevice_t)); + gfxdevice_swf_init(output); + /* configure device */ + parameter_t*p = device_config; + while(p) { + output->setparameter(output, p->name, p->value); + p = p->next; + } }; + +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; +} + +static char*getFontID(GfxFont*font) +{ + GString*gstr = font->getName(); + char* fontname = gstr==0?0:gstr->getCString(); + if(fontname==0) { + char buf[32]; + Ref*r=font->getID(); + sprintf(buf, "UFONT%d", r->num); + return strdup(buf); + } + return strdup(fontname); +} + +static char*getFontName(GfxFont*font) +{ + char*fontid = getFontID(font); + char*fontname= 0; + char* plus = strchr(fontid, '+'); + if(plus && plus < &fontid[strlen(fontid)-1]) { + fontname = strdup(plus+1); + } else { + fontname = strdup(fontid); + } + free(fontid); + return fontname; +} -char mybuf[1024]; -char* gfxstate2str(GfxState *state) +static char mybuf[1024]; +static char* gfxstate2str(GfxState *state) { char*bufpos = mybuf; GfxRGB rgb; @@ -303,8 +619,8 @@ char* gfxstate2str(GfxState *state) if(state->getLineJoin()!=0) bufpos+=sprintf(bufpos,"ML%d ", state->getMiterLimit()); - if(state->getFont() && state->getFont()->getName() && state->getFont()->getName()->getCString()) - bufpos+=sprintf(bufpos,"F\"%s\" ",((state->getFont())->getName())->getCString()); + if(state->getFont() && getFontID(state->getFont())) + bufpos+=sprintf(bufpos,"F\"%s\" ",getFontID(state->getFont())); bufpos+=sprintf(bufpos,"FS%.1f ", state->getFontSize()); bufpos+=sprintf(bufpos,"MAT[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f] ", state->getTextMat()[0],state->getTextMat()[1],state->getTextMat()[2], state->getTextMat()[3],state->getTextMat()[4],state->getTextMat()[5]); @@ -331,14 +647,14 @@ char* gfxstate2str(GfxState *state) return mybuf; } -void dumpFontInfo(char*loglevel, GfxFont*font); -int lastdumps[1024]; -int lastdumppos = 0; +static void dumpFontInfo(char*loglevel, GfxFont*font); +static int lastdumps[1024]; +static int lastdumppos = 0; /* nr = 0 unknown nr = 1 substituting nr = 2 type 3 */ -void showFontError(GfxFont*font, int nr) +static void showFontError(GfxFont*font, int nr) { Ref*r=font->getID(); int t; @@ -358,592 +674,994 @@ void showFontError(GfxFont*font, int nr) dumpFontInfo("", font); } -void dumpFontInfo(char*loglevel, GfxFont*font) +static void dumpFontInfo(char*loglevel, GfxFont*font) { - GString *gstr; - char*name = 0; - gstr = font->getName(); + char* id = getFontID(font); + char* name = getFontName(font); Ref* r=font->getID(); - msg("%s=========== %s (ID:%d,%d) ==========\n", loglevel, gstr?FIXNULL(gstr->getCString()):"(unknown font)", r->num,r->gen); + msg("%s=========== %s (ID:%d,%d) ==========\n", loglevel, name, r->num,r->gen); - gstr = font->getTag(); - if(gstr) - msg("%sTag: %s\n", loglevel, FIXNULL(gstr->getCString())); + GString*gstr = font->getTag(); + + msg("%s| Tag: %s\n", loglevel, id); - if(font->isCIDFont()) msg("%sis CID font\n", loglevel); + if(font->isCIDFont()) msg("%s| is CID font\n", loglevel); GfxFontType type=font->getType(); switch(type) { case fontUnknownType: - msg("%sType: unknown\n",loglevel); + msg("%s| Type: unknown\n",loglevel); break; case fontType1: - msg("%sType: 1\n",loglevel); + msg("%s| Type: 1\n",loglevel); break; case fontType1C: - msg("%sType: 1C\n",loglevel); + msg("%s| Type: 1C\n",loglevel); break; case fontType3: - msg("%sType: 3\n",loglevel); + msg("%s| Type: 3\n",loglevel); break; case fontTrueType: - msg("%sType: TrueType\n",loglevel); + msg("%s| Type: TrueType\n",loglevel); break; case fontCIDType0: - msg("%sType: CIDType0\n",loglevel); + msg("%s| Type: CIDType0\n",loglevel); break; case fontCIDType0C: - msg("%sType: CIDType0C\n",loglevel); + msg("%s| Type: CIDType0C\n",loglevel); break; case fontCIDType2: - msg("%sType: CIDType2\n",loglevel); + msg("%s| Type: CIDType2\n",loglevel); break; } 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("%sEmbedded 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) - msg("%sExternal Font file: %s\n", loglevel, FIXNULL(gstr->getCString())); + msg("%s| External Font file: %s\n", loglevel, FIXNULL(gstr->getCString())); // Get font descriptor flags. - if(font->isFixedWidth()) msg("%sis fixed width\n", loglevel); - if(font->isSerif()) msg("%sis serif\n", loglevel); - if(font->isSymbolic()) msg("%sis symbolic\n", loglevel); - if(font->isItalic()) msg("%sis italic\n", loglevel); - if(font->isBold()) msg("%sis bold\n", loglevel); + if(font->isFixedWidth()) msg("%s| is fixed width\n", loglevel); + if(font->isSerif()) msg("%s| is serif\n", loglevel); + 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");} -SWFOutputDev::SWFOutputDev() + +void dump_outline(gfxline_t*line) { - jpeginfo = 0; - ttfinfo = 0; - linkinfo = 0; - pbminfo = 0; - type3active = 0; - clippos = 0; - clipping[clippos] = 0; - outputstarted = 0; - xref = 0; -// printf("SWFOutputDev::SWFOutputDev() \n"); -}; + 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; + } +} -T1_OUTLINE* gfxPath_to_T1_OUTLINE(GfxState*state, GfxPath*path) +gfxline_t* gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user_movex, int user_movey) { int num = path->getNumSubpaths(); int s,t; - bezierpathsegment*start,*last=0; - bezierpathsegment*outline = start = new 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 = T1_PATHTYPE_MOVE; - outline->dest.x = 0; - outline->dest.y = 0; - outline->link = 0; - return (T1_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 = T1_PATHTYPE_MOVE; - outline->dest.x = x; - outline->dest.y = y; - outline->link = (T1_OUTLINE*)new bezierpathsegment(); - outline = (bezierpathsegment*)outline->link; + double x,y; + + state->transform(subpath->getX(s),subpath->getY(s),&x,&y); + x += user_movex; + y += user_movey; + + 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?T1_PATHTYPE_BEZIER:T1_PATHTYPE_LINE; - outline->link = 0; - outline->link = (T1_OUTLINE*)new 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, 0.05); + } + needsfix = 1; cpos = 0; - lastx = nx; - lasty = ny; } } } - last->link = 0; - return (T1_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 *----------------------------------------------------------------------------*/ void SWFOutputDev::stroke(GfxState *state) { - msg(" stroke\n"); GfxPath * path = state->getPath(); + gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex, user_movey); + 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); - - m.m11 = 1; m.m21 = 0; m.m22 = 1; - m.m12 = 0; m.m13 = 0; m.m23 = 0; - T1_OUTLINE*outline = gfxPath_to_T1_OUTLINE(state, path); + 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; + + 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); + } - lineJoin = 1; // other line joins are not yet supported by the swf encoder - // TODO: support bevel joints + line2 = gfxtool_dash_line(line, dash, dashphase); + line = line2; + free(dash); + msg(" After dashing:"); + } + + 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); + output->stroke(output, line, width, &col, capType, joinType, miterLimit); + + if(line2) + gfxline_free(line2); +} - 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); } + output->fill(output, line, &col); } void SWFOutputDev::fill(GfxState *state) { - msg(" fill\n"); 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; - T1_OUTLINE*outline = gfxPath_to_T1_OUTLINE(state, path); - swfoutput_setdrawmode(&output, DRAWMODE_FILL); - swfoutput_drawpath(&output, outline, &m); + gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex, user_movey); + fillGfxLine(state, line); + gfxline_free(line); } void SWFOutputDev::eoFill(GfxState *state) { - msg(" eofill\n"); 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; - T1_OUTLINE*outline = gfxPath_to_T1_OUTLINE(state, path); - swfoutput_setdrawmode(&output, DRAWMODE_EOFILL); - swfoutput_drawpath(&output, outline, &m); + gfxcolor_t col = getFillColor(state); + + gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex, user_movey); + + if(getLogLevel() >= LOGLEVEL_TRACE) { + msg(" eofill\n"); + dump_outline(line); + } + + output->fill(output, line, &col); + gfxline_free(line); } + void SWFOutputDev::clip(GfxState *state) { - msg(" clip\n"); 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; - T1_OUTLINE*outline = gfxPath_to_T1_OUTLINE(state, path); - swfoutput_startclip(&output, outline, &m); - clipping[clippos] ++; + gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex, user_movey); + clipToGfxLine(state, line); + gfxline_free(line); +} + +void SWFOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line) +{ + if(getLogLevel() >= LOGLEVEL_TRACE) { + msg(" clip\n"); + dump_outline(line); + } + + output->startclip(output, line); + states[statepos].clipping++; } void SWFOutputDev::eoClip(GfxState *state) { - msg(" eoclip\n"); 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; - T1_OUTLINE*outline = gfxPath_to_T1_OUTLINE(state, path); - swfoutput_startclip(&output, outline, &m); - clipping[clippos] ++; + gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex, user_movey); + + if(getLogLevel() >= LOGLEVEL_TRACE) { + msg(" eoclip\n"); + dump_outline(line); + } + + output->startclip(output, line); + states[statepos].clipping++; + gfxline_free(line); +} + +void SWFOutputDev::endframe() +{ + if(outer_clip_box) { + output->endclip(output); + outer_clip_box = 0; + } + + output->endpage(output); +} + +void SWFOutputDev::finish() +{ + if(outer_clip_box) { + if(output) { + output->endclip(output); + } + outer_clip_box = 0; + } + if(output) { + this->result = output->finish(output); + free(output);output=0; + } +} + +int SWFOutputDev::save(char*filename) +{ + finish(); + return result->save(result, filename); +} +void* SWFOutputDev::getSWF() +{ + finish(); + return result->get(result, "swf"); } SWFOutputDev::~SWFOutputDev() { - swfoutput_destroy(&output); - outputstarted = 0; + finish(); + + if(this->result) { + this->result->destroy(this->result); + this->result = 0; + } + + if(this->pages) { + free(this->pages); this->pages = 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; + } + this->fontlist = 0; }; GBool SWFOutputDev::upsideDown() { - msg(" upsidedown?"); return gTrue; }; GBool SWFOutputDev::useDrawChar() { - msg(" usedrawchar?"); return gTrue; } +GBool SWFOutputDev::useGradients() +{ + if(!gradientinfo) + { + msg(" File contains gradients"); + gradientinfo = 1; + } + 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]; + } + + /* we don't need to "draw" space characters, so don't overdo the search + for a matching glyph */ + if(charname && !strcasecmp(charname, "space")) + return -1; + + 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; } -int charcounter = 0; void SWFOutputDev::drawChar(GfxState *state, double x, double y, double dx, double dy, double originX, double originY, CharCode c, Unicode *_u, int uLen) { + msg(" drawChar(%f,%f,%d)", x,y,c); + int render = state->getRender(); // check for invisible text -- this is used by Acrobat Capture - if ((state->getRender() & 3) == 3) + if (render == 3) { + msg(" Ignoring invisible text: char %d at %f,%f", c, x, y); 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 primarily as graphics */ + /* 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) - u = *_u; - msg(" drawChar(%f,%f,%f,%f,'%c',%d) CID=%d\n",x,y,dx,dy,c,u, font->isCIDFont()); + Unicode u=0; + char*name=0; - if(font->isCIDFont()) { - GfxCIDFont*cfont = (GfxCIDFont*)font; - char*name=0; - if(u) { + if(_u && uLen) { + u = *_u; + if (u) { int t; - for(t=0;t couldn't get name for CID character %02x from Encoding", c); - swfoutput_drawchar(&output, x1, y1, "", c); - } + if(font->isCIDFont()) { + GfxCIDFont*cfont = (GfxCIDFont*)font; + + if(font->getType() == fontCIDType2) + CIDToGIDMap = cfont->getCIDToGID(); } else { Gfx8BitFont*font8; font8 = (Gfx8BitFont*)font; char**enc=font8->getEncoding(); - if(enc && enc[c]) - swfoutput_drawchar(&output, x1, y1, enc[c], c); - else { - msg(" couldn't get name for character %02x from Encoding", c); - swfoutput_drawchar(&output, x1, y1, "", 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 = -1; + + if(uLen<=1) { + charid = getGfxCharID(current_gfxfont, c, name, u); + } else { + charid = getGfxCharID(current_gfxfont, c, 0, -1); + if(charid < 0) { + /* multiple unicodes- should usually map to a ligature. + if the ligature doesn't exist, we need to draw + the characters one-by-one. */ + int t; + msg(" ligature %d missing in font %s\n", c, current_font_id); + for(t=0;t 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); + m.tx += user_movex; + m.ty += user_movey; + + if(render == RENDER_FILL) { + output->drawchar(output, current_font_id, charid, &col, &m); + } else { + msg(" Drawing glyph %d as shape", charid); + if(!textmodeinfo) { + msg(" Some texts will be rendered as shape"); + textmodeinfo = 1; + } + 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) { - msg(" endstring\n"); + 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"); + + 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; + } + } } - -GBool SWFOutputDev::beginType3Char(GfxState *state, - CharCode code, Unicode *u, int uLen) +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"); + + if(current_text_clip) { + clipToGfxLine(state, current_text_clip); + gfxline_free(current_text_clip); + current_text_clip = 0; + } +} + +/* 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. + + 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; msg(" endType3Char"); } -void SWFOutputDev::startPage(int pageNum, GfxState *state) +void SWFOutputDev::startFrame(int width, int height) { - double x1,y1,x2,y2; - laststate = state; - msg(" startPage %d\n", pageNum); - msg(" processing page %d", pageNum); + output->startpage(output, width, height); +} - state->transform(state->getX1(),state->getY1(),&x1,&y1); - state->transform(state->getX2(),state->getY2(),&x2,&y2); - if(!outputstarted) { - msg(" Bounding box is (%f,%f)-(%f,%f)", x1,y1,x2,y2); - swfoutput_init(&output, swffilename, abs((int)(x2-x1)),abs((int)(y2-y1))); - outputstarted = 1; - } - else - swfoutput_newpage(&output); +void SWFOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2) +{ + this->currentpage = pageNum; + double x1,y1,x2,y2; + int rot = doc->getPageRotate(1); + gfxcolor_t white; + laststate = state; + gfxline_t clippath[5]; + + white.r = white.g = white.b = white.a = 255; + + /* state->transform(state->getX1(),state->getY1(),&x1,&y1); + state->transform(state->getX2(),state->getY2(),&x2,&y2); + Use CropBox, not MediaBox, as page size + */ + + /*x1 = crop_x1; + y1 = crop_y1; + x2 = crop_x2; + y2 = crop_y2;*/ + state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey; + state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey; + + 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; + } + + //msg(" Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey); + + if(outer_clip_box) { + output->endclip(output); + outer_clip_box = 0; + } + + msg(" 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, user_movey); + if(rot!=0) + msg(" page is rotated %d degrees\n", rot); + + clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1]; + clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2]; + clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3]; + clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4]; + clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0; + output->startclip(output, clippath); outer_clip_box = 1; + output->fill(output, clippath, &white); } void SWFOutputDev::drawLink(Link *link, Catalog *catalog) { - msg(" drawlink\n"); - double x1, y1, x2, y2, w; - GfxRGB rgb; - swfcoord points[5]; - int x, y; + msg(" drawlink\n"); + double x1, y1, x2, y2, w; + GfxRGB rgb; + gfxline_t points[5]; + int x, y; - link->getBorder(&x1, &y1, &x2, &y2, &w); -// if (w > 0) - { +#ifdef XPDF_101 + link->getBorder(&x1, &y1, &x2, &y2, &w); +#else + link->getRect(&x1, &y1, &x2, &y2); +#endif rgb.r = 0; rgb.g = 0; rgb.b = 1; cvtUserToDev(x1, y1, &x, &y); - points[0].x = points[4].x = (int)x; - points[0].y = points[4].y = (int)y; + points[0].type = gfx_moveTo; + points[0].x = points[4].x = x + user_movex; + points[0].y = points[4].y = y + user_movey; + points[0].next = &points[1]; cvtUserToDev(x2, y1, &x, &y); - points[1].x = (int)x; - points[1].y = (int)y; + points[1].type = gfx_lineTo; + points[1].x = x + user_movex; + points[1].y = y + user_movey; + points[1].next = &points[2]; cvtUserToDev(x2, y2, &x, &y); - points[2].x = (int)x; - points[2].y = (int)y; + points[2].type = gfx_lineTo; + points[2].x = x + user_movex; + points[2].y = y + user_movey; + points[2].next = &points[3]; cvtUserToDev(x1, y2, &x, &y); - points[3].x = (int)x; - points[3].y = (int)y; + points[3].type = gfx_lineTo; + points[3].x = x + user_movex; + points[3].y = y + user_movey; + points[3].next = &points[4]; + cvtUserToDev(x1, y1, &x, &y); + points[4].type = gfx_lineTo; + points[4].x = x + user_movex; + points[4].y = y + user_movey; + points[4].next = 0; LinkAction*action=link->getAction(); char buf[128]; - char*s = "-?-"; + char*s = 0; char*type = "-?-"; char*url = 0; char*named = 0; int page = -1; switch(action->getKind()) { - case actionGoTo: { - type = "GoTo"; - LinkGoTo *ha=(LinkGoTo *)link->getAction(); - LinkDest *dest=NULL; - if (ha->getDest()==NULL) - dest=catalog->findDest(ha->getNamedDest()); - else dest=ha->getDest(); - if (dest){ - if (dest->isPageRef()){ - Ref pageref=dest->getPageRef(); - page=catalog->findPage(pageref.num,pageref.gen); - } - else page=dest->getPageNum(); - sprintf(buf, "%d", page); - s = buf; - } - } + case actionGoTo: { + type = "GoTo"; + LinkGoTo *ha=(LinkGoTo *)link->getAction(); + LinkDest *dest=NULL; + if (ha->getDest()==NULL) + dest=catalog->findDest(ha->getNamedDest()); + else dest=ha->getDest(); + if (dest){ + if (dest->isPageRef()){ + Ref pageref=dest->getPageRef(); + page=catalog->findPage(pageref.num,pageref.gen); + } + else page=dest->getPageNum(); + sprintf(buf, "%d", page); + s = strdup(buf); + } + } break; - case actionGoToR: { - type = "GoToR"; - LinkGoToR*l = (LinkGoToR*)action; - GString*g = l->getNamedDest(); - if(g) - s = g->getCString(); - } + case actionGoToR: { + type = "GoToR"; + LinkGoToR*l = (LinkGoToR*)action; + GString*g = l->getNamedDest(); + if(g) + s = strdup(g->getCString()); + } break; - case actionNamed: { - type = "Named"; - LinkNamed*l = (LinkNamed*)action; - GString*name = l->getName(); - if(name) { - s = name->lowerCase()->getCString(); - named = name->getCString(); - if(!strchr(s,':')) - { - if(strstr(s, "next") || strstr(s, "forward")) - { - page = currentpage + 1; - } - else if(strstr(s, "prev") || strstr(s, "back")) - { - page = currentpage - 1; - } - else if(strstr(s, "last") || strstr(s, "end")) - { - page = pages[pagepos-1]; //:) - } - else if(strstr(s, "first") || strstr(s, "top")) - { - page = 1; - } - } - } - } + case actionNamed: { + type = "Named"; + LinkNamed*l = (LinkNamed*)action; + GString*name = l->getName(); + if(name) { + s = strdup(name->lowerCase()->getCString()); + named = name->getCString(); + if(!strchr(s,':')) + { + if(strstr(s, "next") || strstr(s, "forward")) + { + page = currentpage + 1; + } + else if(strstr(s, "prev") || strstr(s, "back")) + { + page = currentpage - 1; + } + else if(strstr(s, "last") || strstr(s, "end")) + { + if(pages && pagepos>0) + page = pages[pagepos-1]; + } + else if(strstr(s, "first") || strstr(s, "top")) + { + page = 1; + } + } + } + } break; - case actionLaunch: { - type = "Launch"; - LinkLaunch*l = (LinkLaunch*)action; - GString * str = new GString(l->getFileName()); - str->append(l->getParams()); - s = str->getCString(); - } + case actionLaunch: { + type = "Launch"; + LinkLaunch*l = (LinkLaunch*)action; + GString * str = new GString(l->getFileName()); + str->append(l->getParams()); + s = strdup(str->getCString()); + delete str; + } break; - case actionURI: { - type = "URI"; - LinkURI*l = (LinkURI*)action; - GString*g = l->getURI(); - if(g) { - url = g->getCString(); - s = url; - } - } + case actionURI: { + type = "URI"; + LinkURI*l = (LinkURI*)action; + GString*g = l->getURI(); + if(g) { + url = g->getCString(); + s = strdup(url); + } + } break; - case actionUnknown: { - type = "Unknown"; - LinkUnknown*l = (LinkUnknown*)action; - s = ""; - } + case actionUnknown: { + type = "Unknown"; + LinkUnknown*l = (LinkUnknown*)action; + s = strdup(""); + } break; - default: { - msg(" Unknown link type!\n"); - break; - } + default: { + msg(" Unknown link type!\n"); + break; + } } + if(!s) s = strdup("-?-"); + if(!linkinfo && (page || url)) { - msg(" File contains links"); - linkinfo = 1; + msg(" File contains links"); + linkinfo = 1; } + if(page>0) { - int t; - for(t=0;t=0) { + char buf[80]; + sprintf(buf, "page%d", t); + output->drawlink(output, points, buf); + } else { + msg(" Invalid link to page %d", page); + } } else if(url) { - swfoutput_linktourl(&output, url, points); - } - else if(named) - { - swfoutput_namedlink(&output, named, points); + output->drawlink(output, points, url); } + msg(" \"%s\" link to \"%s\" (%d)\n", type, FIXNULL(s), page); - } + free(s);s=0; } 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]) { - swfoutput_endclip(&output); - clipping[clippos]--; + while(states[statepos].clipping) { + output->endclip(output); + states[statepos].clipping--; } - clippos--; + statepos--; } -char type3Warning=0; - -int SWFOutputDev::searchT1Font(char*name) +char* SWFOutputDev::searchFont(char*name) { int i; - int mapid=-1; char*filename=0; + int is_standard_font = 0; - msg(" SearchT1Font(%s)", name); + msg(" SearchFont(%s)", name); + /* see if it is a pdf standard font */ for(i=0;i Loading extra font %s from %s\n", FIXNULL(fontname), - FIXNULL(T1_GetFontFileName(i))); - } + if(!fonts[i].used) { - if(fontname && !strcmp(name, fontname)) { - msg(" Extra font %d, \"%s\" is being used.\n", i, fontname); - return i; - } - fontname = T1_GetFontFileName(i); - if(strrchr(fontname,'/')) - fontname = strrchr(fontname,'/')+1; - - if(strstr(fontname, name)) { - msg(" Extra font %d, \"%s\" is being used.\n", i, fontname); - return i; + fonts[i].used = 1; + if(!is_standard_font) + msg(" Using %s for %s", fonts[i].filename, name); } + return strdup(fonts[i].filename); } } - return -1; + return 0; } void SWFOutputDev::updateLineWidth(GfxState *state) { double width = state->getTransformedLineWidth(); - swfoutput_setlinewidth(&output, width); + //swfoutput_setlinewidth(&output, width); } void SWFOutputDev::updateLineCap(GfxState *state) @@ -962,8 +1680,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) @@ -971,248 +1688,262 @@ 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) +{ + fwrite(data, len, 1, (FILE*)stream); } char*SWFOutputDev::writeEmbeddedFontToFile(XRef*ref, GfxFont*font) { - char*tmpFileName = NULL; - FILE *f; - int c; - char *fontBuf; - int fontLen; - Ref embRef; - Object refObj, strObj; - char namebuf[512]; - tmpFileName = mktmpname(namebuf); - int ret; - - ret = font->getEmbeddedFontID(&embRef); - if(!ret) { - msg(" Didn't get embedded font id"); - /* not embedded- the caller should now search the font - directories for this font */ - return 0; - } + char*tmpFileName = NULL; + FILE *f; + int c; + char *fontBuf; + int fontLen; + Ref embRef; + Object refObj, strObj; + char namebuf[512]; + tmpFileName = mktmpname(namebuf); + int ret; + + ret = font->getEmbeddedFontID(&embRef); + if(!ret) { + msg(" Didn't get embedded font id"); + /* not embedded- the caller should now search the font + directories for this font */ + return 0; + } - f = fopen(tmpFileName, "wb"); - if (!f) { - msg(" Couldn't create temporary Type 1 font file"); - return 0; - } - if (font->getType() == fontType1C || - font->getType() == fontCIDType0C) { - if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) { - fclose(f); - msg(" Couldn't read embedded font file"); - return 0; - } - Type1CFontFile *cvt = new Type1CFontFile(fontBuf, fontLen); - cvt->convertToType1(f); - delete cvt; - gfree(fontBuf); - } else if(font->getType() == fontTrueType) { - msg(" writing font using TrueTypeFontFile::writeTTF"); - if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) { - fclose(f); - msg(" Couldn't read embedded font file"); - return 0; - } - TrueTypeFontFile *cvt = new TrueTypeFontFile(fontBuf, fontLen); - cvt->writeTTF(f); - delete cvt; - gfree(fontBuf); - } else { - font->getEmbeddedFontID(&embRef); - refObj.initRef(embRef.num, embRef.gen); - refObj.fetch(ref, &strObj); - refObj.free(); - strObj.streamReset(); - int f4[4]; - char f4c[4]; - int t; - for(t=0;t<4;t++) { - f4[t] = strObj.streamGetChar(); - f4c[t] = (char)f4[t]; - if(f4[t] == EOF) - break; - } - if(t==4) { - if(!strncmp(f4c, "true", 4)) { - /* some weird TTF fonts don't start with 0,1,0,0 but with "true". - Change this on the fly */ - f4[0] = f4[2] = f4[3] = 0; - f4[1] = 1; - } - fputc(f4[0], f); - fputc(f4[1], f); - fputc(f4[2], f); - fputc(f4[3], f); + f = fopen(tmpFileName, "wb"); + if (!f) { + msg(" Couldn't create temporary Type 1 font file"); + return 0; + } - while ((c = strObj.streamGetChar()) != EOF) { - fputc(c, f); - } - } - strObj.streamClose(); - strObj.free(); + /*if(font->isCIDFont()) { + GfxCIDFont* cidFont = (GfxCIDFont *)font; + GString c = cidFont->getCollection(); + msg(" Collection: %s", c.getCString()); + }*/ + + //if (font->getType() == fontType1C) { + if (0) { //font->getType() == fontType1C) { + if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) { + fclose(f); + msg(" Couldn't read embedded font file"); + return 0; } - fclose(f); - - if(font->getType() == fontTrueType || - font->getType() == fontCIDType2) - { - if(!ttfinfo) { - msg(" File contains TrueType fonts"); - ttfinfo = 1; - } - char name2[512]; - char*tmp; - tmp = strdup(mktmpname((char*)name2)); - sprintf(name2, "%s", tmp); - char*a[] = {"./ttf2pt1", "-W", "0", -#ifndef USE_FREETYPE - "-p", "ttf", +#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); + //cvt->convertToType0("test", f); + delete cvt; + gfree(fontBuf); + } else if(font->getType() == fontTrueType) { + msg(" writing font using TrueTypeFontFile::writeTTF"); + if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) { + fclose(f); + msg(" Couldn't read embedded font file"); + return 0; + } +#ifdef XPDF_101 + TrueTypeFontFile *cvt = new TrueTypeFontFile(fontBuf, fontLen); + cvt->writeTTF(f); #else - "-p", "ft", + FoFiTrueType *cvt = FoFiTrueType::make(fontBuf, fontLen); + cvt->writeTTF(FoFiWrite, f); #endif - "-b", tmpFileName, name2}; - msg(" Invoking %s %s %s %s %s %s %s %s",a[0],a[1],a[2],a[3],a[4],a[5],a[6],a[7]); - ttf2pt1_main(8,a); - unlink(tmpFileName); - sprintf(name2,"%s.pfb",tmp); - tmpFileName = strdup(name2); + delete cvt; + gfree(fontBuf); + } else { + font->getEmbeddedFontID(&embRef); + refObj.initRef(embRef.num, embRef.gen); + refObj.fetch(ref, &strObj); + refObj.free(); + strObj.streamReset(); + int f4[4]; + char f4c[4]; + int t; + for(t=0;t<4;t++) { + f4[t] = strObj.streamGetChar(); + f4c[t] = (char)f4[t]; + if(f4[t] == EOF) + break; } + if(t==4) { + if(!strncmp(f4c, "true", 4)) { + /* some weird TTF fonts don't start with 0,1,0,0 but with "true". + Change this on the fly */ + f4[0] = f4[2] = f4[3] = 0; + f4[1] = 1; + } + fputc(f4[0], f); + fputc(f4[1], f); + fputc(f4[2], f); + fputc(f4[3], f); + + while ((c = strObj.streamGetChar()) != EOF) { + fputc(c, f); + } + } + strObj.streamClose(); + strObj.free(); + } + fclose(f); return strdup(tmpFileName); } - -char* gfxFontName(GfxFont* gfxFont) + +char* searchForSuitableFont(GfxFont*gfxFont) { - GString *gstr; - gstr = gfxFont->getName(); - if(gstr) { - return gstr->getCString(); - } - else { - char buf[32]; - Ref*r=gfxFont->getID(); - sprintf(buf, "UFONT%d", r->num); - return strdup(buf); - } -} + char*name = getFontName(gfxFont); + char*fontname = 0; + char*filename = 0; -char* substitutetarget[256]; -char* substitutesource[256]; -int substitutepos = 0; + if(!config_use_fontconfig) + return 0; + +#ifdef HAVE_FONTCONFIG + FcPattern *pattern, *match; + FcResult result; + FcChar8 *v; + + static int fcinitcalled = false; + + msg(" searchForSuitableFont(%s)", name); + + // call init ony once + if (!fcinitcalled) { + msg(" Initializing FontConfig..."); + fcinitcalled = true; + 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); // 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); + FcPatternDestroy(match); + + // 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); // mem leak + char *nfn = strrchr(filename, '/'); + if(nfn) fontname = strdup(nfn+1); + else fontname = filename; + } + msg(" FontConfig: Returning \"%s\"", fontname); + } + } + + //printf("FONTCONFIG: pattern"); + //FcPatternPrint(pattern); + //printf("FONTCONFIG: match"); + //FcPatternPrint(match); + + FcPatternDestroy(pattern); + FcPatternDestroy(match); + + pdfswf_addfont(filename); + return fontname; +#else + return 0; +#endif +} char* SWFOutputDev::substituteFont(GfxFont*gfxFont, char* oldname) { -/* ------------------------------ V1 */ + char*fontname = 0, *filename = 0; + msg(" subsituteFont(%s)", oldname); + + if(!(fontname = searchForSuitableFont(gfxFont))) { + fontname = "Times-Roman"; + } + filename = searchFont(fontname); + if(!filename) { + msg(" Couldn't find font %s- did you install the default fonts?", fontname); + return 0; + } - char*fontname = "Times-Roman"; - msg(" substituteFont(,%s)", FIXNULL(oldname)); - this->t1id = searchT1Font(fontname); 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)); + msg(" substituting %s -> %s", FIXNULL(oldname), FIXNULL(fontname)); substitutepos ++; } - return fontname; - -/* ------------------------------ V2 */ - -/* //substitute font - char* fontname = 0; - double m11, m12, m21, m22; - int index; - int code; - double w,w1,w2; - double*fm; - double v; - if(gfxFont->getName()) { - fontname = gfxFont->getName()->getCString(); - } - -// printf("%d %s\n", t, gfxFont->getCharName(t)); - showFontError(gfxFont, 1); - if(1) { //if (!gfxFont->isCIDFont()) { FIXME: xpdf 1.01 does not have is16Bit() - if(gfxFont->isSymbolic()) { - if(fontname && (strstr(fontname,"ing"))) //Dingbats, Wingdings etc. - index = 16; - else - index = 12; - } else if (gfxFont->isFixedWidth()) { - index = 8; - } else if (gfxFont->isSerif()) { - index = 4; - } else { - index = 0; - } - if (gfxFont->isBold() && index!=16) - index += 2; - if (gfxFont->isItalic() && index!=16) - index += 1; - fontname = fontnames[index]; - // get width of 'm' in real font and substituted font - if ((code = gfxFont->getCharCode("m")) >= 0) - w1 = gfxFont->getWidth(code); - else - w1 = 0; - w2 = fontsizes[index]; - if (gfxFont->getType() == fontType3) { - // This is a hack which makes it possible to substitute for some - // Type 3 fonts. The problem is that it's impossible to know what - // the base coordinate system used in the font is without actually - // rendering the font. This code tries to guess by looking at the - // width of the character 'm' (which breaks if the font is a - // subset that doesn't contain 'm'). - if (w1 > 0 && (w1 > 1.1 * w2 || w1 < 0.9 * w2)) { - w1 /= w2; - m11 *= w1; - m12 *= w1; - m21 *= w1; - m22 *= w1; - } - fm = gfxFont->getFontMatrix(); - v = (fm[0] == 0) ? 1 : (fm[3] / fm[0]); - m21 *= v; - m22 *= v; - } else if (!gfxFont->isSymbolic()) { - // if real font is substantially narrower than substituted - // font, reduce the font size accordingly - if (w1 > 0.01 && w1 < 0.9 * w2) { - w1 /= w2; - if (w1 < 0.8) { - w1 = 0.8; - } - m11 *= w1; - m12 *= w1; - m21 *= w1; - m22 *= w1; - } - } - } - if(fontname) { - this->t1id = searchT1Font(fontname); - } - if(substitutepos>=sizeof(substitutesource)/sizeof(char*)) { - msg(" Too many fonts in file."); - exit(1); - } - if(oldname) { - substitutesource[substitutepos] = oldname; - substitutetarget[substitutepos] = fontname; - msg(" substituting %s -> %s", FIXNULL(oldname), FIXNULL(fontname)); - substitutepos ++; - } - return fontname;*/ + return strdup(filename); //mem leak } void unlinkfont(char* filename) @@ -1244,129 +1975,158 @@ void unlinkfont(char* filename) } } -void SWFOutputDev::startDoc(XRef *xref) +void SWFOutputDev::setXRef(PDFDoc*doc, XRef *xref) { - this->xref = xref; + this->doc = doc; + this->xref = xref; } +int SWFOutputDev::setGfxFont(char*id, char*filename, double maxSize) +{ + 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; + output->addfont(output, id, current_gfxfont); + return 1; + } + l = l->next; + } + if(!filename) return 0; + + /* A font size of e.g. 9 means the font will be scaled down by + 1024 and scaled up by 9. So to have a maximum error of 1/20px, + we have to divide 0.05 by (fontsize/1024) + */ + double quality = (1024 * 0.05) / maxSize; + + font = gfxfont_load(filename, quality); + 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; + } + output->addfont(output, id, current_gfxfont); + return 1; +} void SWFOutputDev::updateFont(GfxState *state) { - GfxFont*gfxFont = state->getFont(); - char * fileName = 0; + GfxFont*gfxFont = state->getFont(); + + if (!gfxFont) { + return; + } + char * fontid = getFontID(gfxFont); + double maxSize = 1.0; + + if(this->info) { + maxSize = this->info->getMaximumFontSize(fontid); + } - if (!gfxFont) { - return; - } - char * fontname = gfxFontName(gfxFont); - - int t; - /* first, look if we substituted this font before- - this way, we don't initialize the T1 Fonts - too often */ - for(t=0;t updateFont(%s) [cached]", fontid); + return; + }*/ - if(swfoutput_queryfont(&output, fontname)) - { - swfoutput_setfont(&output, fontname, -1, 0); - return; - } + // look for Type 3 font + if (gfxFont->getType() == fontType3) { + if(!type3Warning) { + type3Warning = gTrue; + showFontError(gfxFont, 2); + } + free(fontid); + return; + } - // look for Type 3 font - if (!type3Warning && gfxFont->getType() == fontType3) { - type3Warning = gTrue; - showFontError(gfxFont, 2); - } + /* now either load the font, or find a substitution */ - /* now either load the font, or find a substitution */ + Ref embRef; + GBool embedded = gfxFont->getEmbeddedFontID(&embRef); - Ref embRef; - GBool embedded = gfxFont->getEmbeddedFontID(&embRef); - if(embedded) { - if (gfxFont->getType() == fontType1 || - gfxFont->getType() == fontCIDType0C || + char*fileName = 0; + int del = 0; + if(embedded && + (gfxFont->getType() == fontType1 || gfxFont->getType() == fontType1C || + (gfxFont->getType() == fontCIDType0C && forceType0Fonts) || gfxFont->getType() == fontTrueType || gfxFont->getType() == fontCIDType2 - ) + )) { - fileName = writeEmbeddedFontToFile(xref, gfxFont); - if(!fileName) { - msg(" Couldn't write font to file"); - showFontError(gfxFont,0); - return ; - } - this->t1id = T1_AddFont(fileName); - if(this->t1id<0) { - msg(" Couldn't load font from file %s", fileName); - showFontError(gfxFont,0); - unlinkfont(fileName); - return ; - } - } - else { - /* in case the font is embedded, but has an - unsupported format, we just look through the - font directories */ - int newt1id = searchT1Font(fontname); - if(newt1id<0) { - msg(" Couldn't find any suitable replacement for %s",fontname); - showFontError(gfxFont,0); - fontname = substituteFont(gfxFont, fontname); - } else - this->t1id = newt1id; + fileName = writeEmbeddedFontToFile(xref, gfxFont); + if(!fileName) showFontError(gfxFont,0); + else del = 1; + } else { + char * fontname = getFontName(gfxFont); + fileName = searchFont(fontname); + if(!fileName) showFontError(gfxFont,0); + free(fontname); } - } else { - if(fontname) { - int newt1id = searchT1Font(fontname); - if(newt1id<0) { - showFontError(gfxFont,1); - fontname = substituteFont(gfxFont, fontname); - } else - this->t1id = newt1id; - } - else { - showFontError(gfxFont,1); - fontname = substituteFont(gfxFont, fontname); + if(!fileName) { + char * fontname = getFontName(gfxFont); + 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) { free(fontid);fontid = strdup(substitutetarget[substitutepos-1]); /*ugly hack*/}; + msg(" Font is now %s (%s)", fontid, fileName); } - } - if(t1id<0) { - msg(" Current font's t1id is %d", t1id); - //showFontError(gfxFont,0); - return; - } - - /* we may have done some substitutions here, so check - again if this font is cached. */ - if(swfoutput_queryfont(&output, fontname)) - { - swfoutput_setfont(&output, fontname, -1, 0); - return; - } + if(!fileName) { + msg(" Couldn't set font %s\n", fontid); + free(fontid); + return; + } + + msg(" updateFont(%s) -> %s (max size: %f)", fontid, fileName, maxSize); + dumpFontInfo("", gfxFont); - msg(" Creating new SWF font: t1id: %d, filename: %s name:%s", this->t1id, FIXNULL(fileName), FIXNULL(fontname)); - swfoutput_setfont(&output, fontname, this->t1id, fileName); - if(fileName) - unlinkfont(fileName); + //swfoutput_setfont(&output, fontid, fileName); + + if(!setGfxFont(fontid, 0, 0)) { + setGfxFont(fontid, fileName, maxSize); + } + + if(fileName && del) + unlinkfont(fileName); + if(fileName) + free(fileName); + free(fontid); } -int pic_xids[1024]; -int pic_yids[1024]; -int pic_ids[1024]; -int pic_width[1024]; -int pic_height[1024]; -int picpos = 0; -int pic_id = 0; - #define SQR(x) ((x)*(x)) unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize) @@ -1417,9 +2177,76 @@ unsigned char* antialize(unsigned char*data, int width, int height, int newwidth return newdata; } +#define IMAGE_TYPE_JPEG 0 +#define IMAGE_TYPE_LOSSLESS 1 + +static void drawimage(gfxdevice_t*dev, RGBA* data, int sizex,int sizey, + double x1,double y1, + double x2,double y2, + double x3,double y3, + double x4,double y4, int type) +{ + RGBA*newpic=0; + + double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1)); + double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1)); + + gfxline_t p1,p2,p3,p4,p5; + p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2; + p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3; + p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4; + p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5; + p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0; + + {p1.x = (int)(p1.x*20)/20.0; + p1.y = (int)(p1.y*20)/20.0; + p2.x = (int)(p2.x*20)/20.0; + p2.y = (int)(p2.y*20)/20.0; + p3.x = (int)(p3.x*20)/20.0; + p3.y = (int)(p3.y*20)/20.0; + p4.x = (int)(p4.x*20)/20.0; + p4.y = (int)(p4.y*20)/20.0; + p5.x = (int)(p5.x*20)/20.0; + p5.y = (int)(p5.y*20)/20.0; + } + + float m00,m10,tx; + float m01,m11,ty; + + gfxmatrix_t m; + m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey; + m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey; + m.tx = p1.x - 0.5; + m.ty = p1.y - 0.5; + + gfximage_t img; + img.data = (gfxcolor_t*)data; + img.width = sizex; + img.height = sizey; + + if(type == IMAGE_TYPE_JPEG) + /* TODO: pass image_dpi to device instead */ + dev->setparameter(dev, "next_bitmap_is_jpeg", "1"); + + dev->fillbitmap(dev, &p1, &img, &m, 0); +} + +void drawimagejpeg(gfxdevice_t*dev, RGBA*mem, int sizex,int sizey, + double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4) +{ + drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG); +} + +void drawimagelossless(gfxdevice_t*dev, RGBA*mem, int sizex,int sizey, + double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4) +{ + drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS); +} + + 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; @@ -1451,10 +2278,10 @@ void SWFOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str, return; } - state->transform(0, 1, &x1, &y1); - state->transform(0, 0, &x2, &y2); - state->transform(1, 0, &x3, &y3); - state->transform(1, 1, &x4, &y4); + state->transform(0, 1, &x1, &y1); x1 += user_movex; y1+= user_movey; + state->transform(0, 0, &x2, &y2); x2 += user_movex; y2+= user_movey; + state->transform(1, 0, &x3, &y3); x3 += user_movex; y3+= user_movey; + state->transform(1, 1, &x4, &y4); x4 += user_movex; y4+= user_movey; if(!pbminfo && !(str->getKind()==strDCT)) { if(!type3active) { @@ -1470,22 +2297,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) @@ -1495,173 +2321,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) + drawimagejpeg(output, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4); + else + 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", @@ -1687,10 +2465,10 @@ 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; +//SWFOutputDev*output = 0; static void printInfoString(Dict *infoDict, char *key, char *fmt) { Object obj; @@ -1734,204 +2512,443 @@ static void printInfoDate(Dict *infoDict, char *key, char *fmt) { obj.free(); } -void pdfswf_init(char*filename, char*userPassword) -{ - GString *fileName = new GString(filename); - GString *userPW; - Object info; +int jpeg_dpi = 0; +int ppm_dpi = 0; - // read config file - globalParams = new GlobalParams(""); - - // open PDF file - if (userPassword && userPassword[0]) { - userPW = new GString(userPassword); - } else { - userPW = NULL; - } - doc = new PDFDoc(fileName, userPW); - if (userPW) { - delete userPW; - } - if (!doc->isOk()) { - exit(1); - } - - // print doc info - doc->getDocInfo(&info); - if (info.isDict() && - (screenloglevel>=LOGLEVEL_NOTICE)) { - printInfoString(info.getDict(), "Title", "Title: %s\n"); - printInfoString(info.getDict(), "Subject", "Subject: %s\n"); - printInfoString(info.getDict(), "Keywords", "Keywords: %s\n"); - printInfoString(info.getDict(), "Author", "Author: %s\n"); - printInfoString(info.getDict(), "Creator", "Creator: %s\n"); - printInfoString(info.getDict(), "Producer", "Producer: %s\n"); - printInfoDate(info.getDict(), "CreationDate", "CreationDate: %s\n"); - printInfoDate(info.getDict(), "ModDate", "ModDate: %s\n"); - printf("Pages: %d\n", doc->getNumPages()); - printf("Linearized: %s\n", doc->isLinearized() ? "yes" : "no"); - printf("Encrypted: "); - if (doc->isEncrypted()) { - printf("yes (print:%s copy:%s change:%s addNotes:%s)\n", - doc->okToPrint() ? "yes" : "no", - doc->okToCopy() ? "yes" : "no", - doc->okToChange() ? "yes" : "no", - doc->okToAddNotes() ? "yes" : "no"); +void storeDeviceParameter(char*name, char*value) +{ + parameter_t*p = new parameter_t(); + p->name = strdup(name); + p->value = strdup(value); + p->next = 0; + if(device_config_next) { + device_config_next->next = p; + device_config_next = p; } else { - printf("no\n"); + device_config = p; + device_config_next = p; } - } - info.free(); - - numpages = doc->getNumPages(); - if (doc->isEncrypted()) { - /*ERROR: This pdf is encrypted, and disallows copying. - Due to the DMCA, paragraph 1201, (2) A-C, circumventing - a technological measure that efficively controls access to - a protected work is violating American law. - See www.eff.org for more information about DMCA issues. - */ - if(!doc->okToCopy()) { - printf("PDF disallows copying. Bailing out.\n"); - exit(1); //bail out - } - if(!doc->okToChange() || !doc->okToAddNotes()) - swfoutput_setprotected(); - } - - output = new SWFOutputDev(); - output->startDoc(doc->getXRef()); } void pdfswf_setparameter(char*name, char*value) { - if(!strcmp(name, "drawonlyshapes")) { - drawonlyshapes = atoi(value); - } else if(!strcmp(name, "ignoredraworder")) { - ignoredraworder = atoi(value); - } else if(!strcmp(name, "linksopennewwindow")) { - opennewwindow = atoi(value); - } else if(!strcmp(name, "storeallcharacters")) { - storeallcharacters = atoi(value); - } else if(!strcmp(name, "enablezlib")) { - enablezlib = atoi(value); - } else if(!strcmp(name, "insertstop")) { - insertstoptag = atoi(value); - } else if(!strcmp(name, "flashversion")) { - flashversion = atoi(value); - } else if(!strcmp(name, "jpegquality")) { - int val = atoi(value); - if(val<0) val=0; - if(val>100) val=100; - jpegquality = val; - } else if(!strcmp(name, "outputfilename")) { - swffilename = value; - } else if(!strcmp(name, "caplinewidth")) { + msg(" setting parameter %s to \"%s\"", name, value); + if(!strcmp(name, "caplinewidth")) { caplinewidth = atof(value); - } else if(!strcmp(name, "splinequality")) { - int v = atoi(value); - v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel - if(v<1) v = 1; - splinemaxerror = v; - } else if(!strcmp(name, "fontquality")) { - int v = atoi(value); - v = 500-(v*5); // 100% = 0.25 pixel, 0% = 25 pixel - if(v<1) v = 1; - fontsplinemaxerror = v; + } else if(!strcmp(name, "zoom")) { + char buf[80]; + zoom = atof(value); + sprintf(buf, "%f", (double)jpeg_dpi/(double)zoom); + storeDeviceParameter("jpegsubpixels", buf); + sprintf(buf, "%f", (double)ppm_dpi/(double)zoom); + storeDeviceParameter("ppmsubpixels", buf); + } else if(!strcmp(name, "jpegdpi")) { + char buf[80]; + jpeg_dpi = atoi(value); + sprintf(buf, "%f", (double)jpeg_dpi/(double)zoom); + storeDeviceParameter("jpegsubpixels", buf); + } else if(!strcmp(name, "ppmdpi")) { + char buf[80]; + ppm_dpi = atoi(value); + sprintf(buf, "%f", (double)ppm_dpi/(double)zoom); + storeDeviceParameter("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 { - fprintf(stderr, "unknown parameter: %s (=%s)\n", name, value); + storeDeviceParameter(name,value); + } +} +void pdfswf_addfont(char*filename) +{ + fontfile_t f; + memset(&f, 0, sizeof(fontfile_t)); + f.filename = filename; + if(fontnum < sizeof(fonts)/sizeof(fonts[0])) { + fonts[fontnum++] = f; + } else { + msg(" Too many external fonts. Not adding font file \"%s\".", filename); } } -void pdfswf_drawonlyshapes() +static char* dirseparator() { - drawonlyshapes = 1; +#ifdef WIN32 + return "\\"; +#else + return "/"; +#endif } -void pdfswf_ignoredraworder() +void pdfswf_addlanguagedir(char*dir) { - ignoredraworder = 1; + 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_linksopennewwindow() +void pdfswf_addfontdir(char*dirname) { - opennewwindow = 1; +#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 } -void pdfswf_storeallcharacters() + +typedef struct _pdf_doc_internal +{ + int protect; + PDFDoc*doc; + InfoOutputDev*info; +} pdf_doc_internal_t; +typedef struct _pdf_page_internal +{ +} pdf_page_internal_t; +typedef struct _swf_output_internal +{ + SWFOutputDev*outputDev; +} swf_output_internal_t; + +pdf_doc_t* pdf_init(char*filename, char*userPassword) +{ + pdf_doc_t*pdf_doc = (pdf_doc_t*)malloc(sizeof(pdf_doc_t)); + memset(pdf_doc, 0, sizeof(pdf_doc_t)); + pdf_doc_internal_t*i= (pdf_doc_internal_t*)malloc(sizeof(pdf_doc_internal_t)); + memset(i, 0, sizeof(pdf_doc_internal_t)); + pdf_doc->internal = i; + + GString *fileName = new GString(filename); + GString *userPW; + Object info; + + // read config file + if(!globalParams) + globalParams = new GlobalParams(""); + + // open PDF file + if (userPassword && userPassword[0]) { + userPW = new GString(userPassword); + } else { + userPW = NULL; + } + i->doc = new PDFDoc(fileName, userPW); + if (userPW) { + delete userPW; + } + if (!i->doc->isOk()) { + return 0; + } + + // print doc info + i->doc->getDocInfo(&info); + if (info.isDict() && + (getScreenLogLevel()>=LOGLEVEL_NOTICE)) { + printInfoString(info.getDict(), "Title", "Title: %s\n"); + printInfoString(info.getDict(), "Subject", "Subject: %s\n"); + printInfoString(info.getDict(), "Keywords", "Keywords: %s\n"); + printInfoString(info.getDict(), "Author", "Author: %s\n"); + printInfoString(info.getDict(), "Creator", "Creator: %s\n"); + printInfoString(info.getDict(), "Producer", "Producer: %s\n"); + printInfoDate(info.getDict(), "CreationDate", "CreationDate: %s\n"); + printInfoDate(info.getDict(), "ModDate", "ModDate: %s\n"); + printf("Pages: %d\n", i->doc->getNumPages()); + printf("Linearized: %s\n", i->doc->isLinearized() ? "yes" : "no"); + printf("Encrypted: "); + if (i->doc->isEncrypted()) { + printf("yes (print:%s copy:%s change:%s addNotes:%s)\n", + i->doc->okToPrint() ? "yes" : "no", + i->doc->okToCopy() ? "yes" : "no", + i->doc->okToChange() ? "yes" : "no", + i->doc->okToAddNotes() ? "yes" : "no"); + } else { + printf("no\n"); + } + } + info.free(); + + pdf_doc->num_pages = i->doc->getNumPages(); + i->protect = 0; + if (i->doc->isEncrypted()) { + if(!i->doc->okToCopy()) { + printf("PDF disallows copying.\n"); + return 0; + } + if(!i->doc->okToChange() || !i->doc->okToAddNotes()) + i->protect = 1; + } + + InfoOutputDev*io = new InfoOutputDev(); + int t; + for(t=1;t<=pdf_doc->num_pages;t++) { +#ifdef XPDF_101 + i->doc->displayPage((OutputDev*)io, t, /*zoom*/zoom, /*rotate*/0, /*doLinks*/(int)1); +#else + i->doc->displayPage((OutputDev*)io, t, zoom, zoom, /*rotate*/0, true, /*doLinks*/(int)1); +#endif + } + i->info = io; + + return pdf_doc; +} + +class MemCheck +{ + public: ~MemCheck() + { + delete globalParams;globalParams=0; + Object::memCheck(stderr); + gMemReport(stderr); + } +} myMemCheck; + +void pdf_destroy(pdf_doc_t*pdf_doc) { - storeallcharacters = 1; + pdf_doc_internal_t*i= (pdf_doc_internal_t*)pdf_doc->internal; + + delete i->doc; i->doc=0; + + if(i->info) { + delete i->info;i->info=0; + } + + free(pdf_doc->internal);pdf_doc->internal=0; + free(pdf_doc);pdf_doc=0; } -void pdfswf_enablezlib() +pdf_page_t* pdf_getpage(pdf_doc_t*pdf_doc, int page) { - enablezlib = 1; + 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)); + memset(pi, 0, sizeof(pdf_page_internal_t)); + pdf_page->internal = pi; + + pdf_page->parent = pdf_doc; + pdf_page->nr = page; + return pdf_page; } -void pdfswf_jpegquality(int val) +void pdf_page_destroy(pdf_page_t*pdf_page) { - if(val<0) val=0; - if(val>100) val=100; - jpegquality = val; + pdf_page_internal_t*i= (pdf_page_internal_t*)pdf_page->internal; + free(pdf_page->internal);pdf_page->internal = 0; + free(pdf_page);pdf_page=0; } -void pdfswf_setoutputfilename(char*_filename) +swf_output_t* swf_output_init() { - swffilename = _filename; + swf_output_t*swf_output = (swf_output_t*)malloc(sizeof(swf_output_t)); + memset(swf_output, 0, sizeof(swf_output_t)); + swf_output_internal_t*i= (swf_output_internal_t*)malloc(sizeof(swf_output_internal_t)); + memset(i, 0, sizeof(swf_output_internal_t)); + swf_output->internal = i; + + i->outputDev = new SWFOutputDev(); + return swf_output; } -void pdfswf_insertstop() +void swf_output_setparameter(swf_output_t*swf, char*name, char*value) { - insertstoptag = 1; + pdfswf_setparameter(name, value); } -void pdfswf_setversion(int n) +void swf_output_startframe(swf_output_t*swf, int width, int height) { - flashversion = n; + swf_output_internal_t*i= (swf_output_internal_t*)swf->internal; + i->outputDev->startFrame(width, height); } +void swf_output_endframe(swf_output_t*swf) +{ + swf_output_internal_t*i= (swf_output_internal_t*)swf->internal; + i->outputDev->endframe(); +} -void pdfswf_convertpage(int page) +void swf_output_preparepage(swf_output_t*swf, int pdfpage, int outputpage) { - if(!pages) - { - pages = (int*)malloc(1024*sizeof(int)); - pagebuflen = 1024; + swf_output_internal_t*i= (swf_output_internal_t*)swf->internal; + SWFOutputDev*o = i->outputDev; + + if(pdfpage < 0) + return; + + if(!o->pages) { + o->pagebuflen = 1024; + o->pages = (int*)malloc(o->pagebuflen*sizeof(int)); + memset(o->pages, -1, o->pagebuflen*sizeof(int)); } else { - if(pagepos == pagebuflen) + while(pdfpage >= o->pagebuflen) { - pagebuflen+=1024; - pages = (int*)realloc(pages, pagebuflen); + int oldlen = o->pagebuflen; + o->pagebuflen+=1024; + o->pages = (int*)realloc(o->pages, o->pagebuflen*sizeof(int)); + memset(&o->pages[oldlen], -1, (o->pagebuflen-oldlen)*sizeof(int)); } } - pages[pagepos++] = page; + o->pages[pdfpage] = outputpage; + if(pdfpage>o->pagepos) + o->pagepos = pdfpage; } -void pdfswf_performconversion() +int swf_output_save(swf_output_t*swf, char*filename) { - int t; - for(t=0;tdisplayPage((OutputDev*)output, currentpage, /*dpi*/72, /*rotate*/0, /*doLinks*/(int)1); + swf_output_internal_t*i= (swf_output_internal_t*)swf->internal; + int ret = i->outputDev->save(filename); + 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(); + return ret; +} + +void swf_output_destroy(swf_output_t*output) +{ + swf_output_internal_t*i = (swf_output_internal_t*)output->internal; + delete i->outputDev; i->outputDev=0; + free(output->internal);output->internal=0; + free(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*)swf->internal; + + if(!pi) { + msg(" pdf_page_render: Parent PDF this page belongs to doesn't exist yet/anymore"); + return; + } + + if(pi->protect) { + gfxdevice_t*dev = si->outputDev->output; + dev->setparameter(dev, "protect", "1"); } + si->outputDev->setInfo(pi->info); + si->outputDev->setXRef(pi->doc, pi->doc->getXRef()); +#ifdef XPDF_101 + pi->doc->displayPage((OutputDev*)si->outputDev, page->nr, /*zoom*/zoom, /*rotate*/0, /*doLinks*/(int)1); +#else + pi->doc->displayPage((OutputDev*)si->outputDev, page->nr, zoom, zoom, /*rotate*/0, true, /*doLinks*/(int)1); +#endif } -int pdfswf_numpages() +void pdf_page_rendersection(pdf_page_t*page, swf_output_t*output, int x, int y, int x1, int y1, int x2, int y2) { - return doc->getNumPages(); + 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); } -int closed=0; -void pdfswf_close() +void pdf_page_render(pdf_page_t*page, swf_output_t*output) { - msg(" pdfswf.cc: pdfswf_close()"); + 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; - delete doc; - //freeParams(); - // check for memory leaks - Object::memCheck(stderr); - gMemReport(stderr); + + return info; } +void pdf_page_info_destroy(pdf_page_info_t*info) +{ + free(info); +}