//======================================================================== // // HtmlOutputDev.cc // // Copyright 1997-2002 Glyph & Cog, LLC // // Changed 1999-2000 by G.Ovtcharov // // Changed 2002 by Mikhail Kruk // //======================================================================== #ifdef __GNUC__ #pragma implementation #endif #include "config.h" #include #include #include #include #include #include #include "goo/GooString.h" #include "goo/GooList.h" #include "UnicodeMap.h" #include "goo/gmem.h" #include "Error.h" #include "GfxState.h" #include "Page.h" #ifdef ENABLE_LIBJPEG #include "DCTStream.h" #endif #include "GlobalParams.h" #include "HtmlOutputDev.h" #include "HtmlFonts.h" int HtmlPage::pgNum=0; int HtmlOutputDev::imgNum=1; extern double scale; extern GBool complexMode; extern GBool ignore; extern GBool printCommands; extern GBool printHtml; extern GBool noframes; extern GBool stout; extern GBool xml; extern GBool showHidden; extern GBool noMerge; static GooString* basename(GooString* str){ char *p=str->getCString(); int len=str->getLength(); for (int i=len-1;i>=0;i--) if (*(p+i)==SLASH) return new GooString((p+i+1),len-i-1); return new GooString(str); } static GooString* Dirname(GooString* str){ char *p=str->getCString(); int len=str->getLength(); for (int i=len-1;i>=0;i--) if (*(p+i)==SLASH) return new GooString(p,i+1); return new GooString(); } //------------------------------------------------------------------------ // HtmlString //------------------------------------------------------------------------ HtmlString::HtmlString(GfxState *state, double fontSize, HtmlFontAccu* fonts) { GfxFont *font; double x, y; state->transform(state->getCurX(), state->getCurY(), &x, &y); if ((font = state->getFont())) { yMin = y - font->getAscent() * fontSize; yMax = y - font->getDescent() * fontSize; GfxRGB rgb; state->getFillRGB(&rgb); GooString *name = state->getFont()->getName(); if (!name) name = HtmlFont::getDefaultFont(); //new GooString("default"); HtmlFont hfont=HtmlFont(name, static_cast(fontSize-1), rgb); fontpos = fonts->AddFont(hfont); } else { // this means that the PDF file draws text without a current font, // which should never happen yMin = y - 0.95 * fontSize; yMax = y + 0.35 * fontSize; fontpos=0; } if (yMin == yMax) { // this is a sanity check for a case that shouldn't happen -- but // if it does happen, we want to avoid dividing by zero later yMin = y; yMax = y + 1; } col = 0; text = NULL; xRight = NULL; link = NULL; len = size = 0; yxNext = NULL; xyNext = NULL; htext=new GooString(); dir = textDirUnknown; } HtmlString::~HtmlString() { delete text; delete htext; gfree(xRight); } void HtmlString::addChar(GfxState *state, double x, double y, double dx, double dy, Unicode u) { if (dir == textDirUnknown) { //dir = UnicodeMap::getDirection(u); dir = textDirLeftRight; } if (len == size) { size += 16; text = (Unicode *)grealloc(text, size * sizeof(Unicode)); xRight = (double *)grealloc(xRight, size * sizeof(double)); } text[len] = u; if (len == 0) { xMin = x; } xMax = xRight[len] = x + dx; //printf("added char: %f %f xright = %f\n", x, dx, x+dx); ++len; } void HtmlString::endString() { if( dir == textDirRightLeft && len > 1 ) { //printf("will reverse!\n"); for (int i = 0; i < len / 2; i++) { Unicode ch = text[i]; text[i] = text[len - i - 1]; text[len - i - 1] = ch; } } } //------------------------------------------------------------------------ // HtmlPage //------------------------------------------------------------------------ HtmlPage::HtmlPage(GBool rawOrder, char *imgExtVal) { this->rawOrder = rawOrder; curStr = NULL; yxStrings = NULL; xyStrings = NULL; yxCur1 = yxCur2 = NULL; fonts=new HtmlFontAccu(); links=new HtmlLinks(); pageWidth=0; pageHeight=0; fontsPageMarker = 0; DocName=NULL; firstPage = -1; imgExt = new GooString(imgExtVal); } HtmlPage::~HtmlPage() { clear(); if (DocName) delete DocName; if (fonts) delete fonts; if (links) delete links; if (imgExt) delete imgExt; } void HtmlPage::updateFont(GfxState *state) { GfxFont *font; double *fm; char *name; int code; double w; // adjust the font size fontSize = state->getTransformedFontSize(); if ((font = state->getFont()) && font->getType() == fontType3) { // This is a hack which makes it possible to deal with 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'). for (code = 0; code < 256; ++code) { if ((name = ((Gfx8BitFont *)font)->getCharName(code)) && name[0] == 'm' && name[1] == '\0') { break; } } if (code < 256) { w = ((Gfx8BitFont *)font)->getWidth(code); if (w != 0) { // 600 is a generic average 'm' width -- yes, this is a hack fontSize *= w / 0.6; } } fm = font->getFontMatrix(); if (fm[0] != 0) { fontSize *= fabs(fm[3] / fm[0]); } } } void HtmlPage::beginString(GfxState *state, GooString *s) { curStr = new HtmlString(state, fontSize, fonts); } void HtmlPage::conv(){ HtmlString *tmp; int linkIndex = 0; HtmlFont* h; for(tmp=yxStrings;tmp;tmp=tmp->yxNext){ int pos=tmp->fontpos; // printf("%d\n",pos); h=fonts->Get(pos); if (tmp->htext) delete tmp->htext; tmp->htext=HtmlFont::simple(h,tmp->text,tmp->len); if (links->inLink(tmp->xMin,tmp->yMin,tmp->xMax,tmp->yMax, linkIndex)){ tmp->link = links->getLink(linkIndex); /*GooString *t=tmp->htext; tmp->htext=links->getLink(k)->Link(tmp->htext); delete t;*/ } } } void HtmlPage::addChar(GfxState *state, double x, double y, double dx, double dy, double ox, double oy, Unicode *u, int uLen) { double x1, y1, w1, h1, dx2, dy2; int n, i; state->transform(x, y, &x1, &y1); n = curStr->len; // check that new character is in the same direction as current string // and is not too far away from it before adding //if ((UnicodeMap::getDirection(u[0]) != curStr->dir) || // XXX if ( (n > 0 && fabs(x1 - curStr->xRight[n-1]) > 0.1 * (curStr->yMax - curStr->yMin))) { endString(); beginString(state, NULL); } state->textTransformDelta(state->getCharSpace() * state->getHorizScaling(), 0, &dx2, &dy2); dx -= dx2; dy -= dy2; state->transformDelta(dx, dy, &w1, &h1); if (uLen != 0) { w1 /= uLen; h1 /= uLen; } for (i = 0; i < uLen; ++i) { curStr->addChar(state, x1 + i*w1, y1 + i*h1, w1, h1, u[i]); } } void HtmlPage::endString() { HtmlString *p1, *p2; double h, y1, y2; // throw away zero-length strings -- they don't have valid xMin/xMax // values, and they're useless anyway if (curStr->len == 0) { delete curStr; curStr = NULL; return; } curStr->endString(); #if 0 //~tmp if (curStr->yMax - curStr->yMin > 20) { delete curStr; curStr = NULL; return; } #endif // insert string in y-major list h = curStr->yMax - curStr->yMin; y1 = curStr->yMin + 0.5 * h; y2 = curStr->yMin + 0.8 * h; if (rawOrder) { p1 = yxCur1; p2 = NULL; } else if ((!yxCur1 || (y1 >= yxCur1->yMin && (y2 >= yxCur1->yMax || curStr->xMax >= yxCur1->xMin))) && (!yxCur2 || (y1 < yxCur2->yMin || (y2 < yxCur2->yMax && curStr->xMax < yxCur2->xMin)))) { p1 = yxCur1; p2 = yxCur2; } else { for (p1 = NULL, p2 = yxStrings; p2; p1 = p2, p2 = p2->yxNext) { if (y1 < p2->yMin || (y2 < p2->yMax && curStr->xMax < p2->xMin)) break; } yxCur2 = p2; } yxCur1 = curStr; if (p1) p1->yxNext = curStr; else yxStrings = curStr; curStr->yxNext = p2; curStr = NULL; } void HtmlPage::coalesce() { HtmlString *str1, *str2; HtmlFont *hfont1, *hfont2; double space, horSpace, vertSpace, vertOverlap; GBool addSpace, addLineBreak; int n, i; double curX, curY; #if 0 //~ for debugging for (str1 = yxStrings; str1; str1 = str1->yxNext) { printf("x=%f..%f y=%f..%f size=%2d '", str1->xMin, str1->xMax, str1->yMin, str1->yMax, (int)(str1->yMax - str1->yMin)); for (i = 0; i < str1->len; ++i) { fputc(str1->text[i] & 0xff, stdout); } printf("'\n"); } printf("\n------------------------------------------------------------\n\n"); #endif str1 = yxStrings; if( !str1 ) return; //----- discard duplicated text (fake boldface, drop shadows) if( !complexMode ) { /* if not in complex mode get rid of duplicate strings */ HtmlString *str3; GBool found; while (str1) { double size = str1->yMax - str1->yMin; double xLimit = str1->xMin + size * 0.2; found = gFalse; for (str2 = str1, str3 = str1->yxNext; str3 && str3->xMin < xLimit; str2 = str3, str3 = str2->yxNext) { if (str3->len == str1->len && !memcmp(str3->text, str1->text, str1->len * sizeof(Unicode)) && fabs(str3->yMin - str1->yMin) < size * 0.2 && fabs(str3->yMax - str1->yMax) < size * 0.2 && fabs(str3->xMax - str1->xMax) < size * 0.2) { found = gTrue; //printf("found duplicate!\n"); break; } } if (found) { str2->xyNext = str3->xyNext; str2->yxNext = str3->yxNext; delete str3; } else { str1 = str1->yxNext; } } } /*- !complexMode */ str1 = yxStrings; hfont1 = getFont(str1); if( hfont1->isBold() ) str1->htext->insert(0,"",3); if( hfont1->isItalic() ) str1->htext->insert(0,"",3); if( str1->getLink() != NULL ) { GooString *ls = str1->getLink()->getLinkStart(); str1->htext->insert(0, ls); delete ls; } curX = str1->xMin; curY = str1->yMin; while (str1 && (str2 = str1->yxNext)) { hfont2 = getFont(str2); space = str1->yMax - str1->yMin; horSpace = str2->xMin - str1->xMax; addLineBreak = !noMerge && (fabs(str1->xMin - str2->xMin) < 0.4); vertSpace = str2->yMin - str1->yMax; //printf("coalesce %d %d %f? ", str1->dir, str2->dir, d); if (str2->yMin >= str1->yMin && str2->yMin <= str1->yMax) { vertOverlap = str1->yMax - str2->yMin; } else if (str2->yMax >= str1->yMin && str2->yMax <= str1->yMax) { vertOverlap = str2->yMax - str1->yMin; } else { vertOverlap = 0; } if ( ( ( ( (rawOrder && vertOverlap > 0.5 * space) || (!rawOrder && str2->yMin < str1->yMax) ) && (horSpace > -0.5 * space && horSpace < space) ) || (vertSpace >= 0 && vertSpace < 0.5 * space && addLineBreak) ) && (!complexMode || (hfont1->isEqualIgnoreBold(*hfont2))) && // in complex mode fonts must be the same, in other modes fonts do not metter str1->dir == str2->dir // text direction the same ) { // printf("yes\n"); n = str1->len + str2->len; if ((addSpace = horSpace > 0.1 * space)) { ++n; } if (addLineBreak) { ++n; } str1->size = (n + 15) & ~15; str1->text = (Unicode *)grealloc(str1->text, str1->size * sizeof(Unicode)); str1->xRight = (double *)grealloc(str1->xRight, str1->size * sizeof(double)); if (addSpace) { str1->text[str1->len] = 0x20; str1->htext->append(" "); str1->xRight[str1->len] = str2->xMin; ++str1->len; } if (addLineBreak) { str1->text[str1->len] = '\n'; str1->htext->append("
"); str1->xRight[str1->len] = str2->xMin; ++str1->len; str1->yMin = str2->yMin; str1->yMax = str2->yMax; str1->xMax = str2->xMax; int fontLineSize = hfont1->getLineSize(); int curLineSize = (int)(vertSpace + space); if( curLineSize != fontLineSize ) { HtmlFont *newfnt = new HtmlFont(*hfont1); newfnt->setLineSize(curLineSize); str1->fontpos = fonts->AddFont(*newfnt); delete newfnt; hfont1 = getFont(str1); // we have to reget hfont2 because it's location could have // changed on resize hfont2 = getFont(str2); } } for (i = 0; i < str2->len; ++i) { str1->text[str1->len] = str2->text[i]; str1->xRight[str1->len] = str2->xRight[i]; ++str1->len; } /* fix and if str1 and str2 differ */ if( hfont1->isBold() && !hfont2->isBold() ) str1->htext->append("", 4); if( hfont1->isItalic() && !hfont2->isItalic() ) str1->htext->append("", 4); if( !hfont1->isBold() && hfont2->isBold() ) str1->htext->append("", 3); if( !hfont1->isItalic() && hfont2->isItalic() ) str1->htext->append("", 3); /* now handle switch of links */ HtmlLink *hlink1 = str1->getLink(); HtmlLink *hlink2 = str2->getLink(); if( !hlink1 || !hlink2 || !hlink1->isEqualDest(*hlink2) ) { if(hlink1 != NULL ) str1->htext->append(""); if(hlink2 != NULL ) { GooString *ls = hlink2->getLinkStart(); str1->htext->append(ls); delete ls; } } str1->htext->append(str2->htext); // str1 now contains href for link of str2 (if it is defined) str1->link = str2->link; hfont1 = hfont2; if (str2->xMax > str1->xMax) { str1->xMax = str2->xMax; } if (str2->yMax > str1->yMax) { str1->yMax = str2->yMax; } str1->yxNext = str2->yxNext; delete str2; } else { // keep strings separate // printf("no\n"); if( hfont1->isBold() ) str1->htext->append("",4); if( hfont1->isItalic() ) str1->htext->append("",4); if(str1->getLink() != NULL ) str1->htext->append(""); str1->xMin = curX; str1->yMin = curY; str1 = str2; curX = str1->xMin; curY = str1->yMin; hfont1 = hfont2; if( hfont1->isBold() ) str1->htext->insert(0,"",3); if( hfont1->isItalic() ) str1->htext->insert(0,"",3); if( str1->getLink() != NULL ) { GooString *ls = str1->getLink()->getLinkStart(); str1->htext->insert(0, ls); delete ls; } } } str1->xMin = curX; str1->yMin = curY; if( hfont1->isBold() ) str1->htext->append("",4); if( hfont1->isItalic() ) str1->htext->append("",4); if(str1->getLink() != NULL ) str1->htext->append(""); #if 0 //~ for debugging for (str1 = yxStrings; str1; str1 = str1->yxNext) { printf("x=%3d..%3d y=%3d..%3d size=%2d ", (int)str1->xMin, (int)str1->xMax, (int)str1->yMin, (int)str1->yMax, (int)(str1->yMax - str1->yMin)); printf("'%s'\n", str1->htext->getCString()); } printf("\n------------------------------------------------------------\n\n"); #endif } void HtmlPage::dumpAsXML(FILE* f,int page){ fprintf(f, "\n", pageHeight,pageWidth); for(int i=fontsPageMarker;i < fonts->size();i++) { GooString *fontCSStyle = fonts->CSStyle(i); fprintf(f,"\t%s\n",fontCSStyle->getCString()); delete fontCSStyle; } GooString *str, *str1; for(HtmlString *tmp=yxStrings;tmp;tmp=tmp->yxNext){ if (tmp->htext){ str=new GooString(tmp->htext); fprintf(f,"yMin),xoutRound(tmp->xMin)); fprintf(f,"width=\"%d\" height=\"%d\" ",xoutRound(tmp->xMax-tmp->xMin),xoutRound(tmp->yMax-tmp->yMin)); fprintf(f,"font=\"%d\">", tmp->fontpos); if (tmp->fontpos!=-1){ str1=fonts->getCSStyle(tmp->fontpos, str); } fputs(str1->getCString(),f); delete str; delete str1; fputs("\n",f); } } fputs("\n",f); } void HtmlPage::dumpComplex(FILE *file, int page){ FILE* pageFile; GooString* tmp; char* htmlEncoding; if( firstPage == -1 ) firstPage = page; if( !noframes ) { GooString* pgNum=GooString::fromInt(page); tmp = new GooString(DocName); tmp->append('-')->append(pgNum)->append(".html"); delete pgNum; if (!(pageFile = fopen(getFileNameFromPath(tmp->getCString(),tmp->getLength()), "w"))) { error(-1, "Couldn't open html file '%s'", tmp->getCString()); delete tmp; return; } delete tmp; fprintf(pageFile,"%s\n\n\nPage %d\n\n", DOCTYPE, page); htmlEncoding = HtmlOutputDev::mapEncodingToHtml (globalParams->getTextEncodingName()); fprintf(pageFile, "\n", htmlEncoding); } else { pageFile = file; fprintf(pageFile,"\n", page); fprintf(pageFile,"\n", page); } fprintf(pageFile,"
\n", pageWidth, pageHeight); tmp=basename(DocName); fputs("\n",pageFile); if( !noframes ) { fputs("\n\n",pageFile); } if( !ignore ) { fprintf(pageFile, "\"background\n", pageWidth, pageHeight, tmp->getCString(), (page-firstPage+1), imgExt->getCString()); } delete tmp; GooString *str, *str1; for(HtmlString *tmp1=yxStrings;tmp1;tmp1=tmp1->yxNext){ if (tmp1->htext){ str=new GooString(tmp1->htext); fprintf(pageFile, "
", xoutRound(tmp1->yMin), xoutRound(tmp1->xMin)); fputs("",pageFile); if (tmp1->fontpos!=-1){ str1=fonts->getCSStyle(tmp1->fontpos, str); } //printf("%s\n", str1->getCString()); fputs(str1->getCString(),pageFile); delete str; delete str1; fputs("
\n",pageFile); } } fputs("
\n", pageFile); if( !noframes ) { fputs("\n\n",pageFile); fclose(pageFile); } } void HtmlPage::dump(FILE *f, int pageNum) { if (complexMode) { if (xml) dumpAsXML(f, pageNum); if (!xml) dumpComplex(f, pageNum); } else { fprintf(f,"",pageNum); GooString* fName=basename(DocName); for (int i=1;i
\n",fName->getCString(),pageNum,i); HtmlOutputDev::imgNum=1; delete fName; GooString* str; for(HtmlString *tmp=yxStrings;tmp;tmp=tmp->yxNext){ if (tmp->htext){ str=new GooString(tmp->htext); fputs(str->getCString(),f); delete str; fputs("
\n",f); } } fputs("
\n",f); } } void HtmlPage::clear() { HtmlString *p1, *p2; if (curStr) { delete curStr; curStr = NULL; } for (p1 = yxStrings; p1; p1 = p2) { p2 = p1->yxNext; delete p1; } yxStrings = NULL; xyStrings = NULL; yxCur1 = yxCur2 = NULL; if( !noframes ) { delete fonts; fonts=new HtmlFontAccu(); fontsPageMarker = 0; } else { fontsPageMarker = fonts->size(); } delete links; links=new HtmlLinks(); } void HtmlPage::setDocName(char *fname){ DocName=new GooString(fname); } //------------------------------------------------------------------------ // HtmlMetaVar //------------------------------------------------------------------------ HtmlMetaVar::HtmlMetaVar(char *_name, char *_content) { name = new GooString(_name); content = new GooString(_content); } HtmlMetaVar::~HtmlMetaVar() { delete name; delete content; } GooString* HtmlMetaVar::toString() { GooString *result = new GooString("append(name); result->append("\" content=\""); result->append(content); result->append("\">"); return result; } //------------------------------------------------------------------------ // HtmlOutputDev //------------------------------------------------------------------------ static char* HtmlEncodings[][2] = { {"Latin1", "ISO-8859-1"}, {NULL, NULL} }; char* HtmlOutputDev::mapEncodingToHtml(GooString* encoding) { char* enc = encoding->getCString(); for(int i = 0; HtmlEncodings[i][0] != NULL; i++) { if( strcmp(enc, HtmlEncodings[i][0]) == 0 ) { return HtmlEncodings[i][1]; } } return enc; } void HtmlOutputDev::doFrame(int firstPage){ GooString* fName=new GooString(Docname); char* htmlEncoding; fName->append(".html"); if (!(fContentsFrame = fopen(getFileNameFromPath(fName->getCString(),fName->getLength()), "w"))){ delete fName; error(-1, "Couldn't open html file '%s'", fName->getCString()); return; } delete fName; fName=basename(Docname); fputs(DOCTYPE_FRAMES, fContentsFrame); fputs("\n",fContentsFrame); fputs("\n",fContentsFrame); fprintf(fContentsFrame,"\n%s",docTitle->getCString()); htmlEncoding = mapEncodingToHtml(globalParams->getTextEncodingName()); fprintf(fContentsFrame, "\n\n", htmlEncoding); dumpMetaVars(fContentsFrame); fprintf(fContentsFrame, "\n"); fputs("\n",fContentsFrame); fprintf(fContentsFrame,"\n",fName->getCString()); fputs("getCString(), firstPage); else fprintf(fContentsFrame,"\"%ss.html\"",fName->getCString()); fputs(">\n\n\n",fContentsFrame); delete fName; fclose(fContentsFrame); } HtmlOutputDev::HtmlOutputDev(char *fileName, char *title, char *author, char *keywords, char *subject, char *date, char *extension, GBool rawOrder, int firstPage, GBool outline) { char *htmlEncoding; fContentsFrame = NULL; docTitle = new GooString(title); pages = NULL; dumpJPEG=gTrue; //write = gTrue; this->rawOrder = rawOrder; this->doOutline = outline; ok = gFalse; imgNum=1; //this->firstPage = firstPage; //pageNum=firstPage; // open file needClose = gFalse; pages = new HtmlPage(rawOrder, extension); glMetaVars = new GooList(); glMetaVars->append(new HtmlMetaVar("generator", "pdftohtml 0.36")); if( author ) glMetaVars->append(new HtmlMetaVar("author", author)); if( keywords ) glMetaVars->append(new HtmlMetaVar("keywords", keywords)); if( date ) glMetaVars->append(new HtmlMetaVar("date", date)); if( subject ) glMetaVars->append(new HtmlMetaVar("subject", subject)); maxPageWidth = 0; maxPageHeight = 0; pages->setDocName(fileName); Docname=new GooString (fileName); // for non-xml output (complex or simple) with frames generate the left frame if(!xml && !noframes) { GooString* left=new GooString(fileName); left->append("_ind.html"); doFrame(firstPage); if (!(fContentsFrame = fopen(getFileNameFromPath(left->getCString(),left->getLength()), "w"))) { error(-1, "Couldn't open html file '%s'", left->getCString()); delete left; return; } delete left; fputs(DOCTYPE, fContentsFrame); fputs("\n\n\n\n\n",fContentsFrame); if (doOutline) { GooString *str = basename(Docname); fprintf(fContentsFrame, "Outline
", str->getCString(), complexMode ? "-outline.html" : "s.html#outline"); delete str; } if (!complexMode) { /* not in complex mode */ GooString* right=new GooString(fileName); right->append("s.html"); if (!(page=fopen(getFileNameFromPath(right->getCString(),right->getLength()),"w"))){ error(-1, "Couldn't open html file '%s'", right->getCString()); delete right; return; } delete right; fputs(DOCTYPE, page); fputs("\n\n\n\n\n",page); } } if (noframes) { if (stout) page=stdout; else { GooString* right=new GooString(fileName); if (!xml) right->append(".html"); if (xml) right->append(".xml"); if (!(page=fopen(getFileNameFromPath(right->getCString(),right->getLength()),"w"))){ delete right; error(-1, "Couldn't open html file '%s'", right->getCString()); return; } delete right; } htmlEncoding = mapEncodingToHtml(globalParams->getTextEncodingName()); if (xml) { fprintf(page, "\n", htmlEncoding); fputs("\n\n", page); fputs("\n",page); } else { fprintf(page,"%s\n\n\n%s\n", DOCTYPE, docTitle->getCString()); fprintf(page, "\n", htmlEncoding); dumpMetaVars(page); fprintf(page,"\n"); fprintf(page,"\n"); } } ok = gTrue; } HtmlOutputDev::~HtmlOutputDev() { /*if (mode&&!xml){ int h=xoutRound(pages->pageHeight/scale); int w=xoutRound(pages->pageWidth/scale); fprintf(tin,"%s=%03d\n","PAPER_WIDTH",w); fprintf(tin,"%s=%03d\n","PAPER_HEIGHT",h); fclose(tin); }*/ HtmlFont::clear(); delete Docname; delete docTitle; deleteGooList(glMetaVars, HtmlMetaVar); if (fContentsFrame){ fputs("\n\n",fContentsFrame); fclose(fContentsFrame); } if (xml) { fputs("\n",page); fclose(page); } else if ( !complexMode || xml || noframes ) { fputs("\n\n",page); fclose(page); } if (pages) delete pages; } void HtmlOutputDev::startPage(int pageNum, GfxState *state) { /*if (mode&&!xml){ if (write){ write=gFalse; GooString* fname=Dirname(Docname); fname->append("image.log"); if((tin=fopen(getFileNameFromPath(fname->getCString(),fname->getLength()),"w"))==NULL){ printf("Error : can not open %s",fname); exit(1); } delete fname; // if(state->getRotation()!=0) // fprintf(tin,"ROTATE=%d rotate %d neg %d neg translate\n",state->getRotation(),state->getX1(),-state->getY1()); // else fprintf(tin,"ROTATE=%d neg %d neg translate\n",state->getX1(),state->getY1()); } }*/ this->pageNum = pageNum; GooString *str=basename(Docname); pages->clear(); if(!noframes) { if (fContentsFrame) { if (complexMode) fprintf(fContentsFrame,"getCString(),pageNum); else fprintf(fContentsFrame,"getCString(),pageNum); fprintf(fContentsFrame," target=\"contents\" >Page %d
\n",pageNum); } } pages->pageWidth=static_cast(state->getPageWidth()); pages->pageHeight=static_cast(state->getPageHeight()); delete str; } void HtmlOutputDev::endPage() { Links *linksList = catalog->getPage(pageNum)->getLinks(catalog); for (int i = 0; i < linksList->getNumLinks(); ++i) { processLink(linksList->getLink(i)); } pages->conv(); pages->coalesce(); pages->dump(page, pageNum); // I don't yet know what to do in the case when there are pages of different // sizes and we want complex output: running ghostscript many times // seems very inefficient. So for now I'll just use last page's size maxPageWidth = pages->pageWidth; maxPageHeight = pages->pageHeight; //if(!noframes&&!xml) fputs("
\n", fContentsFrame); if(!stout && !globalParams->getErrQuiet()) printf("Page-%d\n",(pageNum)); } void HtmlOutputDev::updateFont(GfxState *state) { pages->updateFont(state); } void HtmlOutputDev::beginString(GfxState *state, GooString *s) { pages->beginString(state, s); } void HtmlOutputDev::endString(GfxState *state) { pages->endString(); } void HtmlOutputDev::drawChar(GfxState *state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int /*nBytes*/, Unicode *u, int uLen) { if ( !showHidden && (state->getRender() & 3) == 3) { return; } pages->addChar(state, x, y, dx, dy, originX, originY, u, uLen); } void HtmlOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, GBool invert, GBool inlineImg) { int i, j; if (ignore||complexMode) { OutputDev::drawImageMask(state, ref, str, width, height, invert, inlineImg); return; } FILE *f1; int c; int x0, y0; // top left corner of image int w0, h0, w1, h1; // size of image double xt, yt, wt, ht; GBool rotate, xFlip, yFlip; GBool dither; int x, y; int ix, iy; int px1, px2, qx, dx; int py1, py2, qy, dy; Gulong pixel; int nComps, nVals, nBits; double r1, g1, b1; // get image position and size state->transform(0, 0, &xt, &yt); state->transformDelta(1, 1, &wt, &ht); if (wt > 0) { x0 = xoutRound(xt); w0 = xoutRound(wt); } else { x0 = xoutRound(xt + wt); w0 = xoutRound(-wt); } if (ht > 0) { y0 = xoutRound(yt); h0 = xoutRound(ht); } else { y0 = xoutRound(yt + ht); h0 = xoutRound(-ht); } state->transformDelta(1, 0, &xt, &yt); rotate = fabs(xt) < fabs(yt); if (rotate) { w1 = h0; h1 = w0; xFlip = ht < 0; yFlip = wt > 0; } else { w1 = w0; h1 = h0; xFlip = wt < 0; yFlip = ht > 0; } // dump JPEG file if (dumpJPEG && str->getKind() == strDCT) { GooString *fName=new GooString(Docname); fName->append("-"); GooString *pgNum=GooString::fromInt(pageNum); GooString *imgnum=GooString::fromInt(imgNum); // open the image file fName->append(pgNum)->append("_")->append(imgnum)->append(".jpg"); ++imgNum; if (!(f1 = fopen(getFileNameFromPath(fName->getCString(),fName->getLength()), "wb"))) { error(-1, "Couldn't open image file '%s'", fName->getCString()); return; } // initialize stream str = ((DCTStream *)str)->getRawStream(); str->reset(); // copy the stream while ((c = str->getChar()) != EOF) fputc(c, f1); fclose(f1); if (pgNum) delete pgNum; if (imgnum) delete imgnum; if (fName) delete fName; } else { OutputDev::drawImageMask(state, ref, str, width, height, invert, inlineImg); } } void HtmlOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, int *maskColors, GBool inlineImg) { int i, j; if (ignore||complexMode) { OutputDev::drawImage(state, ref, str, width, height, colorMap, maskColors, inlineImg); return; } FILE *f1; ImageStream *imgStr; Guchar pixBuf[4]; GfxColor color; int c; int x0, y0; // top left corner of image int w0, h0, w1, h1; // size of image double xt, yt, wt, ht; GBool rotate, xFlip, yFlip; GBool dither; int x, y; int ix, iy; int px1, px2, qx, dx; int py1, py2, qy, dy; Gulong pixel; int nComps, nVals, nBits; double r1, g1, b1; // get image position and size state->transform(0, 0, &xt, &yt); state->transformDelta(1, 1, &wt, &ht); if (wt > 0) { x0 = xoutRound(xt); w0 = xoutRound(wt); } else { x0 = xoutRound(xt + wt); w0 = xoutRound(-wt); } if (ht > 0) { y0 = xoutRound(yt); h0 = xoutRound(ht); } else { y0 = xoutRound(yt + ht); h0 = xoutRound(-ht); } state->transformDelta(1, 0, &xt, &yt); rotate = fabs(xt) < fabs(yt); if (rotate) { w1 = h0; h1 = w0; xFlip = ht < 0; yFlip = wt > 0; } else { w1 = w0; h1 = h0; xFlip = wt < 0; yFlip = ht > 0; } /*if( !globalParams->getErrQuiet() ) printf("image stream of kind %d\n", str->getKind());*/ // dump JPEG file if (dumpJPEG && str->getKind() == strDCT) { GooString *fName=new GooString(Docname); fName->append("-"); GooString *pgNum= GooString::fromInt(pageNum); GooString *imgnum= GooString::fromInt(imgNum); // open the image file fName->append(pgNum)->append("_")->append(imgnum)->append(".jpg"); ++imgNum; if (!(f1 = fopen(getFileNameFromPath(fName->getCString(),fName->getLength()), "wb"))) { error(-1, "Couldn't open image file '%s'", fName->getCString()); return; } // initialize stream str = ((DCTStream *)str)->getRawStream(); str->reset(); // copy the stream while ((c = str->getChar()) != EOF) fputc(c, f1); fclose(f1); delete fName; delete pgNum; delete imgnum; } else { OutputDev::drawImage(state, ref, str, width, height, colorMap, maskColors, inlineImg); } } void HtmlOutputDev::processLink(Link* link){ double _x1,_y1,_x2,_y2; int x1,y1,x2,y2; link->getRect(&_x1,&_y1,&_x2,&_y2); cvtUserToDev(_x1,_y1,&x1,&y1); cvtUserToDev(_x2,_y2,&x2,&y2); GooString* _dest=getLinkDest(link,catalog); HtmlLink t((double) x1,(double) y2,(double) x2,(double) y1,_dest); pages->AddLink(t); delete _dest; } GooString* HtmlOutputDev::getLinkDest(Link *link,Catalog* catalog){ char *p; switch(link->getAction()->getKind()) { case actionGoTo: { GooString* file=basename(Docname); int page=1; LinkGoTo *ha=(LinkGoTo *)link->getAction(); LinkDest *dest=NULL; if (ha->getDest()==NULL) dest=catalog->findDest(ha->getNamedDest()); else dest=ha->getDest()->copy(); if (dest){ if (dest->isPageRef()){ Ref pageref=dest->getPageRef(); page=catalog->findPage(pageref.num,pageref.gen); } else { page=dest->getPageNum(); } delete dest; GooString *str=GooString::fromInt(page); /* complex simple frames file-4.html files.html#4 noframes file.html#4 file.html#4 */ if (noframes) { file->append(".html#"); file->append(str); } else { if( complexMode ) { file->append("-"); file->append(str); file->append(".html"); } else { file->append("s.html#"); file->append(str); } } if (printCommands) printf(" link to page %d ",page); delete str; return file; } else { return new GooString(); } } case actionGoToR: { LinkGoToR *ha=(LinkGoToR *) link->getAction(); LinkDest *dest=NULL; int page=1; GooString *file=new GooString(); if (ha->getFileName()){ delete file; file=new GooString(ha->getFileName()->getCString()); } if (ha->getDest()!=NULL) dest=ha->getDest()->copy(); if (dest&&file){ if (!(dest->isPageRef())) page=dest->getPageNum(); delete dest; if (printCommands) printf(" link to page %d ",page); if (printHtml){ p=file->getCString()+file->getLength()-4; if (!strcmp(p, ".pdf") || !strcmp(p, ".PDF")){ file->del(file->getLength()-4,4); file->append(".html"); } file->append('#'); file->append(GooString::fromInt(page)); } } if (printCommands) printf("filename %s\n",file->getCString()); return file; } case actionURI: { LinkURI *ha=(LinkURI *) link->getAction(); GooString* file=new GooString(ha->getURI()->getCString()); // printf("uri : %s\n",file->getCString()); return file; } case actionLaunch: { LinkLaunch *ha=(LinkLaunch *) link->getAction(); GooString* file=new GooString(ha->getFileName()->getCString()); if (printHtml) { p=file->getCString()+file->getLength()-4; if (!strcmp(p, ".pdf") || !strcmp(p, ".PDF")){ file->del(file->getLength()-4,4); file->append(".html"); } if (printCommands) printf("filename %s",file->getCString()); return file; } } default: return new GooString(); } } void HtmlOutputDev::dumpMetaVars(FILE *file) { GooString *var; for(int i = 0; i < glMetaVars->getLength(); i++) { HtmlMetaVar *t = (HtmlMetaVar*)glMetaVars->get(i); var = t->toString(); fprintf(file, "%s\n", var->getCString()); delete var; } } GBool HtmlOutputDev::dumpDocOutline(Catalog* catalog) { FILE * output; GBool bClose = gFalse; if (!ok || xml) return gFalse; Object *outlines = catalog->getOutline(); if (!outlines->isDict()) return gFalse; if (!complexMode && !xml) { output = page; } else if (complexMode && !xml) { if (noframes) { output = page; fputs("
\n", output); } else { GooString *str = basename(Docname); str->append("-outline.html"); output = fopen(getFileNameFromPath(str->getCString(),str->getLength()), "w"); if (output == NULL) return gFalse; delete str; bClose = gTrue; fputs("\n\nDocument Outline\n\n\n", output); } } GBool done = newOutlineLevel(output, outlines, catalog); if (done && !complexMode) fputs("
\n", output); if (bClose) { fputs("\n\n", output); fclose(output); } return done; } GBool HtmlOutputDev::newOutlineLevel(FILE *output, Object *node, Catalog* catalog, int level) { Object curr, next; GBool atLeastOne = gFalse; if (node->dictLookup("First", &curr)->isDict()) { if (level == 1) { fputs("", output); fputs("

Document Outline

\n", output); } fputs("
    ",output); do { // get title, give up if not found Object title; if (curr.dictLookup("Title", &title)->isNull()) { title.free(); break; } GooString *titleStr = new GooString(title.getString()); title.free(); // get corresponding link // Note: some code duplicated from HtmlOutputDev::getLinkDest(). GooString *linkName = NULL;; Object dest; if (!curr.dictLookup("Dest", &dest)->isNull()) { LinkGoTo *link = new LinkGoTo(&dest); LinkDest *linkdest=NULL; if (link->getDest()==NULL) linkdest=catalog->findDest(link->getNamedDest()); else linkdest=link->getDest()->copy(); delete link; if (linkdest) { int page; if (linkdest->isPageRef()) { Ref pageref=linkdest->getPageRef(); page=catalog->findPage(pageref.num,pageref.gen); } else { page=linkdest->getPageNum(); } delete linkdest; /* complex simple frames file-4.html files.html#4 noframes file.html#4 file.html#4 */ linkName=basename(Docname); GooString *str=GooString::fromInt(page); if (noframes) { linkName->append(".html#"); linkName->append(str); } else { if( complexMode ) { linkName->append("-"); linkName->append(str); linkName->append(".html"); } else { linkName->append("s.html#"); linkName->append(str); } } delete str; } } dest.free(); fputs("
  • ",output); if (linkName) fprintf(output,"", linkName->getCString()); fputs(titleStr->getCString(),output); if (linkName) { fputs("",output); delete linkName; } fputs("\n",output); delete titleStr; atLeastOne = gTrue; newOutlineLevel(output, &curr, catalog, level+1); curr.dictLookup("Next", &next); curr.free(); curr = next; } while(curr.isDict()); fputs("
",output); } curr.free(); return atLeastOne; } char* getFileNameFromPath(char* c, int strlen) { int last_slash_index = 0; int i = 0; char* res; for (i=0;i