diff options
author | Albert Astals Cid <aacid@kde.org> | 2020-07-03 23:51:42 +0200 |
---|---|---|
committer | Albert Astals Cid <aacid@kde.org> | 2020-07-03 23:51:42 +0200 |
commit | 814fbda28cc8a37fed3134c2db8da28f86fb5ee0 (patch) | |
tree | 77872b408199925ebba6a68b0dccaa0d29274c3f /utils | |
parent | 0d48722746b9702e219df58ad14cee6184a62bef (diff) |
Run clang-format
find . \( -name "*.cpp" -or -name "*.h" -or -name "*.c" -or -name "*.cc" \) -exec clang-format -i {} \;
If you reached this file doing a git blame, please see README.contributors (instructions added 2 commits in the future to this one)
Diffstat (limited to 'utils')
32 files changed, 7709 insertions, 8338 deletions
diff --git a/utils/HtmlFonts.cc b/utils/HtmlFonts.cc index 964ab018..bb46c152 100644 --- a/utils/HtmlFonts.cc +++ b/utils/HtmlFonts.cc @@ -44,29 +44,23 @@ #include "GfxFont.h" #include <cstdio> -namespace -{ +namespace { -const char* const defaultFamilyName = "Times"; +const char *const defaultFamilyName = "Times"; -const char* const styleSuffixes[] = { - "-Regular", - "-Bold", - "-BoldOblique", - "-BoldItalic", - "-Oblique", - "-Italic", - "-Roman", +const char *const styleSuffixes[] = { + "-Regular", "-Bold", "-BoldOblique", "-BoldItalic", "-Oblique", "-Italic", "-Roman", }; -void removeStyleSuffix(std::string& familyName) { - for (const char* const styleSuffix : styleSuffixes) { - auto pos = familyName.rfind(styleSuffix); - if (pos != std::string::npos) { - familyName.resize(pos); - return; +void removeStyleSuffix(std::string &familyName) +{ + for (const char *const styleSuffix : styleSuffixes) { + auto pos = familyName.rfind(styleSuffix); + if (pos != std::string::npos) { + familyName.resize(pos); + return; + } } - } } } @@ -75,266 +69,284 @@ void removeStyleSuffix(std::string& familyName) { extern bool xml; extern bool fontFullName; -HtmlFontColor::HtmlFontColor(GfxRGB rgb){ - r=static_cast<int>(rgb.r/65535.0*255.0); - g=static_cast<int>(rgb.g/65535.0*255.0); - b=static_cast<int>(rgb.b/65535.0*255.0); - if (!(Ok(r)&&Ok(b)&&Ok(g))) { - if (!globalParams->getErrQuiet()) fprintf(stderr, "Error : Bad color (%d,%d,%d) reset to (0,0,0)\n", r, g, b); - r=0;g=0;b=0; - } +HtmlFontColor::HtmlFontColor(GfxRGB rgb) +{ + r = static_cast<int>(rgb.r / 65535.0 * 255.0); + g = static_cast<int>(rgb.g / 65535.0 * 255.0); + b = static_cast<int>(rgb.b / 65535.0 * 255.0); + if (!(Ok(r) && Ok(b) && Ok(g))) { + if (!globalParams->getErrQuiet()) + fprintf(stderr, "Error : Bad color (%d,%d,%d) reset to (0,0,0)\n", r, g, b); + r = 0; + g = 0; + b = 0; + } } -GooString *HtmlFontColor::convtoX(unsigned int xcol) const{ - GooString *xret=new GooString(); - char tmp; - unsigned int k; - k = (xcol/16); - if (k<10) tmp=(char) ('0'+k); else tmp=(char)('a'+k-10); - xret->append(tmp); - k = (xcol%16); - if (k<10) tmp=(char) ('0'+k); else tmp=(char)('a'+k-10); - xret->append(tmp); - return xret; +GooString *HtmlFontColor::convtoX(unsigned int xcol) const +{ + GooString *xret = new GooString(); + char tmp; + unsigned int k; + k = (xcol / 16); + if (k < 10) + tmp = (char)('0' + k); + else + tmp = (char)('a' + k - 10); + xret->append(tmp); + k = (xcol % 16); + if (k < 10) + tmp = (char)('0' + k); + else + tmp = (char)('a' + k - 10); + xret->append(tmp); + return xret; } -GooString *HtmlFontColor::toString() const{ - GooString *tmp=new GooString("#"); - GooString *tmpr=convtoX(r); - GooString *tmpg=convtoX(g); - GooString *tmpb=convtoX(b); - tmp->append(tmpr); - tmp->append(tmpg); - tmp->append(tmpb); - delete tmpr; - delete tmpg; - delete tmpb; - return tmp; -} - -HtmlFont::HtmlFont(GfxFont *font, int _size, GfxRGB rgb){ - color=HtmlFontColor(rgb); - - lineSize = -1; - - size=_size; - italic = false; - bold = false; - rotOrSkewed = false; - - if (font->isBold() || font->getWeight() >= GfxFont::W700) bold=true; - if (font->isItalic()) italic=true; - - if (const GooString *fontname = font->getName()){ - FontName = new GooString(fontname); - - GooString fontnameLower(fontname); - fontnameLower.lowerCase(); - - if (!bold && strstr(fontnameLower.c_str(),"bold")) { - bold=true; - } +GooString *HtmlFontColor::toString() const +{ + GooString *tmp = new GooString("#"); + GooString *tmpr = convtoX(r); + GooString *tmpg = convtoX(g); + GooString *tmpb = convtoX(b); + tmp->append(tmpr); + tmp->append(tmpg); + tmp->append(tmpb); + delete tmpr; + delete tmpg; + delete tmpb; + return tmp; +} + +HtmlFont::HtmlFont(GfxFont *font, int _size, GfxRGB rgb) +{ + color = HtmlFontColor(rgb); - if (!italic && - (strstr(fontnameLower.c_str(),"italic")|| - strstr(fontnameLower.c_str(),"oblique"))) { - italic=true; + lineSize = -1; + + size = _size; + italic = false; + bold = false; + rotOrSkewed = false; + + if (font->isBold() || font->getWeight() >= GfxFont::W700) + bold = true; + if (font->isItalic()) + italic = true; + + if (const GooString *fontname = font->getName()) { + FontName = new GooString(fontname); + + GooString fontnameLower(fontname); + fontnameLower.lowerCase(); + + if (!bold && strstr(fontnameLower.c_str(), "bold")) { + bold = true; + } + + if (!italic && (strstr(fontnameLower.c_str(), "italic") || strstr(fontnameLower.c_str(), "oblique"))) { + italic = true; + } + + familyName = fontname->c_str(); + removeStyleSuffix(familyName); + } else { + FontName = new GooString(defaultFamilyName); + familyName = defaultFamilyName; } - familyName = fontname->c_str(); - removeStyleSuffix(familyName); - } else { - FontName = new GooString(defaultFamilyName); - familyName = defaultFamilyName; - } + rotSkewMat[0] = rotSkewMat[1] = rotSkewMat[2] = rotSkewMat[3] = 0; +} - rotSkewMat[0] = rotSkewMat[1] = rotSkewMat[2] = rotSkewMat[3] = 0; +HtmlFont::HtmlFont(const HtmlFont &x) +{ + size = x.size; + lineSize = x.lineSize; + italic = x.italic; + bold = x.bold; + familyName = x.familyName; + color = x.color; + FontName = new GooString(x.FontName); + rotOrSkewed = x.rotOrSkewed; + memcpy(rotSkewMat, x.rotSkewMat, sizeof(rotSkewMat)); } - -HtmlFont::HtmlFont(const HtmlFont& x){ - size=x.size; - lineSize=x.lineSize; - italic=x.italic; - bold=x.bold; - familyName=x.familyName; - color=x.color; - FontName=new GooString(x.FontName); - rotOrSkewed = x.rotOrSkewed; - memcpy(rotSkewMat, x.rotSkewMat, sizeof(rotSkewMat)); - } - - -HtmlFont::~HtmlFont(){ - delete FontName; + +HtmlFont::~HtmlFont() +{ + delete FontName; } -HtmlFont& HtmlFont::operator=(const HtmlFont& x){ - if (this==&x) return *this; - size=x.size; - lineSize=x.lineSize; - italic=x.italic; - bold=x.bold; - familyName=x.familyName; - color=x.color; - delete FontName; - FontName=new GooString(x.FontName); - return *this; +HtmlFont &HtmlFont::operator=(const HtmlFont &x) +{ + if (this == &x) + return *this; + size = x.size; + lineSize = x.lineSize; + italic = x.italic; + bold = x.bold; + familyName = x.familyName; + color = x.color; + delete FontName; + FontName = new GooString(x.FontName); + return *this; } /* This function is used to compare font uniquely for insertion into the list of all encountered fonts */ -bool HtmlFont::isEqual(const HtmlFont& x) const{ - return (size==x.size) && - (lineSize==x.lineSize) && - (FontName->cmp(x.FontName) == 0) && (bold==x.bold) && (italic==x.italic) && - (color.isEqual(x.getColor())) && isRotOrSkewed() == x.isRotOrSkewed() && - (!isRotOrSkewed() || rot_matrices_equal(getRotMat(), x.getRotMat())); +bool HtmlFont::isEqual(const HtmlFont &x) const +{ + return (size == x.size) && (lineSize == x.lineSize) && (FontName->cmp(x.FontName) == 0) && (bold == x.bold) && (italic == x.italic) && (color.isEqual(x.getColor())) && isRotOrSkewed() == x.isRotOrSkewed() + && (!isRotOrSkewed() || rot_matrices_equal(getRotMat(), x.getRotMat())); } /* This one is used to decide whether two pieces of text can be joined together and therefore we don't care about bold/italics properties */ -bool HtmlFont::isEqualIgnoreBold(const HtmlFont& x) const{ - return ((size==x.size) && - (familyName == x.familyName) && - (color.isEqual(x.getColor()))); +bool HtmlFont::isEqualIgnoreBold(const HtmlFont &x) const +{ + return ((size == x.size) && (familyName == x.familyName) && (color.isEqual(x.getColor()))); } -GooString* HtmlFont::getFontName(){ - return new GooString(familyName); +GooString *HtmlFont::getFontName() +{ + return new GooString(familyName); } -GooString* HtmlFont::getFullName(){ - return new GooString(FontName); +GooString *HtmlFont::getFullName() +{ + return new GooString(FontName); } // this method if plain wrong todo -GooString* HtmlFont::HtmlFilter(const Unicode* u, int uLen) { - GooString *tmp = new GooString(); - const UnicodeMap *uMap; - char buf[8]; - int n; - - // get the output encoding - if (!(uMap = globalParams->getTextEncoding())) { - return tmp; - } - - for (int i = 0; i < uLen; ++i) { - // skip control characters. W3C disallows them and they cause a warning - // with PHP. - if (u[i] <= 31 && u[i] != '\t') - continue; - - switch (u[i]) - { - case '"': tmp->append("""); break; - case '&': tmp->append("&"); break; - case '<': tmp->append("<"); break; - case '>': tmp->append(">"); break; - case ' ': case '\t': tmp->append( !xml && ( i+1 >= uLen || !tmp->getLength() || tmp->getChar( tmp->getLength()-1 ) == ' ' ) ? " " : " " ); - break; - default: - { - // convert unicode to string - if ((n = uMap->mapUnicode(u[i], buf, sizeof(buf))) > 0) { - tmp->append(buf, n); - } - } +GooString *HtmlFont::HtmlFilter(const Unicode *u, int uLen) +{ + GooString *tmp = new GooString(); + const UnicodeMap *uMap; + char buf[8]; + int n; + + // get the output encoding + if (!(uMap = globalParams->getTextEncoding())) { + return tmp; } - } - return tmp; -} + for (int i = 0; i < uLen; ++i) { + // skip control characters. W3C disallows them and they cause a warning + // with PHP. + if (u[i] <= 31 && u[i] != '\t') + continue; + + switch (u[i]) { + case '"': + tmp->append("""); + break; + case '&': + tmp->append("&"); + break; + case '<': + tmp->append("<"); + break; + case '>': + tmp->append(">"); + break; + case ' ': + case '\t': + tmp->append(!xml && (i + 1 >= uLen || !tmp->getLength() || tmp->getChar(tmp->getLength() - 1) == ' ') ? " " : " "); + break; + default: { + // convert unicode to string + if ((n = uMap->mapUnicode(u[i], buf, sizeof(buf))) > 0) { + tmp->append(buf, n); + } + } + } + } -HtmlFontAccu::HtmlFontAccu(){ + return tmp; } -HtmlFontAccu::~HtmlFontAccu(){ -} +HtmlFontAccu::HtmlFontAccu() { } -int HtmlFontAccu::AddFont(const HtmlFont& font){ - std::vector<HtmlFont>::iterator i; - for (i=accu.begin();i!=accu.end();++i) - { - if (font.isEqual(*i)) - { - return (int)(i-(accu.begin())); - } - } - - accu.push_back(font); - return (accu.size()-1); -} +HtmlFontAccu::~HtmlFontAccu() { } -// get CSS font definition for font #i -GooString* HtmlFontAccu::CSStyle(int i, int j){ - GooString *tmp=new GooString(); - - std::vector<HtmlFont>::iterator g=accu.begin(); - g+=i; - HtmlFont font=*g; - GooString *colorStr=font.getColor().toString(); - GooString *fontName=(fontFullName ? font.getFullName() : font.getFontName()); - - if(!xml){ - tmp->append(".ft"); - tmp->append(std::to_string(j)); - tmp->append(std::to_string(i)); - tmp->append("{font-size:"); - tmp->append(std::to_string(font.getSize())); - if( font.getLineSize() != -1 && font.getLineSize() != 0 ) - { - tmp->append("px;line-height:"); - tmp->append(std::to_string(font.getLineSize())); - } - tmp->append("px;font-family:"); - tmp->append(fontName); //font.getFontName()); - tmp->append(";color:"); - tmp->append(colorStr); - // if there is rotation or skew, include the matrix - if (font.isRotOrSkewed()) { - const double * const text_mat = font.getRotMat(); - GooString matrix_str(" matrix("); - matrix_str.appendf("{0:10.10g}, {1:10.10g}, {2:10.10g}, {3:10.10g}, 0, 0)", - text_mat[0], text_mat[1], text_mat[2], text_mat[3]); - tmp->append(";-moz-transform:"); - tmp->append(&matrix_str); - tmp->append(";-webkit-transform:"); - tmp->append(&matrix_str); - tmp->append(";-o-transform:"); - tmp->append(&matrix_str); - tmp->append(";-ms-transform:"); - tmp->append(&matrix_str); - // Todo: 75% is a wild guess that seems to work pretty well; - // We probably need to calculate the real percentage - // Based on the characteristic baseline and bounding box of current font - // PDF origin is at baseline - tmp->append(";-moz-transform-origin: left 75%"); - tmp->append(";-webkit-transform-origin: left 75%"); - tmp->append(";-o-transform-origin: left 75%"); - tmp->append(";-ms-transform-origin: left 75%"); - } - tmp->append(";}"); - } - if (xml) { - tmp->append("<fontspec id=\""); - tmp->append(std::to_string(i)); - tmp->append("\" size=\""); - tmp->append(std::to_string(font.getSize())); - tmp->append("\" family=\""); - tmp->append(fontName); - tmp->append("\" color=\""); - tmp->append(colorStr); - tmp->append("\"/>"); - } - - delete fontName; - delete colorStr; - return tmp; +int HtmlFontAccu::AddFont(const HtmlFont &font) +{ + std::vector<HtmlFont>::iterator i; + for (i = accu.begin(); i != accu.end(); ++i) { + if (font.isEqual(*i)) { + return (int)(i - (accu.begin())); + } + } + + accu.push_back(font); + return (accu.size() - 1); } - +// get CSS font definition for font #i +GooString *HtmlFontAccu::CSStyle(int i, int j) +{ + GooString *tmp = new GooString(); + + std::vector<HtmlFont>::iterator g = accu.begin(); + g += i; + HtmlFont font = *g; + GooString *colorStr = font.getColor().toString(); + GooString *fontName = (fontFullName ? font.getFullName() : font.getFontName()); + + if (!xml) { + tmp->append(".ft"); + tmp->append(std::to_string(j)); + tmp->append(std::to_string(i)); + tmp->append("{font-size:"); + tmp->append(std::to_string(font.getSize())); + if (font.getLineSize() != -1 && font.getLineSize() != 0) { + tmp->append("px;line-height:"); + tmp->append(std::to_string(font.getLineSize())); + } + tmp->append("px;font-family:"); + tmp->append(fontName); // font.getFontName()); + tmp->append(";color:"); + tmp->append(colorStr); + // if there is rotation or skew, include the matrix + if (font.isRotOrSkewed()) { + const double *const text_mat = font.getRotMat(); + GooString matrix_str(" matrix("); + matrix_str.appendf("{0:10.10g}, {1:10.10g}, {2:10.10g}, {3:10.10g}, 0, 0)", text_mat[0], text_mat[1], text_mat[2], text_mat[3]); + tmp->append(";-moz-transform:"); + tmp->append(&matrix_str); + tmp->append(";-webkit-transform:"); + tmp->append(&matrix_str); + tmp->append(";-o-transform:"); + tmp->append(&matrix_str); + tmp->append(";-ms-transform:"); + tmp->append(&matrix_str); + // Todo: 75% is a wild guess that seems to work pretty well; + // We probably need to calculate the real percentage + // Based on the characteristic baseline and bounding box of current font + // PDF origin is at baseline + tmp->append(";-moz-transform-origin: left 75%"); + tmp->append(";-webkit-transform-origin: left 75%"); + tmp->append(";-o-transform-origin: left 75%"); + tmp->append(";-ms-transform-origin: left 75%"); + } + tmp->append(";}"); + } + if (xml) { + tmp->append("<fontspec id=\""); + tmp->append(std::to_string(i)); + tmp->append("\" size=\""); + tmp->append(std::to_string(font.getSize())); + tmp->append("\" family=\""); + tmp->append(fontName); + tmp->append("\" color=\""); + tmp->append(colorStr); + tmp->append("\"/>"); + } + + delete fontName; + delete colorStr; + return tmp; +} diff --git a/utils/HtmlFonts.h b/utils/HtmlFonts.h index c44ce6a1..6f410262 100644 --- a/utils/HtmlFonts.h +++ b/utils/HtmlFonts.h @@ -36,79 +36,87 @@ #include "CharTypes.h" #include <vector> -class HtmlFontColor{ - private: - unsigned int r; - unsigned int g; - unsigned int b; - bool Ok(unsigned int xcol){ return xcol<=255;} - GooString *convtoX(unsigned int xcol) const; - public: - HtmlFontColor():r(0),g(0),b(0){} - HtmlFontColor(GfxRGB rgb); - HtmlFontColor(const HtmlFontColor& x){r=x.r;g=x.g;b=x.b;} - HtmlFontColor& operator=(const HtmlFontColor &x){ - r=x.r;g=x.g;b=x.b; - return *this; - } - ~HtmlFontColor(){}; - GooString* toString() const; - bool isEqual(const HtmlFontColor& col) const{ - return ((r==col.r)&&(g==col.g)&&(b==col.b)); - } -} ; - +class HtmlFontColor +{ +private: + unsigned int r; + unsigned int g; + unsigned int b; + bool Ok(unsigned int xcol) { return xcol <= 255; } + GooString *convtoX(unsigned int xcol) const; -class HtmlFont{ - private: - int size; - int lineSize; - bool italic; - bool bold; - bool rotOrSkewed; - std::string familyName; - GooString *FontName; - HtmlFontColor color; - double rotSkewMat[4]; // only four values needed for rotation and skew -public: +public: + HtmlFontColor() : r(0), g(0), b(0) { } + HtmlFontColor(GfxRGB rgb); + HtmlFontColor(const HtmlFontColor &x) + { + r = x.r; + g = x.g; + b = x.b; + } + HtmlFontColor &operator=(const HtmlFontColor &x) + { + r = x.r; + g = x.g; + b = x.b; + return *this; + } + ~HtmlFontColor() {}; + GooString *toString() const; + bool isEqual(const HtmlFontColor &col) const { return ((r == col.r) && (g == col.g) && (b == col.b)); } +}; - HtmlFont(GfxFont *font,int _size, GfxRGB rgb); - HtmlFont(const HtmlFont& x); - HtmlFont& operator=(const HtmlFont& x); - HtmlFontColor getColor() const {return color;} - ~HtmlFont(); - GooString* getFullName(); - bool isItalic() const {return italic;} - bool isBold() const {return bold;} - bool isRotOrSkewed() const { return rotOrSkewed; } - int getSize() const {return size;} - int getLineSize() const {return lineSize;} - void setLineSize(int _lineSize) { lineSize = _lineSize; } - void setRotMat(const double * const mat) - { rotOrSkewed = true; memcpy(rotSkewMat, mat, sizeof(rotSkewMat)); } - const double *getRotMat() const { return rotSkewMat; } - GooString* getFontName(); - static GooString* HtmlFilter(const Unicode* u, int uLen); //char* s); - bool isEqual(const HtmlFont& x) const; - bool isEqualIgnoreBold(const HtmlFont& x) const; - void print() const {printf("font: %s (%s) %d %s%s\n", FontName->c_str(), familyName.c_str(), size, bold ? "bold " : "", italic ? "italic " : "");}; +class HtmlFont +{ +private: + int size; + int lineSize; + bool italic; + bool bold; + bool rotOrSkewed; + std::string familyName; + GooString *FontName; + HtmlFontColor color; + double rotSkewMat[4]; // only four values needed for rotation and skew +public: + HtmlFont(GfxFont *font, int _size, GfxRGB rgb); + HtmlFont(const HtmlFont &x); + HtmlFont &operator=(const HtmlFont &x); + HtmlFontColor getColor() const { return color; } + ~HtmlFont(); + GooString *getFullName(); + bool isItalic() const { return italic; } + bool isBold() const { return bold; } + bool isRotOrSkewed() const { return rotOrSkewed; } + int getSize() const { return size; } + int getLineSize() const { return lineSize; } + void setLineSize(int _lineSize) { lineSize = _lineSize; } + void setRotMat(const double *const mat) + { + rotOrSkewed = true; + memcpy(rotSkewMat, mat, sizeof(rotSkewMat)); + } + const double *getRotMat() const { return rotSkewMat; } + GooString *getFontName(); + static GooString *HtmlFilter(const Unicode *u, int uLen); // char* s); + bool isEqual(const HtmlFont &x) const; + bool isEqualIgnoreBold(const HtmlFont &x) const; + void print() const { printf("font: %s (%s) %d %s%s\n", FontName->c_str(), familyName.c_str(), size, bold ? "bold " : "", italic ? "italic " : ""); }; }; -class HtmlFontAccu{ +class HtmlFontAccu +{ private: - std::vector<HtmlFont> accu; - + std::vector<HtmlFont> accu; + public: - HtmlFontAccu(); - ~HtmlFontAccu(); - HtmlFontAccu(const HtmlFontAccu &) = delete; - HtmlFontAccu& operator=(const HtmlFontAccu &) = delete; - int AddFont(const HtmlFont& font); - const HtmlFont *Get(int i) const { - return &accu[i]; - } - GooString* CSStyle(int i, int j = 0); - int size() const {return accu.size();} - -}; + HtmlFontAccu(); + ~HtmlFontAccu(); + HtmlFontAccu(const HtmlFontAccu &) = delete; + HtmlFontAccu &operator=(const HtmlFontAccu &) = delete; + int AddFont(const HtmlFont &font); + const HtmlFont *Get(int i) const { return &accu[i]; } + GooString *CSStyle(int i, int j = 0); + int size() const { return accu.size(); } +}; #endif diff --git a/utils/HtmlLinks.cc b/utils/HtmlLinks.cc index 5738ad6b..b505e37c 100644 --- a/utils/HtmlLinks.cc +++ b/utils/HtmlLinks.cc @@ -30,79 +30,96 @@ extern bool xml; -HtmlLink::HtmlLink(const HtmlLink& x){ - Xmin=x.Xmin; - Ymin=x.Ymin; - Xmax=x.Xmax; - Ymax=x.Ymax; - dest=new GooString(x.dest); +HtmlLink::HtmlLink(const HtmlLink &x) +{ + Xmin = x.Xmin; + Ymin = x.Ymin; + Xmax = x.Xmax; + Ymax = x.Ymax; + dest = new GooString(x.dest); } -HtmlLink::HtmlLink(double xmin,double ymin,double xmax,double ymax,GooString * _dest) +HtmlLink::HtmlLink(double xmin, double ymin, double xmax, double ymax, GooString *_dest) { - if (xmin < xmax) { - Xmin=xmin; - Xmax=xmax; - } else { - Xmin=xmax; - Xmax=xmin; - } - if (ymin < ymax) { - Ymin=ymin; - Ymax=ymax; - } else { - Ymin=ymax; - Ymax=ymin; - } - dest=new GooString(_dest); + if (xmin < xmax) { + Xmin = xmin; + Xmax = xmax; + } else { + Xmin = xmax; + Xmax = xmin; + } + if (ymin < ymax) { + Ymin = ymin; + Ymax = ymax; + } else { + Ymin = ymax; + Ymax = ymin; + } + dest = new GooString(_dest); } -HtmlLink::~HtmlLink(){ - delete dest; +HtmlLink::~HtmlLink() +{ + delete dest; } -bool HtmlLink::isEqualDest(const HtmlLink& x) const{ - return (!strcmp(dest->c_str(), x.dest->c_str())); +bool HtmlLink::isEqualDest(const HtmlLink &x) const +{ + return (!strcmp(dest->c_str(), x.dest->c_str())); } -bool HtmlLink::inLink(double xmin,double ymin,double xmax,double ymax) const { - double y=(ymin+ymax)/2; - if (y>Ymax) return false; - return (y>Ymin)&&(xmin<Xmax)&&(xmax>Xmin); - } - -static GooString* EscapeSpecialChars( GooString* s ) +bool HtmlLink::inLink(double xmin, double ymin, double xmax, double ymax) const { - GooString* tmp = nullptr; - for( int i = 0, j = 0; i < s->getLength(); i++, j++ ){ + double y = (ymin + ymax) / 2; + if (y > Ymax) + return false; + return (y > Ymin) && (xmin < Xmax) && (xmax > Xmin); +} + +static GooString *EscapeSpecialChars(GooString *s) +{ + GooString *tmp = nullptr; + for (int i = 0, j = 0; i < s->getLength(); i++, j++) { const char *replace = nullptr; - switch ( s->getChar(i) ){ - case '"': replace = """; break; - case '&': replace = "&"; break; - case '<': replace = "<"; break; - case '>': replace = ">"; break; - default: continue; - } - if( replace ){ - if( !tmp ) tmp = new GooString( s ); - if( tmp ){ - tmp->del( j, 1 ); - int l = strlen( replace ); - tmp->insert( j, replace, l ); - j += l - 1; - } - } - } - return tmp ? tmp : s; + switch (s->getChar(i)) { + case '"': + replace = """; + break; + case '&': + replace = "&"; + break; + case '<': + replace = "<"; + break; + case '>': + replace = ">"; + break; + default: + continue; + } + if (replace) { + if (!tmp) + tmp = new GooString(s); + if (tmp) { + tmp->del(j, 1); + int l = strlen(replace); + tmp->insert(j, replace, l); + j += l - 1; + } + } + } + return tmp ? tmp : s; } -GooString* HtmlLink::getLinkStart() { - GooString *res = new GooString("<a href=\""); - GooString *d = xml ? EscapeSpecialChars(dest) : dest; - res->append( d ); - if( d != dest ) delete d; - res->append("\">"); - return res; +GooString *HtmlLink::getLinkStart() +{ + GooString *res = new GooString("<a href=\""); + GooString *d = xml ? EscapeSpecialChars(dest) : dest; + res->append(d); + if (d != dest) + delete d; + res->append("\">"); + return res; } /*GooString* HtmlLink::Link(GooString* content){ @@ -116,29 +133,30 @@ GooString* HtmlLink::getLinkStart() { return tmp; }*/ - - -HtmlLinks::HtmlLinks(){ - accu=new std::vector<HtmlLink>(); +HtmlLinks::HtmlLinks() +{ + accu = new std::vector<HtmlLink>(); } -HtmlLinks::~HtmlLinks(){ - delete accu; - accu=nullptr; +HtmlLinks::~HtmlLinks() +{ + delete accu; + accu = nullptr; } -bool HtmlLinks::inLink(double xmin,double ymin,double xmax,double ymax,int& p)const { - - for(std::vector<HtmlLink>::iterator i=accu->begin();i!=accu->end();++i){ - if (i->inLink(xmin,ymin,xmax,ymax)) { - p=(i - accu->begin()); - return true; +bool HtmlLinks::inLink(double xmin, double ymin, double xmax, double ymax, int &p) const +{ + + for (std::vector<HtmlLink>::iterator i = accu->begin(); i != accu->end(); ++i) { + if (i->inLink(xmin, ymin, xmax, ymax)) { + p = (i - accu->begin()); + return true; + } } - } - return false; + return false; } -HtmlLink* HtmlLinks::getLink(int i) const{ - return &(*accu)[i]; +HtmlLink *HtmlLinks::getLink(int i) const +{ + return &(*accu)[i]; } - diff --git a/utils/HtmlLinks.h b/utils/HtmlLinks.h index 78bbb11e..fa4fb2a3 100644 --- a/utils/HtmlLinks.h +++ b/utils/HtmlLinks.h @@ -32,45 +32,45 @@ #include <vector> #include "goo/GooString.h" -class HtmlLink{ +class HtmlLink +{ -private: - double Xmin; - double Ymin; - double Xmax; - double Ymax; - GooString* dest; +private: + double Xmin; + double Ymin; + double Xmax; + double Ymax; + GooString *dest; public: - HtmlLink(const HtmlLink& x); - HtmlLink(double xmin,double ymin,double xmax,double ymax,GooString *_dest); - ~HtmlLink(); - HtmlLink& operator=(const HtmlLink &) = delete; - bool isEqualDest(const HtmlLink& x) const; - GooString *getDest(){return new GooString(dest);} - double getX1() const {return Xmin;} - double getX2() const {return Xmax;} - double getY1() const {return Ymin;} - double getY2() const {return Ymax;} - bool inLink(double xmin,double ymin,double xmax,double ymax) const ; - //GooString *Link(GooString *content); - GooString* getLinkStart(); - + HtmlLink(const HtmlLink &x); + HtmlLink(double xmin, double ymin, double xmax, double ymax, GooString *_dest); + ~HtmlLink(); + HtmlLink &operator=(const HtmlLink &) = delete; + bool isEqualDest(const HtmlLink &x) const; + GooString *getDest() { return new GooString(dest); } + double getX1() const { return Xmin; } + double getX2() const { return Xmax; } + double getY1() const { return Ymin; } + double getY2() const { return Ymax; } + bool inLink(double xmin, double ymin, double xmax, double ymax) const; + // GooString *Link(GooString *content); + GooString *getLinkStart(); }; -class HtmlLinks{ +class HtmlLinks +{ private: - std::vector<HtmlLink> *accu; -public: - HtmlLinks(); - ~HtmlLinks(); - HtmlLinks(const HtmlLinks &) = delete; - HtmlLinks& operator=(const HtmlLinks &) = delete; - void AddLink(const HtmlLink& x) {accu->push_back(x);} - bool inLink(double xmin,double ymin,double xmax,double ymax,int& p) const; - HtmlLink* getLink(int i) const; + std::vector<HtmlLink> *accu; +public: + HtmlLinks(); + ~HtmlLinks(); + HtmlLinks(const HtmlLinks &) = delete; + HtmlLinks &operator=(const HtmlLinks &) = delete; + void AddLink(const HtmlLink &x) { accu->push_back(x); } + bool inLink(double xmin, double ymin, double xmax, double ymax, int &p) const; + HtmlLink *getLink(int i) const; }; #endif - diff --git a/utils/HtmlOutputDev.cc b/utils/HtmlOutputDev.cc index b0f562ad..27a7f11a 100644 --- a/utils/HtmlOutputDev.cc +++ b/utils/HtmlOutputDev.cc @@ -78,7 +78,7 @@ #include "PDFDoc.h" #ifdef ENABLE_LIBPNG -#include <png.h> +# include <png.h> #endif #define DEBUG __FILE__ << ": " << __LINE__ << ": DEBUG: " @@ -86,22 +86,25 @@ class HtmlImage { public: - HtmlImage(GooString *_fName, GfxState *state) - : fName(_fName) { - state->transform(0, 0, &xMin, &yMax); - state->transform(1, 1, &xMax, &yMin); - } - ~HtmlImage() { delete fName; } - HtmlImage(const HtmlImage &) = delete; - HtmlImage& operator=(const HtmlImage &) = delete; + HtmlImage(GooString *_fName, GfxState *state) : fName(_fName) + { + state->transform(0, 0, &xMin, &yMax); + state->transform(1, 1, &xMax, &yMin); + } + ~HtmlImage() { delete fName; } + HtmlImage(const HtmlImage &) = delete; + HtmlImage &operator=(const HtmlImage &) = delete; - double xMin, xMax; // image x coordinates - double yMin, yMax; // image y coordinates - GooString *fName; // image file name + double xMin, xMax; // image x coordinates + double yMin, yMax; // image y coordinates + GooString *fName; // image file name }; // returns true if x is closer to y than x is to z -static inline bool IS_CLOSER(float x, float y, float z) { return std::fabs((x)-(y)) < std::fabs((x)-(z)); } +static inline bool IS_CLOSER(float x, float y, float z) +{ + return std::fabs((x) - (y)) < std::fabs((x) - (z)); +} extern bool complexMode; extern bool singleHtml; @@ -130,290 +133,291 @@ static GooString* Dirname(GooString* str){ if (*(p+i)==SLASH) return new GooString(p,i+1); return new GooString(); -} +} #endif -static const char *print_matrix(const double *mat) { - delete gstr_buff0; +static const char *print_matrix(const double *mat) +{ + delete gstr_buff0; - gstr_buff0 = GooString::format("[{0:g} {1:g} {2:g} {3:g} {4:g} {5:g}]", - *mat, mat[1], mat[2], mat[3], mat[4], mat[5]); - return gstr_buff0->c_str(); + gstr_buff0 = GooString::format("[{0:g} {1:g} {2:g} {3:g} {4:g} {5:g}]", *mat, mat[1], mat[2], mat[3], mat[4], mat[5]); + return gstr_buff0->c_str(); } -static const char *print_uni_str(const Unicode *u, const unsigned uLen) { - GooString *gstr_buff1 = nullptr; - - delete gstr_buff0; - - if (!uLen) return ""; - gstr_buff0 = GooString::format("{0:c}", (*u < 0x7F ? *u & 0xFF : '?')); - for (unsigned i = 1; i < uLen; i++) { - if (u[i] < 0x7F) { - gstr_buff1 = gstr_buff0->append(u[i] < 0x7F ? static_cast<char>(u[i]) & 0xFF : '?'); - delete gstr_buff0; - gstr_buff0 = gstr_buff1; +static const char *print_uni_str(const Unicode *u, const unsigned uLen) +{ + GooString *gstr_buff1 = nullptr; + + delete gstr_buff0; + + if (!uLen) + return ""; + gstr_buff0 = GooString::format("{0:c}", (*u < 0x7F ? *u & 0xFF : '?')); + for (unsigned i = 1; i < uLen; i++) { + if (u[i] < 0x7F) { + gstr_buff1 = gstr_buff0->append(u[i] < 0x7F ? static_cast<char>(u[i]) & 0xFF : '?'); + delete gstr_buff0; + gstr_buff0 = gstr_buff1; + } } - } - return gstr_buff0->c_str(); + return gstr_buff0->c_str(); } //------------------------------------------------------------------------ // HtmlString //------------------------------------------------------------------------ -HtmlString::HtmlString(GfxState *state, double fontSize, HtmlFontAccu* _fonts) : fonts(_fonts) { - GfxFont *font; - double x, y; - - state->transform(state->getCurX(), state->getCurY(), &x, &y); - if ((font = state->getFont())) { - double ascent = font->getAscent(); - double descent = font->getDescent(); - if( ascent > 1.05 ){ - //printf( "ascent=%.15g is too high, descent=%.15g\n", ascent, descent ); - ascent = 1.05; - } - if( descent < -0.4 ){ - //printf( "descent %.15g is too low, ascent=%.15g\n", descent, ascent ); - descent = -0.4; - } - yMin = y - ascent * fontSize; - yMax = y - descent * fontSize; - GfxRGB rgb; - state->getFillRGB(&rgb); - HtmlFont hfont=HtmlFont(font, static_cast<int>(fontSize), rgb); - if (isMatRotOrSkew(state->getTextMat())) { - double normalizedMatrix[4]; - memcpy(normalizedMatrix, state->getTextMat(), sizeof(normalizedMatrix)); - // browser rotates the opposite way - // so flip the sign of the angle -> sin() components change sign - if (debug) - std::cerr << DEBUG << "before transform: " << print_matrix(normalizedMatrix) << std::endl; - normalizedMatrix[1] *= -1; - normalizedMatrix[2] *= -1; - if (debug) - std::cerr << DEBUG << "after reflecting angle: " << print_matrix(normalizedMatrix) << std::endl; - normalizeRotMat(normalizedMatrix); - if (debug) - std::cerr << DEBUG << "after norm: " << print_matrix(normalizedMatrix) << std::endl; - hfont.setRotMat(normalizedMatrix); - } - 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 = nullptr; - xRight = nullptr; - link = nullptr; - len = size = 0; - yxNext = nullptr; - xyNext = nullptr; - htext=new GooString(); - dir = textDirUnknown; +HtmlString::HtmlString(GfxState *state, double fontSize, HtmlFontAccu *_fonts) : fonts(_fonts) +{ + GfxFont *font; + double x, y; + + state->transform(state->getCurX(), state->getCurY(), &x, &y); + if ((font = state->getFont())) { + double ascent = font->getAscent(); + double descent = font->getDescent(); + if (ascent > 1.05) { + // printf( "ascent=%.15g is too high, descent=%.15g\n", ascent, descent ); + ascent = 1.05; + } + if (descent < -0.4) { + // printf( "descent %.15g is too low, ascent=%.15g\n", descent, ascent ); + descent = -0.4; + } + yMin = y - ascent * fontSize; + yMax = y - descent * fontSize; + GfxRGB rgb; + state->getFillRGB(&rgb); + HtmlFont hfont = HtmlFont(font, static_cast<int>(fontSize), rgb); + if (isMatRotOrSkew(state->getTextMat())) { + double normalizedMatrix[4]; + memcpy(normalizedMatrix, state->getTextMat(), sizeof(normalizedMatrix)); + // browser rotates the opposite way + // so flip the sign of the angle -> sin() components change sign + if (debug) + std::cerr << DEBUG << "before transform: " << print_matrix(normalizedMatrix) << std::endl; + normalizedMatrix[1] *= -1; + normalizedMatrix[2] *= -1; + if (debug) + std::cerr << DEBUG << "after reflecting angle: " << print_matrix(normalizedMatrix) << std::endl; + normalizeRotMat(normalizedMatrix); + if (debug) + std::cerr << DEBUG << "after norm: " << print_matrix(normalizedMatrix) << std::endl; + hfont.setRotMat(normalizedMatrix); + } + 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 = nullptr; + xRight = nullptr; + link = nullptr; + len = size = 0; + yxNext = nullptr; + xyNext = nullptr; + htext = new GooString(); + dir = textDirUnknown; } - -HtmlString::~HtmlString() { - gfree(text); - delete htext; - gfree(xRight); +HtmlString::~HtmlString() +{ + gfree(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::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; + 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(bool rawOrderA) { - rawOrder = rawOrderA; - curStr = nullptr; - yxStrings = nullptr; - xyStrings = nullptr; - yxCur1 = yxCur2 = nullptr; - fonts=new HtmlFontAccu(); - links=new HtmlLinks(); - imgList=new std::vector<HtmlImage*>(); - pageWidth=0; - pageHeight=0; - fontsPageMarker = 0; - DocName=nullptr; - firstPage = -1; +HtmlPage::HtmlPage(bool rawOrderA) +{ + rawOrder = rawOrderA; + curStr = nullptr; + yxStrings = nullptr; + xyStrings = nullptr; + yxCur1 = yxCur2 = nullptr; + fonts = new HtmlFontAccu(); + links = new HtmlLinks(); + imgList = new std::vector<HtmlImage *>(); + pageWidth = 0; + pageHeight = 0; + fontsPageMarker = 0; + DocName = nullptr; + firstPage = -1; } -HtmlPage::~HtmlPage() { - clear(); - delete DocName; - delete fonts; - delete links; - for (auto entry : *imgList) { - delete entry; - } - delete imgList; +HtmlPage::~HtmlPage() +{ + clear(); + delete DocName; + delete fonts; + delete links; + for (auto entry : *imgList) { + delete entry; + } + delete imgList; } -void HtmlPage::updateFont(GfxState *state) { - GfxFont *font; - const 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; - } - } - const double *fm = font->getFontMatrix(); - if (fm[0] != 0) { - fontSize *= fabs(fm[3] / fm[0]); +void HtmlPage::updateFont(GfxState *state) +{ + GfxFont *font; + const 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; + } + } + const double *fm = font->getFontMatrix(); + if (fm[0] != 0) { + fontSize *= fabs(fm[3] / fm[0]); + } } - } } -void HtmlPage::beginString(GfxState *state, const GooString *s) { - curStr = new HtmlString(state, fontSize, fonts); +void HtmlPage::beginString(GfxState *state, const GooString *s) +{ + curStr = new HtmlString(state, fontSize, fonts); } +void HtmlPage::conv() +{ + for (HtmlString *tmp = yxStrings; tmp; tmp = tmp->yxNext) { + delete tmp->htext; + tmp->htext = HtmlFont::HtmlFilter(tmp->text, tmp->len); -void HtmlPage::conv(){ - for(HtmlString *tmp=yxStrings;tmp;tmp=tmp->yxNext){ - delete tmp->htext; - tmp->htext=HtmlFont::HtmlFilter(tmp->text,tmp->len); - - int linkIndex = 0; - if (links->inLink(tmp->xMin,tmp->yMin,tmp->xMax,tmp->yMax, linkIndex)){ - tmp->link = links->getLink(linkIndex); - } - } + int linkIndex = 0; + if (links->inLink(tmp->xMin, tmp->yMin, tmp->xMax, tmp->yMax, linkIndex)) { + tmp->link = links->getLink(linkIndex); + } + } } - -void HtmlPage::addChar(GfxState *state, double x, double y, - double dx, double dy, - double ox, double oy, const 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 (debug) { - const double *text_mat = state->getTextMat(); - // rotation is (cos q, sin q, -sin q, cos q, 0, 0) - // sin q is zero iff there is no rotation, or 180 deg. rotation; - // for 180 rotation, cos q will be negative - if (text_mat[0] < 0 || !is_within(text_mat[1], .1, 0)) { - std::cerr << DEBUG << "rotation matrix for \"" << print_uni_str(u, uLen) << '"' << std::endl; - std::cerr << "text " << print_matrix(state->getTextMat()); +void HtmlPage::addChar(GfxState *state, double x, double y, double dx, double dy, double ox, double oy, const 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 (debug) { + const double *text_mat = state->getTextMat(); + // rotation is (cos q, sin q, -sin q, cos q, 0, 0) + // sin q is zero iff there is no rotation, or 180 deg. rotation; + // for 180 rotation, cos q will be negative + if (text_mat[0] < 0 || !is_within(text_mat[1], .1, 0)) { + std::cerr << DEBUG << "rotation matrix for \"" << print_uni_str(u, uLen) << '"' << std::endl; + std::cerr << "text " << print_matrix(state->getTextMat()); + } + } + if (n > 0 && // don't start a new string, unless there is already a string + // TODO: the following line assumes that text is flowing left to + // right, which will not necessarily be the case, e.g. if rotated; + // It assesses whether or not two characters are close enough to + // be part of the same string + fabs(x1 - curStr->xRight[n - 1]) > wordBreakThreshold * (curStr->yMax - curStr->yMin) && + // rotation is (cos q, sin q, -sin q, cos q, 0, 0) + // sin q is zero iff there is no rotation, or 180 deg. rotation; + // for 180 rotation, cos q will be negative + !rot_matrices_equal(curStr->getFont().getRotMat(), state->getTextMat())) { + endString(); + beginString(state, nullptr); + } + 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]); } - } - if (n > 0 && // don't start a new string, unless there is already a string - // TODO: the following line assumes that text is flowing left to - // right, which will not necessarily be the case, e.g. if rotated; - // It assesses whether or not two characters are close enough to - // be part of the same string - fabs(x1 - curStr->xRight[n-1]) > wordBreakThreshold * (curStr->yMax - curStr->yMin) && - // rotation is (cos q, sin q, -sin q, cos q, 0, 0) - // sin q is zero iff there is no rotation, or 180 deg. rotation; - // for 180 rotation, cos q will be negative - !rot_matrices_equal(curStr->getFont().getRotMat(), state->getTextMat())) - { - endString(); - beginString(state, nullptr); - } - 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 = nullptr; - return; - } +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 = nullptr; + return; + } - curStr->endString(); + curStr->endString(); #if 0 //~tmp if (curStr->yMax - curStr->yMin > 20) { @@ -423,76 +427,72 @@ void HtmlPage::endString() { } #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 = nullptr; - } 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 = nullptr, 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 = nullptr; + // 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 = nullptr; + } 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 = nullptr, 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 = nullptr; } -static const char *strrstr( const char *s, const char *ss ) +static const char *strrstr(const char *s, const char *ss) { - const char *p = strstr( s, ss ); - for( const char *pp = p; pp != nullptr; pp = strstr( p+1, ss ) ){ - p = pp; - } - return p; + const char *p = strstr(s, ss); + for (const char *pp = p; pp != nullptr; pp = strstr(p + 1, ss)) { + p = pp; + } + return p; } -static void CloseTags( GooString *htext, bool &finish_a, bool &finish_italic, bool &finish_bold ) +static void CloseTags(GooString *htext, bool &finish_a, bool &finish_italic, bool &finish_bold) { - const char *last_italic = finish_italic && ( finish_bold || finish_a ) ? strrstr( htext->c_str(), "<i>" ) : nullptr; - const char *last_bold = finish_bold && ( finish_italic || finish_a ) ? strrstr( htext->c_str(), "<b>" ) : nullptr; - const char *last_a = finish_a && ( finish_italic || finish_bold ) ? strrstr( htext->c_str(), "<a " ) : nullptr; - if( finish_a && ( finish_italic || finish_bold ) && last_a > ( last_italic > last_bold ? last_italic : last_bold ) ){ - htext->append("</a>", 4); - finish_a = false; - } - if( finish_italic && finish_bold && last_italic > last_bold ){ - htext->append("</i>", 4); - finish_italic = false; - } - if( finish_bold ) - htext->append("</b>", 4); - if( finish_italic ) - htext->append("</i>", 4); - if( finish_a ) - htext->append("</a>"); + const char *last_italic = finish_italic && (finish_bold || finish_a) ? strrstr(htext->c_str(), "<i>") : nullptr; + const char *last_bold = finish_bold && (finish_italic || finish_a) ? strrstr(htext->c_str(), "<b>") : nullptr; + const char *last_a = finish_a && (finish_italic || finish_bold) ? strrstr(htext->c_str(), "<a ") : nullptr; + if (finish_a && (finish_italic || finish_bold) && last_a > (last_italic > last_bold ? last_italic : last_bold)) { + htext->append("</a>", 4); + finish_a = false; + } + if (finish_italic && finish_bold && last_italic > last_bold) { + htext->append("</i>", 4); + finish_italic = false; + } + if (finish_bold) + htext->append("</b>", 4); + if (finish_italic) + htext->append("</i>", 4); + if (finish_a) + htext->append("</a>"); } // Strings are lines of text; // This function aims to combine strings into lines and paragraphs if !noMerge // It may also strip out duplicate strings (if they are on top of each other); sometimes they are to create a font effect -void HtmlPage::coalesce() { - HtmlString *str1, *str2; - double space, horSpace, vertSpace, vertOverlap; - bool addSpace, addLineBreak; - int n, i; - double curX, curY; +void HtmlPage::coalesce() +{ + HtmlString *str1, *str2; + double space, horSpace, vertSpace, vertOverlap; + bool addSpace, addLineBreak; + int n, i; + double curX, curY; #if 0 //~ for debugging for (str1 = yxStrings; str1; str1 = str1->yxNext) { @@ -506,216 +506,189 @@ void HtmlPage::coalesce() { } 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; - bool found; - while (str1) - { - double size = str1->yMax - str1->yMin; - double xLimit = str1->xMin + size * 0.2; - found = false; - 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 = true; - //printf("found duplicate!\n"); - break; - } - } - if (found) - { - str2->xyNext = str3->xyNext; - str2->yxNext = str3->yxNext; - delete str3; - } - else - { - str1 = str1->yxNext; - } - } - } /*- !complexMode */ - - str1 = yxStrings; - - const HtmlFont *hfont1 = getFont(str1); - if( hfont1->isBold() ) - str1->htext->insert(0,"<b>",3); - if( hfont1->isItalic() ) - str1->htext->insert(0,"<i>",3); - if( str1->getLink() != nullptr ) { - GooString *ls = str1->getLink()->getLinkStart(); - str1->htext->insert(0, ls); - delete ls; - } - curX = str1->xMin; curY = str1->yMin; - - while (str1 && (str2 = str1->yxNext)) { - const HtmlFont *hfont2 = getFont(str2); - space = str1->yMax - str1->yMin; // the height of the font's bounding box - horSpace = str2->xMin - str1->xMax; - // if strings line up on left-hand side AND they are on subsequent lines, we need a line break - addLineBreak = !noMerge && (fabs(str1->xMin - str2->xMin) < 0.4) && IS_CLOSER(str2->yMax, str1->yMax + space, str1->yMax); - vertSpace = str2->yMin - str1->yMax; - -//printf("coalesce %d %d %f? ", str1->dir, str2->dir, d); + str1 = yxStrings; - 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; - } - - // Combine strings if: - // They appear to be the same font (complex mode only) && going in the same direction AND at least one of the following: - // 1. They appear to be part of the same line of text - // 2. They appear to be subsequent lines of a paragraph - // We assume (1) or (2) above, respectively, based on: - // (1) strings overlap vertically AND - // horizontal space between end of str1 and start of str2 is consistent with a single space or less; - // when rawOrder, the strings have to overlap vertically by at least 50% - // (2) Strings flow down the page, but the space between them is not too great, and they are lined up on the left - 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 > wordBreakThreshold * 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(xml?" ":" "); - str1->xRight[str1->len] = str2->xMin; - ++str1->len; - } - if (addLineBreak) { - str1->text[str1->len] = '\n'; - str1->htext->append("<br/>"); - 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; - } + if (!str1) + return; - /* fix <i>, <b> if str1 and str2 differ and handle switch of links */ - HtmlLink *hlink1 = str1->getLink(); - HtmlLink *hlink2 = str2->getLink(); - bool switch_links = !hlink1 || !hlink2 || !hlink1->isEqualDest(*hlink2); - bool finish_a = switch_links && hlink1 != nullptr; - bool finish_italic = hfont1->isItalic() && ( !hfont2->isItalic() || finish_a ); - bool finish_bold = hfont1->isBold() && ( !hfont2->isBold() || finish_a || finish_italic ); - CloseTags( str1->htext, finish_a, finish_italic, finish_bold ); - if( switch_links && hlink2 != nullptr ) { - GooString *ls = hlink2->getLinkStart(); - str1->htext->append(ls); + //----- discard duplicated text (fake boldface, drop shadows) + if (!complexMode) { /* if not in complex mode get rid of duplicate strings */ + HtmlString *str3; + bool found; + while (str1) { + double size = str1->yMax - str1->yMin; + double xLimit = str1->xMin + size * 0.2; + found = false; + 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 = true; + // printf("found duplicate!\n"); + break; + } + } + if (found) { + str2->xyNext = str3->xyNext; + str2->yxNext = str3->yxNext; + delete str3; + } else { + str1 = str1->yxNext; + } + } + } /*- !complexMode */ + + str1 = yxStrings; + + const HtmlFont *hfont1 = getFont(str1); + if (hfont1->isBold()) + str1->htext->insert(0, "<b>", 3); + if (hfont1->isItalic()) + str1->htext->insert(0, "<i>", 3); + if (str1->getLink() != nullptr) { + GooString *ls = str1->getLink()->getLinkStart(); + str1->htext->insert(0, ls); delete ls; - } - if( ( !hfont1->isItalic() || finish_italic ) && hfont2->isItalic() ) - str1->htext->append("<i>", 3); - if( ( !hfont1->isBold() || finish_bold ) && hfont2->isBold() ) - str1->htext->append("<b>", 3); - - - 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"); - bool finish_a = str1->getLink() != nullptr; - bool finish_bold = hfont1->isBold(); - bool finish_italic = hfont1->isItalic(); - CloseTags( str1->htext, finish_a, finish_italic, finish_bold ); - - str1->xMin = curX; str1->yMin = curY; - str1 = str2; - curX = str1->xMin; curY = str1->yMin; - hfont1 = hfont2; - if( hfont1->isBold() ) - str1->htext->insert(0,"<b>",3); - if( hfont1->isItalic() ) - str1->htext->insert(0,"<i>",3); - if( str1->getLink() != nullptr ) { - GooString *ls = str1->getLink()->getLinkStart(); - str1->htext->insert(0, ls); - delete ls; - } } - } - str1->xMin = curX; str1->yMin = curY; + curX = str1->xMin; + curY = str1->yMin; + + while (str1 && (str2 = str1->yxNext)) { + const HtmlFont *hfont2 = getFont(str2); + space = str1->yMax - str1->yMin; // the height of the font's bounding box + horSpace = str2->xMin - str1->xMax; + // if strings line up on left-hand side AND they are on subsequent lines, we need a line break + addLineBreak = !noMerge && (fabs(str1->xMin - str2->xMin) < 0.4) && IS_CLOSER(str2->yMax, str1->yMax + space, str1->yMax); + 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; + } - bool finish_bold = hfont1->isBold(); - bool finish_italic = hfont1->isItalic(); - bool finish_a = str1->getLink() != nullptr; - CloseTags( str1->htext, finish_a, finish_italic, finish_bold ); + // Combine strings if: + // They appear to be the same font (complex mode only) && going in the same direction AND at least one of the following: + // 1. They appear to be part of the same line of text + // 2. They appear to be subsequent lines of a paragraph + // We assume (1) or (2) above, respectively, based on: + // (1) strings overlap vertically AND + // horizontal space between end of str1 and start of str2 is consistent with a single space or less; + // when rawOrder, the strings have to overlap vertically by at least 50% + // (2) Strings flow down the page, but the space between them is not too great, and they are lined up on the left + 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 > wordBreakThreshold * 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(xml ? " " : " "); + str1->xRight[str1->len] = str2->xMin; + ++str1->len; + } + if (addLineBreak) { + str1->text[str1->len] = '\n'; + str1->htext->append("<br/>"); + 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 <i>, <b> if str1 and str2 differ and handle switch of links */ + HtmlLink *hlink1 = str1->getLink(); + HtmlLink *hlink2 = str2->getLink(); + bool switch_links = !hlink1 || !hlink2 || !hlink1->isEqualDest(*hlink2); + bool finish_a = switch_links && hlink1 != nullptr; + bool finish_italic = hfont1->isItalic() && (!hfont2->isItalic() || finish_a); + bool finish_bold = hfont1->isBold() && (!hfont2->isBold() || finish_a || finish_italic); + CloseTags(str1->htext, finish_a, finish_italic, finish_bold); + if (switch_links && hlink2 != nullptr) { + GooString *ls = hlink2->getLinkStart(); + str1->htext->append(ls); + delete ls; + } + if ((!hfont1->isItalic() || finish_italic) && hfont2->isItalic()) + str1->htext->append("<i>", 3); + if ((!hfont1->isBold() || finish_bold) && hfont2->isBold()) + str1->htext->append("<b>", 3); + + 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"); + bool finish_a = str1->getLink() != nullptr; + bool finish_bold = hfont1->isBold(); + bool finish_italic = hfont1->isItalic(); + CloseTags(str1->htext, finish_a, finish_italic, finish_bold); + + str1->xMin = curX; + str1->yMin = curY; + str1 = str2; + curX = str1->xMin; + curY = str1->yMin; + hfont1 = hfont2; + if (hfont1->isBold()) + str1->htext->insert(0, "<b>", 3); + if (hfont1->isItalic()) + str1->htext->insert(0, "<i>", 3); + if (str1->getLink() != nullptr) { + GooString *ls = str1->getLink()->getLinkStart(); + str1->htext->insert(0, ls); + delete ls; + } + } + } + str1->xMin = curX; + str1->yMin = curY; + + bool finish_bold = hfont1->isBold(); + bool finish_italic = hfont1->isItalic(); + bool finish_a = str1->getLink() != nullptr; + CloseTags(str1->htext, finish_a, finish_italic, finish_bold); #if 0 //~ for debugging for (str1 = yxStrings; str1; str1 = str1->yxNext) { @@ -726,270 +699,282 @@ void HtmlPage::coalesce() { } printf("\n------------------------------------------------------------\n\n"); #endif - } -void HtmlPage::dumpAsXML(FILE* f,int page){ - fprintf(f, "<page number=\"%d\" position=\"absolute\"", page); - fprintf(f," top=\"0\" left=\"0\" height=\"%d\" width=\"%d\">\n", pageHeight,pageWidth); - - for(int i=fontsPageMarker;i < fonts->size();i++) { - GooString *fontCSStyle = fonts->CSStyle(i); - fprintf(f,"\t%s\n",fontCSStyle->c_str()); - delete fontCSStyle; - } +void HtmlPage::dumpAsXML(FILE *f, int page) +{ + fprintf(f, "<page number=\"%d\" position=\"absolute\"", page); + fprintf(f, " top=\"0\" left=\"0\" height=\"%d\" width=\"%d\">\n", pageHeight, pageWidth); - for (auto ptr : *imgList) { - auto img = static_cast<HtmlImage *>(ptr); - if (!noRoundedCoordinates) { - fprintf(f, "<image top=\"%d\" left=\"%d\" ", xoutRound(img->yMin), xoutRound(img->xMin)); - fprintf(f, "width=\"%d\" height=\"%d\" ", xoutRound(img->xMax - img->xMin), xoutRound(img->yMax - img->yMin)); + for (int i = fontsPageMarker; i < fonts->size(); i++) { + GooString *fontCSStyle = fonts->CSStyle(i); + fprintf(f, "\t%s\n", fontCSStyle->c_str()); + delete fontCSStyle; } - else { - fprintf(f, "<image top=\"%f\" left=\"%f\" ", img->yMin, img->xMin); - fprintf(f, "width=\"%f\" height=\"%f\" ", img->xMax - img->xMin, img->yMax - img->yMin); + + for (auto ptr : *imgList) { + auto img = static_cast<HtmlImage *>(ptr); + if (!noRoundedCoordinates) { + fprintf(f, "<image top=\"%d\" left=\"%d\" ", xoutRound(img->yMin), xoutRound(img->xMin)); + fprintf(f, "width=\"%d\" height=\"%d\" ", xoutRound(img->xMax - img->xMin), xoutRound(img->yMax - img->yMin)); + } else { + fprintf(f, "<image top=\"%f\" left=\"%f\" ", img->yMin, img->xMin); + fprintf(f, "width=\"%f\" height=\"%f\" ", img->xMax - img->xMin, img->yMax - img->yMin); + } + fprintf(f, "src=\"%s\"/>\n", img->fName->c_str()); + delete img; } - fprintf(f,"src=\"%s\"/>\n",img->fName->c_str()); - delete img; - } - imgList->clear(); + imgList->clear(); - for(HtmlString *tmp=yxStrings;tmp;tmp=tmp->yxNext){ - if (tmp->htext){ - if (!noRoundedCoordinates) { - fprintf(f, "<text top=\"%d\" left=\"%d\" ", xoutRound(tmp->yMin), xoutRound(tmp->xMin)); - fprintf(f, "width=\"%d\" height=\"%d\" ", xoutRound(tmp->xMax - tmp->xMin), xoutRound(tmp->yMax - tmp->yMin)); - } - else { - fprintf(f, "<text top=\"%f\" left=\"%f\" ", tmp->yMin, tmp->xMin); - fprintf(f, "width=\"%f\" height=\"%f\" ", tmp->xMax - tmp->xMin, tmp->yMax - tmp->yMin); - } - fprintf(f,"font=\"%d\">", tmp->fontpos); - fputs(tmp->htext->c_str(),f); - fputs("</text>\n",f); + for (HtmlString *tmp = yxStrings; tmp; tmp = tmp->yxNext) { + if (tmp->htext) { + if (!noRoundedCoordinates) { + fprintf(f, "<text top=\"%d\" left=\"%d\" ", xoutRound(tmp->yMin), xoutRound(tmp->xMin)); + fprintf(f, "width=\"%d\" height=\"%d\" ", xoutRound(tmp->xMax - tmp->xMin), xoutRound(tmp->yMax - tmp->yMin)); + } else { + fprintf(f, "<text top=\"%f\" left=\"%f\" ", tmp->yMin, tmp->xMin); + fprintf(f, "width=\"%f\" height=\"%f\" ", tmp->xMax - tmp->xMin, tmp->yMax - tmp->yMin); + } + fprintf(f, "font=\"%d\">", tmp->fontpos); + fputs(tmp->htext->c_str(), f); + fputs("</text>\n", f); + } } - } - fputs("</page>\n",f); + fputs("</page>\n", f); } static void printCSS(FILE *f) { - // Image flip/flop CSS - // Source: - // http://stackoverflow.com/questions/1309055/cross-browser-way-to-flip-html-image-via-javascript-css - // tested in Chrome, Fx (Linux) and IE9 (W7) - static const char css[] = - "<style type=\"text/css\">" "\n" - "<!--" "\n" - ".xflip {" "\n" - " -moz-transform: scaleX(-1);" "\n" - " -webkit-transform: scaleX(-1);" "\n" - " -o-transform: scaleX(-1);" "\n" - " transform: scaleX(-1);" "\n" - " filter: fliph;" "\n" - "}" "\n" - ".yflip {" "\n" - " -moz-transform: scaleY(-1);" "\n" - " -webkit-transform: scaleY(-1);" "\n" - " -o-transform: scaleY(-1);" "\n" - " transform: scaleY(-1);" "\n" - " filter: flipv;" "\n" - "}" "\n" - ".xyflip {" "\n" - " -moz-transform: scaleX(-1) scaleY(-1);" "\n" - " -webkit-transform: scaleX(-1) scaleY(-1);" "\n" - " -o-transform: scaleX(-1) scaleY(-1);" "\n" - " transform: scaleX(-1) scaleY(-1);" "\n" - " filter: fliph + flipv;" "\n" - "}" "\n" - "-->" "\n" - "</style>" "\n"; - - fwrite( css, sizeof(css)-1, 1, f ); + // Image flip/flop CSS + // Source: + // http://stackoverflow.com/questions/1309055/cross-browser-way-to-flip-html-image-via-javascript-css + // tested in Chrome, Fx (Linux) and IE9 (W7) + static const char css[] = "<style type=\"text/css\">" + "\n" + "<!--" + "\n" + ".xflip {" + "\n" + " -moz-transform: scaleX(-1);" + "\n" + " -webkit-transform: scaleX(-1);" + "\n" + " -o-transform: scaleX(-1);" + "\n" + " transform: scaleX(-1);" + "\n" + " filter: fliph;" + "\n" + "}" + "\n" + ".yflip {" + "\n" + " -moz-transform: scaleY(-1);" + "\n" + " -webkit-transform: scaleY(-1);" + "\n" + " -o-transform: scaleY(-1);" + "\n" + " transform: scaleY(-1);" + "\n" + " filter: flipv;" + "\n" + "}" + "\n" + ".xyflip {" + "\n" + " -moz-transform: scaleX(-1) scaleY(-1);" + "\n" + " -webkit-transform: scaleX(-1) scaleY(-1);" + "\n" + " -o-transform: scaleX(-1) scaleY(-1);" + "\n" + " transform: scaleX(-1) scaleY(-1);" + "\n" + " filter: fliph + flipv;" + "\n" + "}" + "\n" + "-->" + "\n" + "</style>" + "\n"; + + fwrite(css, sizeof(css) - 1, 1, f); } -int HtmlPage::dumpComplexHeaders(FILE * const file, FILE *& pageFile, int page) { +int HtmlPage::dumpComplexHeaders(FILE *const file, FILE *&pageFile, int page) +{ - if( !noframes ) - { - const std::string pgNum = std::to_string(page); - std::string pageFileName(DocName->toStr()); - if (!singleHtml){ + if (!noframes) { + const std::string pgNum = std::to_string(page); + std::string pageFileName(DocName->toStr()); + if (!singleHtml) { pageFileName += '-' + pgNum + ".html"; pageFile = fopen(pageFileName.c_str(), "w"); - } else { + } else { pageFileName += "-html.html"; pageFile = fopen(pageFileName.c_str(), "a"); - } + } - if (!pageFile) { - error(errIO, -1, "Couldn't open html file '{0:t}'", pageFileName.c_str()); - return 1; - } - - if (!singleHtml) - fprintf(pageFile,"%s\n<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"\" xml:lang=\"\">\n<head>\n<title>Page %d</title>\n\n", DOCTYPE, page); - else - fprintf(pageFile,"%s\n<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"\" xml:lang=\"\">\n<head>\n<title>%s</title>\n\n", DOCTYPE, pageFileName.c_str()); - - const std::string htmlEncoding = HtmlOutputDev::mapEncodingToHtml(globalParams->getTextEncodingName()); - if (!singleHtml) - fprintf(pageFile, "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=%s\"/>\n", htmlEncoding.c_str()); - else - fprintf(pageFile, "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=%s\"/>\n <br/>\n", htmlEncoding.c_str()); - } - else - { - pageFile = file; - fprintf(pageFile,"<!-- Page %d -->\n", page); - fprintf(pageFile,"<a name=\"%d\"></a>\n", page); - } - - return 0; -} + if (!pageFile) { + error(errIO, -1, "Couldn't open html file '{0:t}'", pageFileName.c_str()); + return 1; + } -void HtmlPage::dumpComplex(FILE *file, int page, const std::vector<std::string>& backgroundImages) { - FILE* pageFile; + if (!singleHtml) + fprintf(pageFile, "%s\n<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"\" xml:lang=\"\">\n<head>\n<title>Page %d</title>\n\n", DOCTYPE, page); + else + fprintf(pageFile, "%s\n<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"\" xml:lang=\"\">\n<head>\n<title>%s</title>\n\n", DOCTYPE, pageFileName.c_str()); - if( firstPage == -1 ) firstPage = page; - - if (dumpComplexHeaders(file, pageFile, page)) { error(errIO, -1, "Couldn't write headers."); return; } - - fputs("<style type=\"text/css\">\n<!--\n",pageFile); - fputs("\tp {margin: 0; padding: 0;}",pageFile); - for(int i=fontsPageMarker;i!=fonts->size();i++) { - GooString *fontCSStyle; - if (!singleHtml) - fontCSStyle = fonts->CSStyle(i); - else - fontCSStyle = fonts->CSStyle(i,page); - fprintf(pageFile,"\t%s\n",fontCSStyle->c_str()); - delete fontCSStyle; - } - - fputs("-->\n</style>\n",pageFile); - - if( !noframes ) - { - fputs("</head>\n<body bgcolor=\"#A0A0A0\" vlink=\"blue\" link=\"blue\">\n",pageFile); - } - - fprintf(pageFile,"<div id=\"page%d-div\" style=\"position:relative;width:%dpx;height:%dpx;\">\n", - page, pageWidth, pageHeight); - - if(!ignore && (size_t) (page - firstPage) < backgroundImages.size()) - { - fprintf(pageFile, - "<img width=\"%d\" height=\"%d\" src=\"%s\" alt=\"background image\"/>\n", - pageWidth, pageHeight, backgroundImages[page - firstPage].c_str()); - } - - for(HtmlString *tmp1=yxStrings;tmp1;tmp1=tmp1->yxNext){ - if (tmp1->htext){ - fprintf(pageFile, - "<p style=\"position:absolute;top:%dpx;left:%dpx;white-space:nowrap\" class=\"ft", - xoutRound(tmp1->yMin), - xoutRound(tmp1->xMin)); - if (!singleHtml) { - fputc('0', pageFile); - } else { - fprintf(pageFile, "%d", page); - } - fprintf(pageFile,"%d\">", tmp1->fontpos); - fputs(tmp1->htext->c_str(), pageFile); - fputs("</p>\n", pageFile); + const std::string htmlEncoding = HtmlOutputDev::mapEncodingToHtml(globalParams->getTextEncodingName()); + if (!singleHtml) + fprintf(pageFile, "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=%s\"/>\n", htmlEncoding.c_str()); + else + fprintf(pageFile, "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=%s\"/>\n <br/>\n", htmlEncoding.c_str()); + } else { + pageFile = file; + fprintf(pageFile, "<!-- Page %d -->\n", page); + fprintf(pageFile, "<a name=\"%d\"></a>\n", page); } - } - fputs("</div>\n", pageFile); - - if( !noframes ) - { - fputs("</body>\n</html>\n",pageFile); - fclose(pageFile); - } + return 0; } - -void HtmlPage::dump(FILE *f, int pageNum, const std::vector<std::string>& backgroundImages) +void HtmlPage::dumpComplex(FILE *file, int page, const std::vector<std::string> &backgroundImages) { - if (complexMode || singleHtml) - { - if (xml) dumpAsXML(f, pageNum); - if (!xml) dumpComplex(f, pageNum, backgroundImages); - } - else - { - fprintf(f,"<a name=%d></a>",pageNum); - // Loop over the list of image names on this page - for (auto ptr : *imgList) { - auto img = static_cast<HtmlImage *>(ptr); + FILE *pageFile; - // see printCSS() for class names - const char *styles[4] = { "", " class=\"xflip\"", " class=\"yflip\"", " class=\"xyflip\"" }; - int style_index=0; - if (img->xMin > img->xMax) style_index += 1; // xFlip - if (img->yMin > img->yMax) style_index += 2; // yFlip + if (firstPage == -1) + firstPage = page; - fprintf(f,"<img%s src=\"%s\"/><br/>\n",styles[style_index],img->fName->c_str()); - delete img; + if (dumpComplexHeaders(file, pageFile, page)) { + error(errIO, -1, "Couldn't write headers."); + return; } - imgList->clear(); - GooString* str; - for(HtmlString *tmp=yxStrings;tmp;tmp=tmp->yxNext){ - if (tmp->htext){ - str=new GooString(tmp->htext); - fputs(str->c_str(),f); - delete str; - fputs("<br/>\n",f); - } + fputs("<style type=\"text/css\">\n<!--\n", pageFile); + fputs("\tp {margin: 0; padding: 0;}", pageFile); + for (int i = fontsPageMarker; i != fonts->size(); i++) { + GooString *fontCSStyle; + if (!singleHtml) + fontCSStyle = fonts->CSStyle(i); + else + fontCSStyle = fonts->CSStyle(i, page); + fprintf(pageFile, "\t%s\n", fontCSStyle->c_str()); + delete fontCSStyle; } - fputs("<hr/>\n",f); - } -} + fputs("-->\n</style>\n", pageFile); + if (!noframes) { + fputs("</head>\n<body bgcolor=\"#A0A0A0\" vlink=\"blue\" link=\"blue\">\n", pageFile); + } -void HtmlPage::clear() { - HtmlString *p1, *p2; + fprintf(pageFile, "<div id=\"page%d-div\" style=\"position:relative;width:%dpx;height:%dpx;\">\n", page, pageWidth, pageHeight); - if (curStr) { - delete curStr; - curStr = nullptr; - } - for (p1 = yxStrings; p1; p1 = p2) { - p2 = p1->yxNext; - delete p1; - } - yxStrings = nullptr; - xyStrings = nullptr; - yxCur1 = yxCur2 = nullptr; - - if( !noframes ) - { - delete fonts; - fonts=new HtmlFontAccu(); - fontsPageMarker = 0; - } - else - { - fontsPageMarker = fonts->size(); - } + if (!ignore && (size_t)(page - firstPage) < backgroundImages.size()) { + fprintf(pageFile, "<img width=\"%d\" height=\"%d\" src=\"%s\" alt=\"background image\"/>\n", pageWidth, pageHeight, backgroundImages[page - firstPage].c_str()); + } + + for (HtmlString *tmp1 = yxStrings; tmp1; tmp1 = tmp1->yxNext) { + if (tmp1->htext) { + fprintf(pageFile, "<p style=\"position:absolute;top:%dpx;left:%dpx;white-space:nowrap\" class=\"ft", xoutRound(tmp1->yMin), xoutRound(tmp1->xMin)); + if (!singleHtml) { + fputc('0', pageFile); + } else { + fprintf(pageFile, "%d", page); + } + fprintf(pageFile, "%d\">", tmp1->fontpos); + fputs(tmp1->htext->c_str(), pageFile); + fputs("</p>\n", pageFile); + } + } + + fputs("</div>\n", pageFile); + + if (!noframes) { + fputs("</body>\n</html>\n", pageFile); + fclose(pageFile); + } +} + +void HtmlPage::dump(FILE *f, int pageNum, const std::vector<std::string> &backgroundImages) +{ + if (complexMode || singleHtml) { + if (xml) + dumpAsXML(f, pageNum); + if (!xml) + dumpComplex(f, pageNum, backgroundImages); + } else { + fprintf(f, "<a name=%d></a>", pageNum); + // Loop over the list of image names on this page + for (auto ptr : *imgList) { + auto img = static_cast<HtmlImage *>(ptr); + + // see printCSS() for class names + const char *styles[4] = { "", " class=\"xflip\"", " class=\"yflip\"", " class=\"xyflip\"" }; + int style_index = 0; + if (img->xMin > img->xMax) + style_index += 1; // xFlip + if (img->yMin > img->yMax) + style_index += 2; // yFlip + + fprintf(f, "<img%s src=\"%s\"/><br/>\n", styles[style_index], img->fName->c_str()); + delete img; + } + imgList->clear(); + + GooString *str; + for (HtmlString *tmp = yxStrings; tmp; tmp = tmp->yxNext) { + if (tmp->htext) { + str = new GooString(tmp->htext); + fputs(str->c_str(), f); + delete str; + fputs("<br/>\n", f); + } + } + fputs("<hr/>\n", f); + } +} - delete links; - links=new HtmlLinks(); - +void HtmlPage::clear() +{ + HtmlString *p1, *p2; + + if (curStr) { + delete curStr; + curStr = nullptr; + } + for (p1 = yxStrings; p1; p1 = p2) { + p2 = p1->yxNext; + delete p1; + } + yxStrings = nullptr; + xyStrings = nullptr; + yxCur1 = yxCur2 = nullptr; + + if (!noframes) { + delete fonts; + fonts = new HtmlFontAccu(); + fontsPageMarker = 0; + } else { + fontsPageMarker = fonts->size(); + } + delete links; + links = new HtmlLinks(); } -void HtmlPage::setDocName(const char *fname){ - DocName=new GooString(fname); +void HtmlPage::setDocName(const char *fname) +{ + DocName = new GooString(fname); } -void HtmlPage::addImage(GooString *fname, GfxState *state) { - HtmlImage *img = new HtmlImage(fname, state); - imgList->push_back(img); +void HtmlPage::addImage(GooString *fname, GfxState *state) +{ + HtmlImage *img = new HtmlImage(fname, state); + imgList->push_back(img); } //------------------------------------------------------------------------ @@ -1004,11 +989,11 @@ HtmlMetaVar::HtmlMetaVar(const char *_name, const char *_content) HtmlMetaVar::~HtmlMetaVar() { - delete name; - delete content; -} - -GooString* HtmlMetaVar::toString() const + delete name; + delete content; +} + +GooString *HtmlMetaVar::toString() const { GooString *result = new GooString("<meta name=\""); result->append(name); @@ -1022,201 +1007,192 @@ GooString* HtmlMetaVar::toString() const // HtmlOutputDev //------------------------------------------------------------------------ -static const char* HtmlEncodings[][2] = { - {"Latin1", "ISO-8859-1"}, - {nullptr, nullptr} -}; +static const char *HtmlEncodings[][2] = { { "Latin1", "ISO-8859-1" }, { nullptr, nullptr } }; std::string HtmlOutputDev::mapEncodingToHtml(const std::string &encoding) { - for(int i = 0; HtmlEncodings[i][0] != nullptr; i++) - { - if( encoding == HtmlEncodings[i][0] ) - { - return HtmlEncodings[i][1]; + for (int i = 0; HtmlEncodings[i][0] != nullptr; i++) { + if (encoding == HtmlEncodings[i][0]) { + return HtmlEncodings[i][1]; + } } - } - return encoding; + return encoding; } -void HtmlOutputDev::doFrame(int firstPage){ - GooString* fName=new GooString(Docname); - fName->append(".html"); +void HtmlOutputDev::doFrame(int firstPage) +{ + GooString *fName = new GooString(Docname); + fName->append(".html"); + + if (!(fContentsFrame = fopen(fName->c_str(), "w"))) { + error(errIO, -1, "Couldn't open html file '{0:t}'", fName); + delete fName; + return; + } - if (!(fContentsFrame = fopen(fName->c_str(), "w"))){ - error(errIO, -1, "Couldn't open html file '{0:t}'", fName); delete fName; - return; - } - - delete fName; - - const std::string baseName = gbasename(Docname->c_str()); - fputs(DOCTYPE, fContentsFrame); - fputs("\n<html>",fContentsFrame); - fputs("\n<head>",fContentsFrame); - fprintf(fContentsFrame,"\n<title>%s</title>",docTitle->c_str()); - const std::string htmlEncoding = mapEncodingToHtml(globalParams->getTextEncodingName()); - fprintf(fContentsFrame, "\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=%s\"/>\n", htmlEncoding.c_str()); - dumpMetaVars(fContentsFrame); - fprintf(fContentsFrame, "</head>\n"); - fputs("<frameset cols=\"100,*\">\n",fContentsFrame); - fprintf(fContentsFrame,"<frame name=\"links\" src=\"%s_ind.html\"/>\n", baseName.c_str()); - fputs("<frame name=\"contents\" src=",fContentsFrame); - if (complexMode) - fprintf(fContentsFrame,"\"%s-%d.html\"", baseName.c_str(), firstPage); - else - fprintf(fContentsFrame,"\"%ss.html\"", baseName.c_str()); - - fputs("/>\n</frameset>\n</html>\n",fContentsFrame); - - fclose(fContentsFrame); + + const std::string baseName = gbasename(Docname->c_str()); + fputs(DOCTYPE, fContentsFrame); + fputs("\n<html>", fContentsFrame); + fputs("\n<head>", fContentsFrame); + fprintf(fContentsFrame, "\n<title>%s</title>", docTitle->c_str()); + const std::string htmlEncoding = mapEncodingToHtml(globalParams->getTextEncodingName()); + fprintf(fContentsFrame, "\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=%s\"/>\n", htmlEncoding.c_str()); + dumpMetaVars(fContentsFrame); + fprintf(fContentsFrame, "</head>\n"); + fputs("<frameset cols=\"100,*\">\n", fContentsFrame); + fprintf(fContentsFrame, "<frame name=\"links\" src=\"%s_ind.html\"/>\n", baseName.c_str()); + fputs("<frame name=\"contents\" src=", fContentsFrame); + if (complexMode) + fprintf(fContentsFrame, "\"%s-%d.html\"", baseName.c_str(), firstPage); + else + fprintf(fContentsFrame, "\"%ss.html\"", baseName.c_str()); + + fputs("/>\n</frameset>\n</html>\n", fContentsFrame); + + fclose(fContentsFrame); } -HtmlOutputDev::HtmlOutputDev(Catalog *catalogA, const char *fileName, const char *title, - const char *author, const char *keywords, const char *subject, const char *date, - bool rawOrderA, int firstPage, bool outline) +HtmlOutputDev::HtmlOutputDev(Catalog *catalogA, const char *fileName, const char *title, const char *author, const char *keywords, const char *subject, const char *date, bool rawOrderA, int firstPage, bool outline) { - catalog = catalogA; - fContentsFrame = nullptr; - page = nullptr; - docTitle = new GooString(title); - pages = nullptr; - dumpJPEG=true; - //write = true; - rawOrder = rawOrderA; - this->doOutline = outline; - ok = false; - //this->firstPage = firstPage; - //pageNum=firstPage; - // open file - needClose = false; - pages = new HtmlPage(rawOrder); - - glMetaVars = new std::vector<HtmlMetaVar*>(); - glMetaVars->push_back(new HtmlMetaVar("generator", "pdftohtml 0.36")); - if( author ) glMetaVars->push_back(new HtmlMetaVar("author", author)); - if( keywords ) glMetaVars->push_back(new HtmlMetaVar("keywords", keywords)); - if( date ) glMetaVars->push_back(new HtmlMetaVar("date", date)); - if( subject ) glMetaVars->push_back(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) - { - if (!singleHtml) - { - GooString* left=new GooString(fileName); - left->append("_ind.html"); - - doFrame(firstPage); - - if (!(fContentsFrame = fopen(left->c_str(), "w"))) - { - error(errIO, -1, "Couldn't open html file '{0:t}'", left); - delete left; - return; - } - delete left; - fputs(DOCTYPE, fContentsFrame); - fputs("<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"\" xml:lang=\"\">\n<head>\n<title></title>\n</head>\n<body>\n", fContentsFrame); - - if (doOutline) - { - fprintf(fContentsFrame, "<a href=\"%s%s\" target=\"contents\">Outline</a><br/>", - gbasename(Docname->c_str()).c_str(), - complexMode ? "-outline.html" : "s.html#outline"); - } - } - if (!complexMode) - { /* not in complex mode */ - - GooString* right=new GooString(fileName); - right->append("s.html"); - - if (!(page=fopen(right->c_str(),"w"))){ - error(errIO, -1, "Couldn't open html file '{0:t}'", right); - delete right; - return; - } - delete right; - fputs(DOCTYPE, page); - fputs("<html>\n<head>\n<title></title>\n",page); - printCSS(page); - fputs("</head>\n<body>\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(right->c_str(),"w"))){ - error(errIO, -1, "Couldn't open html file '{0:t}'", right); - delete right; - return; - } - delete right; + catalog = catalogA; + fContentsFrame = nullptr; + page = nullptr; + docTitle = new GooString(title); + pages = nullptr; + dumpJPEG = true; + // write = true; + rawOrder = rawOrderA; + this->doOutline = outline; + ok = false; + // this->firstPage = firstPage; + // pageNum=firstPage; + // open file + needClose = false; + pages = new HtmlPage(rawOrder); + + glMetaVars = new std::vector<HtmlMetaVar *>(); + glMetaVars->push_back(new HtmlMetaVar("generator", "pdftohtml 0.36")); + if (author) + glMetaVars->push_back(new HtmlMetaVar("author", author)); + if (keywords) + glMetaVars->push_back(new HtmlMetaVar("keywords", keywords)); + if (date) + glMetaVars->push_back(new HtmlMetaVar("date", date)); + if (subject) + glMetaVars->push_back(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) { + if (!singleHtml) { + GooString *left = new GooString(fileName); + left->append("_ind.html"); + + doFrame(firstPage); + + if (!(fContentsFrame = fopen(left->c_str(), "w"))) { + error(errIO, -1, "Couldn't open html file '{0:t}'", left); + delete left; + return; + } + delete left; + fputs(DOCTYPE, fContentsFrame); + fputs("<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"\" xml:lang=\"\">\n<head>\n<title></title>\n</head>\n<body>\n", fContentsFrame); + + if (doOutline) { + fprintf(fContentsFrame, "<a href=\"%s%s\" target=\"contents\">Outline</a><br/>", gbasename(Docname->c_str()).c_str(), complexMode ? "-outline.html" : "s.html#outline"); + } + } + if (!complexMode) { /* not in complex mode */ + + GooString *right = new GooString(fileName); + right->append("s.html"); + + if (!(page = fopen(right->c_str(), "w"))) { + error(errIO, -1, "Couldn't open html file '{0:t}'", right); + delete right; + return; + } + delete right; + fputs(DOCTYPE, page); + fputs("<html>\n<head>\n<title></title>\n", page); + printCSS(page); + fputs("</head>\n<body>\n", page); + } } - const std::string htmlEncoding = mapEncodingToHtml(globalParams->getTextEncodingName()); - if (xml) - { - fprintf(page, "<?xml version=\"1.0\" encoding=\"%s\"?>\n", htmlEncoding.c_str()); - fputs("<!DOCTYPE pdf2xml SYSTEM \"pdf2xml.dtd\">\n\n", page); - fprintf(page,"<pdf2xml producer=\"%s\" version=\"%s\">\n", PACKAGE_NAME, PACKAGE_VERSION); - } - else - { - fprintf(page,"%s\n<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"\" xml:lang=\"\">\n<head>\n<title>%s</title>\n", DOCTYPE, docTitle->c_str()); - - fprintf(page, "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=%s\"/>\n", htmlEncoding.c_str()); - - dumpMetaVars(page); - printCSS(page); - fprintf(page,"</head>\n"); - fprintf(page,"<body bgcolor=\"#A0A0A0\" vlink=\"blue\" link=\"blue\">\n"); + 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(right->c_str(), "w"))) { + error(errIO, -1, "Couldn't open html file '{0:t}'", right); + delete right; + return; + } + delete right; + } + + const std::string htmlEncoding = mapEncodingToHtml(globalParams->getTextEncodingName()); + if (xml) { + fprintf(page, "<?xml version=\"1.0\" encoding=\"%s\"?>\n", htmlEncoding.c_str()); + fputs("<!DOCTYPE pdf2xml SYSTEM \"pdf2xml.dtd\">\n\n", page); + fprintf(page, "<pdf2xml producer=\"%s\" version=\"%s\">\n", PACKAGE_NAME, PACKAGE_VERSION); + } else { + fprintf(page, "%s\n<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"\" xml:lang=\"\">\n<head>\n<title>%s</title>\n", DOCTYPE, docTitle->c_str()); + + fprintf(page, "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=%s\"/>\n", htmlEncoding.c_str()); + + dumpMetaVars(page); + printCSS(page); + fprintf(page, "</head>\n"); + fprintf(page, "<body bgcolor=\"#A0A0A0\" vlink=\"blue\" link=\"blue\">\n"); + } } - } - ok = true; + ok = true; } -HtmlOutputDev::~HtmlOutputDev() { +HtmlOutputDev::~HtmlOutputDev() +{ delete Docname; delete docTitle; for (auto entry : *glMetaVars) { - delete entry; + delete entry; } delete glMetaVars; - if (fContentsFrame){ - fputs("</body>\n</html>\n",fContentsFrame); - fclose(fContentsFrame); + if (fContentsFrame) { + fputs("</body>\n</html>\n", fContentsFrame); + fclose(fContentsFrame); } if (page != nullptr) { - if (xml) { - fputs("</pdf2xml>\n",page); - fclose(page); - } else - if ( !complexMode || xml || noframes ) - { - fputs("</body>\n</html>\n",page); - fclose(page); - } + if (xml) { + fputs("</pdf2xml>\n", page); + fclose(page); + } else if (!complexMode || xml || noframes) { + fputs("</body>\n</html>\n", page); + fclose(page); + } } if (pages) - delete pages; + delete pages; } -void HtmlOutputDev::startPage(int pageNumA, GfxState *state, XRef *xref) { +void HtmlOutputDev::startPage(int pageNumA, GfxState *state, XRef *xref) +{ #if 0 if (mode&&!xml){ if (write){ @@ -1236,610 +1212,568 @@ void HtmlOutputDev::startPage(int pageNumA, GfxState *state, XRef *xref) { } #endif - pageNum = pageNumA; - const std::string str = gbasename(Docname->c_str()); - pages->clear(); - if(!noframes) - { - if (fContentsFrame) - { - if (complexMode) - fprintf(fContentsFrame,"<a href=\"%s-%d.html\"", str.c_str(), pageNum); - else - fprintf(fContentsFrame,"<a href=\"%ss.html#%d\"", str.c_str(), pageNum); - fprintf(fContentsFrame," target=\"contents\" >Page %d</a><br/>\n",pageNum); + pageNum = pageNumA; + const std::string str = gbasename(Docname->c_str()); + pages->clear(); + if (!noframes) { + if (fContentsFrame) { + if (complexMode) + fprintf(fContentsFrame, "<a href=\"%s-%d.html\"", str.c_str(), pageNum); + else + fprintf(fContentsFrame, "<a href=\"%ss.html#%d\"", str.c_str(), pageNum); + fprintf(fContentsFrame, " target=\"contents\" >Page %d</a><br/>\n", pageNum); + } } - } - pages->pageWidth=static_cast<int>(state->getPageWidth()); - pages->pageHeight=static_cast<int>(state->getPageHeight()); -} + pages->pageWidth = static_cast<int>(state->getPageWidth()); + pages->pageHeight = static_cast<int>(state->getPageHeight()); +} + +void HtmlOutputDev::endPage() +{ + Links *linksList = docPage->getLinks(); + for (int i = 0; i < linksList->getNumLinks(); ++i) { + doProcessLink(linksList->getLink(i)); + } + delete linksList; + pages->conv(); + pages->coalesce(); + pages->dump(page, pageNum, backgroundImages); -void HtmlOutputDev::endPage() { - Links *linksList = docPage->getLinks(); - for (int i = 0; i < linksList->getNumLinks(); ++i) - { - doProcessLink(linksList->getLink(i)); - } - delete linksList; + // 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; - pages->conv(); - pages->coalesce(); - pages->dump(page, pageNum, backgroundImages); - - // 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("<br/>\n", fContentsFrame); - if(!stout && !globalParams->getErrQuiet()) printf("Page-%d\n",(pageNum)); + // if(!noframes&&!xml) fputs("<br/>\n", fContentsFrame); + if (!stout && !globalParams->getErrQuiet()) + printf("Page-%d\n", (pageNum)); } -void HtmlOutputDev::addBackgroundImage(const std::string& img) { - backgroundImages.push_back(img); +void HtmlOutputDev::addBackgroundImage(const std::string &img) +{ + backgroundImages.push_back(img); } -void HtmlOutputDev::updateFont(GfxState *state) { - pages->updateFont(state); +void HtmlOutputDev::updateFont(GfxState *state) +{ + pages->updateFont(state); } -void HtmlOutputDev::beginString(GfxState *state, const GooString *s) { - pages->beginString(state, s); +void HtmlOutputDev::beginString(GfxState *state, const GooString *s) +{ + pages->beginString(state, s); } -void HtmlOutputDev::endString(GfxState *state) { - pages->endString(); +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*/, const Unicode *u, int uLen) +void HtmlOutputDev::drawChar(GfxState *state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int /*nBytes*/, const Unicode *u, int uLen) { - if ( !showHidden && (state->getRender() & 3) == 3) { - return; - } - pages->addChar(state, x, y, dx, dy, originX, originY, u, uLen); + if (!showHidden && (state->getRender() & 3) == 3) { + return; + } + pages->addChar(state, x, y, dx, dy, originX, originY, u, uLen); } void HtmlOutputDev::drawJpegImage(GfxState *state, Stream *str) { - InMemoryFile ims; - FILE *f1 = nullptr; - int c; - - // open the image file - GooString *fName = createImageFileName("jpg"); - f1 = dataUrls ? ims.open("wb") : fopen(fName->c_str(), "wb"); - if (!f1) { - error(errIO, -1, "Couldn't open image file '{0:t}'", fName); - delete fName; - return; - } + InMemoryFile ims; + FILE *f1 = nullptr; + int c; + + // open the image file + GooString *fName = createImageFileName("jpg"); + f1 = dataUrls ? ims.open("wb") : fopen(fName->c_str(), "wb"); + if (!f1) { + error(errIO, -1, "Couldn't open image file '{0:t}'", fName); + delete fName; + return; + } - // initialize stream - str = str->getNextStream(); - str->reset(); + // initialize stream + str = str->getNextStream(); + str->reset(); - // copy the stream - while ((c = str->getChar()) != EOF) - fputc(c, f1); + // copy the stream + while ((c = str->getChar()) != EOF) + fputc(c, f1); - fclose(f1); + fclose(f1); - if (dataUrls) { - delete fName; - fName = new GooString(std::string("data:image/jpeg;base64,") + gbase64Encode(ims.getBuffer())); - } - pages->addImage(fName, state); + if (dataUrls) { + delete fName; + fName = new GooString(std::string("data:image/jpeg;base64,") + gbase64Encode(ims.getBuffer())); + } + pages->addImage(fName, state); } -void HtmlOutputDev::drawPngImage(GfxState *state, Stream *str, int width, int height, - GfxImageColorMap *colorMap, bool isMask) +void HtmlOutputDev::drawPngImage(GfxState *state, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool isMask) { #ifdef ENABLE_LIBPNG - FILE *f1; - InMemoryFile ims; - - if (!colorMap && !isMask) { - error(errInternal, -1, "Can't have color image without a color map"); - return; - } - - // open the image file - GooString *fName=createImageFileName("png"); - f1 = dataUrls ? ims.open("wb") : fopen(fName->c_str(), "wb"); - if (!f1) { - error(errIO, -1, "Couldn't open image file '{0:t}'", fName); - delete fName; - return; - } + FILE *f1; + InMemoryFile ims; - PNGWriter *writer = new PNGWriter( isMask ? PNGWriter::MONOCHROME : PNGWriter::RGB ); - // TODO can we calculate the resolution of the image? - if (!writer->init(f1, width, height, 72, 72)) { - error(errInternal, -1, "Can't init PNG for image '{0:t}'", fName); - delete writer; - fclose(f1); - return; - } - - if (!isMask) { - unsigned char *p; - GfxRGB rgb; - png_byte *row = (png_byte *) gmalloc(3 * width); // 3 bytes/pixel: RGB - png_bytep *row_pointer= &row; - - // Initialize the image stream - ImageStream *imgStr = new ImageStream(str, width, - colorMap->getNumPixelComps(), colorMap->getBits()); - imgStr->reset(); - - // For each line... - for (int y = 0; y < height; y++) { + if (!colorMap && !isMask) { + error(errInternal, -1, "Can't have color image without a color map"); + return; + } - // Convert into a PNG row - p = imgStr->getLine(); - if (!p) { - error(errIO, -1, "Failed to read PNG. '{0:t}' will be incorrect", fName); + // open the image file + GooString *fName = createImageFileName("png"); + f1 = dataUrls ? ims.open("wb") : fopen(fName->c_str(), "wb"); + if (!f1) { + error(errIO, -1, "Couldn't open image file '{0:t}'", fName); delete fName; - gfree(row); - delete writer; - delete imgStr; - fclose(f1); return; - } - for (int x = 0; x < width; x++) { - colorMap->getRGB(p, &rgb); - // Write the RGB pixels into the row - row[3*x]= colToByte(rgb.r); - row[3*x+1]= colToByte(rgb.g); - row[3*x+2]= colToByte(rgb.b); - p += colorMap->getNumPixelComps(); - } + } - if (!writer->writeRow(row_pointer)) { - error(errIO, -1, "Failed to write into PNG '{0:t}'", fName); + PNGWriter *writer = new PNGWriter(isMask ? PNGWriter::MONOCHROME : PNGWriter::RGB); + // TODO can we calculate the resolution of the image? + if (!writer->init(f1, width, height, 72, 72)) { + error(errInternal, -1, "Can't init PNG for image '{0:t}'", fName); delete writer; - delete imgStr; fclose(f1); return; - } - } - gfree(row); - imgStr->close(); - delete imgStr; - } - else { // isMask == true - int size = (width + 7)/8; - - // PDF masks use 0 = draw current color, 1 = leave unchanged. - // We invert this to provide the standard interpretation of alpha - // (0 = transparent, 1 = opaque). If the colorMap already inverts - // the mask we leave the data unchanged. - int invert_bits = 0xff; - if (colorMap) { - GfxGray gray; - unsigned char zero[gfxColorMaxComps]; - memset(zero, 0, sizeof(zero)); - colorMap->getGray(zero, &gray); - if (colToByte(gray) == 0) - invert_bits = 0x00; } - str->reset(); - unsigned char *png_row = (unsigned char *)gmalloc(size); + if (!isMask) { + unsigned char *p; + GfxRGB rgb; + png_byte *row = (png_byte *)gmalloc(3 * width); // 3 bytes/pixel: RGB + png_bytep *row_pointer = &row; + + // Initialize the image stream + ImageStream *imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), colorMap->getBits()); + imgStr->reset(); + + // For each line... + for (int y = 0; y < height; y++) { + + // Convert into a PNG row + p = imgStr->getLine(); + if (!p) { + error(errIO, -1, "Failed to read PNG. '{0:t}' will be incorrect", fName); + delete fName; + gfree(row); + delete writer; + delete imgStr; + fclose(f1); + return; + } + for (int x = 0; x < width; x++) { + colorMap->getRGB(p, &rgb); + // Write the RGB pixels into the row + row[3 * x] = colToByte(rgb.r); + row[3 * x + 1] = colToByte(rgb.g); + row[3 * x + 2] = colToByte(rgb.b); + p += colorMap->getNumPixelComps(); + } + + if (!writer->writeRow(row_pointer)) { + error(errIO, -1, "Failed to write into PNG '{0:t}'", fName); + delete writer; + delete imgStr; + fclose(f1); + return; + } + } + gfree(row); + imgStr->close(); + delete imgStr; + } else { // isMask == true + int size = (width + 7) / 8; + + // PDF masks use 0 = draw current color, 1 = leave unchanged. + // We invert this to provide the standard interpretation of alpha + // (0 = transparent, 1 = opaque). If the colorMap already inverts + // the mask we leave the data unchanged. + int invert_bits = 0xff; + if (colorMap) { + GfxGray gray; + unsigned char zero[gfxColorMaxComps]; + memset(zero, 0, sizeof(zero)); + colorMap->getGray(zero, &gray); + if (colToByte(gray) == 0) + invert_bits = 0x00; + } - for (int ri = 0; ri < height; ++ri) - { - for(int i = 0; i < size; i++) - png_row[i] = str->getChar() ^ invert_bits; + str->reset(); + unsigned char *png_row = (unsigned char *)gmalloc(size); - if (!writer->writeRow( &png_row )) - { - error(errIO, -1, "Failed to write into PNG '{0:t}'", fName); - delete writer; - fclose(f1); + for (int ri = 0; ri < height; ++ri) { + for (int i = 0; i < size; i++) + png_row[i] = str->getChar() ^ invert_bits; + + if (!writer->writeRow(&png_row)) { + error(errIO, -1, "Failed to write into PNG '{0:t}'", fName); + delete writer; + fclose(f1); + gfree(png_row); + return; + } + } + str->close(); gfree(png_row); - return; - } } - str->close(); - gfree(png_row); - } - str->close(); + str->close(); - writer->close(); - delete writer; - fclose(f1); + writer->close(); + delete writer; + fclose(f1); - if (dataUrls) { - delete fName; - fName = new GooString(std::string("data:image/png;base64,") + gbase64Encode(ims.getBuffer())); - } - pages->addImage(fName, state); + if (dataUrls) { + delete fName; + fName = new GooString(std::string("data:image/png;base64,") + gbase64Encode(ims.getBuffer())); + } + pages->addImage(fName, state); #else - return; + return; #endif } GooString *HtmlOutputDev::createImageFileName(const char *ext) { - return GooString::format("{0:s}-{1:d}_{2:d}.{3:s}", Docname->c_str(), pageNum, pages->getNumImages() + 1, ext); + return GooString::format("{0:s}-{1:d}_{2:d}.{3:s}", Docname->c_str(), pageNum, pages->getNumImages() + 1, ext); } -void HtmlOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, - int width, int height, bool invert, - bool interpolate, bool inlineImg) { +void HtmlOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, bool invert, bool interpolate, bool inlineImg) +{ - if (ignore||(complexMode && !xml)) { - OutputDev::drawImageMask(state, ref, str, width, height, invert, interpolate, inlineImg); - return; - } - - // dump JPEG file - if (dumpJPEG && str->getKind() == strDCT) { - drawJpegImage(state, str); - } - else { + if (ignore || (complexMode && !xml)) { + OutputDev::drawImageMask(state, ref, str, width, height, invert, interpolate, inlineImg); + return; + } + + // dump JPEG file + if (dumpJPEG && str->getKind() == strDCT) { + drawJpegImage(state, str); + } else { #ifdef ENABLE_LIBPNG - drawPngImage(state, str, width, height, nullptr, true); + drawPngImage(state, str, width, height, nullptr, true); #else - OutputDev::drawImageMask(state, ref, str, width, height, invert, interpolate, inlineImg); + OutputDev::drawImageMask(state, ref, str, width, height, invert, interpolate, inlineImg); #endif - } + } } -void HtmlOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, - int width, int height, GfxImageColorMap *colorMap, - bool interpolate, const int *maskColors, bool inlineImg) { +void HtmlOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool interpolate, const int *maskColors, bool inlineImg) +{ - if (ignore||(complexMode && !xml)) { - OutputDev::drawImage(state, ref, str, width, height, colorMap, interpolate, - maskColors, inlineImg); - return; - } - - /*if( !globalParams->getErrQuiet() ) - printf("image stream of kind %d\n", str->getKind());*/ - // dump JPEG file - if (dumpJPEG && str->getKind() == strDCT && (colorMap->getNumPixelComps() == 1 || - colorMap->getNumPixelComps() == 3) && !inlineImg) { - drawJpegImage(state, str); - } - else { + if (ignore || (complexMode && !xml)) { + OutputDev::drawImage(state, ref, str, width, height, colorMap, interpolate, maskColors, inlineImg); + return; + } + + /*if( !globalParams->getErrQuiet() ) + printf("image stream of kind %d\n", str->getKind());*/ + // dump JPEG file + if (dumpJPEG && str->getKind() == strDCT && (colorMap->getNumPixelComps() == 1 || colorMap->getNumPixelComps() == 3) && !inlineImg) { + drawJpegImage(state, str); + } else { #ifdef ENABLE_LIBPNG - drawPngImage(state, str, width, height, colorMap ); + drawPngImage(state, str, width, height, colorMap); #else - OutputDev::drawImage(state, ref, str, width, height, colorMap, interpolate, - maskColors, inlineImg); + OutputDev::drawImage(state, ref, str, width, height, colorMap, interpolate, maskColors, inlineImg); #endif - } + } } +void HtmlOutputDev::doProcessLink(AnnotLink *link) +{ + double _x1, _y1, _x2, _y2; + int x1, y1, x2, y2; + link->getRect(&_x1, &_y1, &_x2, &_y2); + cvtUserToDev(_x1, _y1, &x1, &y1); -void HtmlOutputDev::doProcessLink(AnnotLink* 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); - + cvtUserToDev(_x2, _y2, &x2, &y2); - GooString* _dest=getLinkDest(link); - HtmlLink t((double) x1,(double) y2,(double) x2,(double) y1,_dest); - pages->AddLink(t); - delete _dest; + GooString *_dest = getLinkDest(link); + HtmlLink t((double)x1, (double)y2, (double)x2, (double)y1, _dest); + pages->AddLink(t); + delete _dest; } -GooString* HtmlOutputDev::getLinkDest(AnnotLink *link){ - if (!link->getAction()) - return new GooString(); - switch(link->getAction()->getKind()) - { - case actionGoTo: - { - int destPage=1; - LinkGoTo *ha=(LinkGoTo *)link->getAction(); - std::unique_ptr<LinkDest> dest; - if (ha->getDest()!=nullptr) - dest=std::unique_ptr<LinkDest>(ha->getDest()->copy()); - else if (ha->getNamedDest()!=nullptr) - dest=catalog->findDest(ha->getNamedDest()); - - if (dest){ - GooString* file = new GooString(gbasename(Docname->c_str())); - - if (dest->isPageRef()){ - const Ref pageref=dest->getPageRef(); - destPage=catalog->findPage(pageref); - } - else { - destPage=dest->getPageNum(); - } - - /* complex simple - frames file-4.html files.html#4 - noframes file.html#4 file.html#4 - */ - if (noframes) - { - file->append(".html#"); - file->append(std::to_string(destPage)); - } - else - { - if( complexMode ) - { - file->append("-"); - file->append(std::to_string(destPage)); - file->append(".html"); - } - else - { - file->append("s.html#"); - file->append(std::to_string(destPage)); - } - } - - if (printCommands) printf(" link to page %d ",destPage); - return file; - } - else - { - return new GooString(); - } - } - case actionGoToR: - { - LinkGoToR *ha=(LinkGoToR *) link->getAction(); - LinkDest *dest=nullptr; - int destPage=1; - GooString *file=new GooString(); - if (ha->getFileName()){ - delete file; - file=new GooString(ha->getFileName()->c_str()); - } - if (ha->getDest()!=nullptr) dest=ha->getDest()->copy(); - if (dest&&file){ - if (!(dest->isPageRef())) destPage=dest->getPageNum(); - delete dest; - - if (printCommands) printf(" link to page %d ",destPage); - if (printHtml){ - const char *p=file->c_str()+file->getLength()-4; - if (!strcmp(p, ".pdf") || !strcmp(p, ".PDF")){ - file->del(file->getLength()-4,4); - file->append(".html"); - } - file->append('#'); - file->append(std::to_string(destPage)); - } - } - if (printCommands && file) printf("filename %s\n",file->c_str()); - return file; - } - case actionURI: - { - LinkURI *ha=(LinkURI *) link->getAction(); - GooString* file=new GooString(ha->getURI()); - // printf("uri : %s\n",file->c_str()); - return file; - } - case actionLaunch: - if (printHtml) { - LinkLaunch *ha=(LinkLaunch *) link->getAction(); - GooString* file=new GooString(ha->getFileName()->c_str()); - const char *p=file->c_str()+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->c_str()); - - return file; - - } - // fallthrough - default: - return new GooString(); - } +GooString *HtmlOutputDev::getLinkDest(AnnotLink *link) +{ + if (!link->getAction()) + return new GooString(); + switch (link->getAction()->getKind()) { + case actionGoTo: { + int destPage = 1; + LinkGoTo *ha = (LinkGoTo *)link->getAction(); + std::unique_ptr<LinkDest> dest; + if (ha->getDest() != nullptr) + dest = std::unique_ptr<LinkDest>(ha->getDest()->copy()); + else if (ha->getNamedDest() != nullptr) + dest = catalog->findDest(ha->getNamedDest()); + + if (dest) { + GooString *file = new GooString(gbasename(Docname->c_str())); + + if (dest->isPageRef()) { + const Ref pageref = dest->getPageRef(); + destPage = catalog->findPage(pageref); + } else { + destPage = dest->getPageNum(); + } + + /* complex simple + frames file-4.html files.html#4 + noframes file.html#4 file.html#4 + */ + if (noframes) { + file->append(".html#"); + file->append(std::to_string(destPage)); + } else { + if (complexMode) { + file->append("-"); + file->append(std::to_string(destPage)); + file->append(".html"); + } else { + file->append("s.html#"); + file->append(std::to_string(destPage)); + } + } + + if (printCommands) + printf(" link to page %d ", destPage); + return file; + } else { + return new GooString(); + } + } + case actionGoToR: { + LinkGoToR *ha = (LinkGoToR *)link->getAction(); + LinkDest *dest = nullptr; + int destPage = 1; + GooString *file = new GooString(); + if (ha->getFileName()) { + delete file; + file = new GooString(ha->getFileName()->c_str()); + } + if (ha->getDest() != nullptr) + dest = ha->getDest()->copy(); + if (dest && file) { + if (!(dest->isPageRef())) + destPage = dest->getPageNum(); + delete dest; + + if (printCommands) + printf(" link to page %d ", destPage); + if (printHtml) { + const char *p = file->c_str() + file->getLength() - 4; + if (!strcmp(p, ".pdf") || !strcmp(p, ".PDF")) { + file->del(file->getLength() - 4, 4); + file->append(".html"); + } + file->append('#'); + file->append(std::to_string(destPage)); + } + } + if (printCommands && file) + printf("filename %s\n", file->c_str()); + return file; + } + case actionURI: { + LinkURI *ha = (LinkURI *)link->getAction(); + GooString *file = new GooString(ha->getURI()); + // printf("uri : %s\n",file->c_str()); + return file; + } + case actionLaunch: + if (printHtml) { + LinkLaunch *ha = (LinkLaunch *)link->getAction(); + GooString *file = new GooString(ha->getFileName()->c_str()); + const char *p = file->c_str() + 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->c_str()); + + return file; + } + // fallthrough + default: + return new GooString(); + } } void HtmlOutputDev::dumpMetaVars(FILE *file) { - GooString *var; + GooString *var; - for(const HtmlMetaVar *t : *glMetaVars) - { - var = t->toString(); - fprintf(file, "%s\n", var->c_str()); - delete var; - } + for (const HtmlMetaVar *t : *glMetaVars) { + var = t->toString(); + fprintf(file, "%s\n", var->c_str()); + delete var; + } } -bool HtmlOutputDev::dumpDocOutline(PDFDoc* doc) -{ - FILE * output = nullptr; - bool bClose = false; - - if (!ok) +bool HtmlOutputDev::dumpDocOutline(PDFDoc *doc) +{ + FILE *output = nullptr; + bool bClose = false; + + if (!ok) + return false; + + Outline *outline = doc->getOutline(); + if (!outline) + return false; + + const std::vector<OutlineItem *> *outlines = outline->getItems(); + if (!outlines) + return false; + + if (!complexMode || xml) { + output = page; + } else if (complexMode && !xml) { + if (noframes) { + output = page; + fputs("<hr/>\n", output); + } else { + GooString *str = Docname->copy(); + str->append("-outline.html"); + output = fopen(str->c_str(), "w"); + delete str; + if (output == nullptr) return false; - - Outline *outline = doc->getOutline(); - if (!outline) - return false; + bClose = true; + + const std::string htmlEncoding = HtmlOutputDev::mapEncodingToHtml(globalParams->getTextEncodingName()); + + fprintf(output, + "<html xmlns=\"http://www.w3.org/1999/xhtml\" " + "lang=\"\" xml:lang=\"\">\n" + "<head>\n" + "<title>Document Outline</title>\n" + "<meta http-equiv=\"Content-Type\" content=\"text/html; " + "charset=%s\"/>\n" + "</head>\n<body>\n", + htmlEncoding.c_str()); + } + } - const std::vector<OutlineItem*> *outlines = outline->getItems(); - if (!outlines) - return false; - - if (!complexMode || xml) - { - output = page; - } - else if (complexMode && !xml) - { - if (noframes) - { - output = page; - fputs("<hr/>\n", output); - } - else - { - GooString *str = Docname->copy(); - str->append("-outline.html"); - output = fopen(str->c_str(), "w"); - delete str; - if (output == nullptr) - return false; - bClose = true; - - const std::string htmlEncoding = - HtmlOutputDev::mapEncodingToHtml(globalParams->getTextEncodingName()); - - fprintf(output, "<html xmlns=\"http://www.w3.org/1999/xhtml\" " \ - "lang=\"\" xml:lang=\"\">\n" \ - "<head>\n" \ - "<title>Document Outline</title>\n" \ - "<meta http-equiv=\"Content-Type\" content=\"text/html; " \ - "charset=%s\"/>\n" \ - "</head>\n<body>\n", htmlEncoding.c_str()); - } - } - - if (!xml) - { - bool done = newHtmlOutlineLevel(output, outlines); - if (done && !complexMode) - fputs("<hr/>\n", output); - - if (bClose) - { - fputs("</body>\n</html>\n", output); - fclose(output); - } - } - else - newXmlOutlineLevel(output, outlines); - - return true; + if (!xml) { + bool done = newHtmlOutlineLevel(output, outlines); + if (done && !complexMode) + fputs("<hr/>\n", output); + + if (bClose) { + fputs("</body>\n</html>\n", output); + fclose(output); + } + } else + newXmlOutlineLevel(output, outlines); + + return true; } -bool HtmlOutputDev::newHtmlOutlineLevel(FILE *output, const std::vector<OutlineItem*> *outlines, int level) +bool HtmlOutputDev::newHtmlOutlineLevel(FILE *output, const std::vector<OutlineItem *> *outlines, int level) { - bool atLeastOne = false; + bool atLeastOne = false; - if (level == 1) - { - fputs("<a name=\"outline\"></a>", output); - fputs("<h1>Document Outline</h1>\n", output); - } - fputs("<ul>\n",output); + if (level == 1) { + fputs("<a name=\"outline\"></a>", output); + fputs("<h1>Document Outline</h1>\n", output); + } + fputs("<ul>\n", output); - for (OutlineItem *item : *outlines) - { - GooString *titleStr = HtmlFont::HtmlFilter(item->getTitle(), - item->getTitleLength()); + for (OutlineItem *item : *outlines) { + GooString *titleStr = HtmlFont::HtmlFilter(item->getTitle(), item->getTitleLength()); - GooString *linkName = nullptr;; + GooString *linkName = nullptr; + ; const int itemPage = getOutlinePageNum(item); - if (itemPage > 0) - { - /* complex simple - frames file-4.html files.html#4 - noframes file.html#4 file.html#4 - */ - linkName = new GooString(gbasename(Docname->c_str())); - if (noframes) { - linkName->append(".html#"); - linkName->append(std::to_string(itemPage)); - } else { - if( complexMode ) { - linkName->append("-"); - linkName->append(std::to_string(itemPage)); - linkName->append(".html"); - } else { - linkName->append("s.html#"); - linkName->append(std::to_string(itemPage)); - } - } - } - - fputs("<li>",output); - if (linkName) - fprintf(output,"<a href=\"%s\">", linkName->c_str()); - fputs(titleStr->c_str(),output); - if (linkName) { - fputs("</a>",output); - delete linkName; - } - delete titleStr; - atLeastOne = true; - - item->open(); - if (item->hasKids() && item->getKids()) - { - fputs("\n",output); - newHtmlOutlineLevel(output, item->getKids(), level+1); - } - item->close(); - fputs("</li>\n",output); - } - fputs("</ul>\n",output); - - return atLeastOne; + if (itemPage > 0) { + /* complex simple + frames file-4.html files.html#4 + noframes file.html#4 file.html#4 + */ + linkName = new GooString(gbasename(Docname->c_str())); + if (noframes) { + linkName->append(".html#"); + linkName->append(std::to_string(itemPage)); + } else { + if (complexMode) { + linkName->append("-"); + linkName->append(std::to_string(itemPage)); + linkName->append(".html"); + } else { + linkName->append("s.html#"); + linkName->append(std::to_string(itemPage)); + } + } + } + + fputs("<li>", output); + if (linkName) + fprintf(output, "<a href=\"%s\">", linkName->c_str()); + fputs(titleStr->c_str(), output); + if (linkName) { + fputs("</a>", output); + delete linkName; + } + delete titleStr; + atLeastOne = true; + + item->open(); + if (item->hasKids() && item->getKids()) { + fputs("\n", output); + newHtmlOutlineLevel(output, item->getKids(), level + 1); + } + item->close(); + fputs("</li>\n", output); + } + fputs("</ul>\n", output); + + return atLeastOne; } -void HtmlOutputDev::newXmlOutlineLevel(FILE *output, const std::vector<OutlineItem*> *outlines) +void HtmlOutputDev::newXmlOutlineLevel(FILE *output, const std::vector<OutlineItem *> *outlines) { fputs("<outline>\n", output); - for (OutlineItem *item : *outlines) - { - GooString *titleStr = HtmlFont::HtmlFilter(item->getTitle(), - item->getTitleLength()); + for (OutlineItem *item : *outlines) { + GooString *titleStr = HtmlFont::HtmlFilter(item->getTitle(), item->getTitleLength()); const int itemPage = getOutlinePageNum(item); - if (itemPage > 0) - { - fprintf(output, "<item page=\"%d\">%s</item>\n", - itemPage, titleStr->c_str()); - } - else - { + if (itemPage > 0) { + fprintf(output, "<item page=\"%d\">%s</item>\n", itemPage, titleStr->c_str()); + } else { fprintf(output, "<item>%s</item>\n", titleStr->c_str()); } delete titleStr; item->open(); - if (item->hasKids() && item->getKids()) - { + if (item->hasKids() && item->getKids()) { newXmlOutlineLevel(output, item->getKids()); } item->close(); - } + } fputs("</outline>\n", output); } int HtmlOutputDev::getOutlinePageNum(OutlineItem *item) { - const LinkAction *action = item->getAction(); - const LinkGoTo *link = nullptr; + const LinkAction *action = item->getAction(); + const LinkGoTo *link = nullptr; std::unique_ptr<LinkDest> linkdest; - int pagenum = -1; + int pagenum = -1; if (!action || action->getKind() != actionGoTo) return pagenum; - link = static_cast<const LinkGoTo*>(action); + link = static_cast<const LinkGoTo *>(action); if (!link || !link->isOk()) return pagenum; diff --git a/utils/HtmlOutputDev.h b/utils/HtmlOutputDev.h index b724ec18..8ad8ebf1 100644 --- a/utils/HtmlOutputDev.h +++ b/utils/HtmlOutputDev.h @@ -58,160 +58,150 @@ class OutlineItem; // HtmlString //------------------------------------------------------------------------ -enum UnicodeTextDirection { - textDirUnknown, - textDirLeftRight, - textDirRightLeft, - textDirTopBottom +enum UnicodeTextDirection +{ + textDirUnknown, + textDirLeftRight, + textDirRightLeft, + textDirTopBottom }; -class HtmlString { +class HtmlString +{ public: + // Constructor. + HtmlString(GfxState *state, double fontSize, HtmlFontAccu *fonts); - // Constructor. - HtmlString(GfxState *state, double fontSize, HtmlFontAccu* fonts); + // Destructor. + ~HtmlString(); - // Destructor. - ~HtmlString(); + HtmlString(const HtmlString &) = delete; + HtmlString &operator=(const HtmlString &) = delete; - HtmlString(const HtmlString &) = delete; - HtmlString& operator=(const HtmlString &) = delete; - - // Add a character to the string. - void addChar(GfxState *state, double x, double y, - double dx, double dy, - Unicode u); - HtmlLink* getLink() { return link; } - const HtmlFont &getFont() const { return *fonts->Get(fontpos); } - void endString(); // postprocessing + // Add a character to the string. + void addChar(GfxState *state, double x, double y, double dx, double dy, Unicode u); + HtmlLink *getLink() { return link; } + const HtmlFont &getFont() const { return *fonts->Get(fontpos); } + void endString(); // postprocessing private: -// aender die text variable - HtmlLink *link; - double xMin, xMax; // bounding box x coordinates - double yMin, yMax; // bounding box y coordinates - int col; // starting column - Unicode *text; // the text - double *xRight; // right-hand x coord of each char - HtmlString *yxNext; // next string in y-major order - HtmlString *xyNext; // next string in x-major order - int fontpos; - GooString* htext; - int len; // length of text and xRight - int size; // size of text and xRight arrays - UnicodeTextDirection dir; // direction (left to right/right to left) - HtmlFontAccu *fonts; - - friend class HtmlPage; - + // aender die text variable + HtmlLink *link; + double xMin, xMax; // bounding box x coordinates + double yMin, yMax; // bounding box y coordinates + int col; // starting column + Unicode *text; // the text + double *xRight; // right-hand x coord of each char + HtmlString *yxNext; // next string in y-major order + HtmlString *xyNext; // next string in x-major order + int fontpos; + GooString *htext; + int len; // length of text and xRight + int size; // size of text and xRight arrays + UnicodeTextDirection dir; // direction (left to right/right to left) + HtmlFontAccu *fonts; + + friend class HtmlPage; }; - //------------------------------------------------------------------------ // HtmlPage //------------------------------------------------------------------------ - - -class HtmlPage { +class HtmlPage +{ public: + // Constructor. + HtmlPage(bool rawOrder); + + // Destructor. + ~HtmlPage(); - // Constructor. - HtmlPage(bool rawOrder); + HtmlPage(const HtmlPage &) = delete; + HtmlPage &operator=(const HtmlPage &) = delete; - // Destructor. - ~HtmlPage(); + // Begin a new string. + void beginString(GfxState *state, const GooString *s); - HtmlPage(const HtmlPage &) = delete; - HtmlPage& operator=(const HtmlPage &) = delete; + // Add a character to the current string. + void addChar(GfxState *state, double x, double y, double dx, double dy, double ox, double oy, const Unicode *u, int uLen); // unsigned char c); - // Begin a new string. - void beginString(GfxState *state, const GooString *s); + void updateFont(GfxState *state); - // Add a character to the current string. - void addChar(GfxState *state, double x, double y, - double dx, double dy, - double ox, double oy, - const Unicode *u, int uLen); //unsigned char c); + // End the current string, sorting it into the list of strings. + void endString(); - void updateFont(GfxState *state); + // Coalesce strings that look like parts of the same line. + void coalesce(); - // End the current string, sorting it into the list of strings. - void endString(); + // Find a string. If <top> is true, starts looking at top of page; + // otherwise starts looking at <xMin>,<yMin>. If <bottom> is true, + // stops looking at bottom of page; otherwise stops looking at + // <xMax>,<yMax>. If found, sets the text bounding rectangle and + // returns true; otherwise returns false. - // Coalesce strings that look like parts of the same line. - void coalesce(); + // new functions + void AddLink(const HtmlLink &x) { links->AddLink(x); } - // Find a string. If <top> is true, starts looking at top of page; - // otherwise starts looking at <xMin>,<yMin>. If <bottom> is true, - // stops looking at bottom of page; otherwise stops looking at - // <xMax>,<yMax>. If found, sets the text bounding rectangle and - // returns true; otherwise returns false. - + // add an image to the current page + void addImage(GooString *fname, GfxState *state); - // new functions - void AddLink(const HtmlLink& x){ - links->AddLink(x); - } + // number of images on the current page + int getNumImages() { return imgList->size(); } - // add an image to the current page - void addImage(GooString *fname, GfxState *state); + void dump(FILE *f, int pageNum, const std::vector<std::string> &backgroundImages); - // number of images on the current page - int getNumImages() { return imgList->size(); } + // Clear the page. + void clear(); - void dump(FILE *f, int pageNum, const std::vector<std::string>& backgroundImages); + void conv(); - // Clear the page. - void clear(); - - void conv(); private: - const HtmlFont* getFont(HtmlString *hStr) const { return fonts->Get(hStr->fontpos); } - - double fontSize; // current font size - bool rawOrder; // keep strings in content stream order - - HtmlString *curStr; // currently active string - - HtmlString *yxStrings; // strings in y-major order - HtmlString *xyStrings; // strings in x-major order - HtmlString *yxCur1, *yxCur2; // cursors for yxStrings list - - void setDocName(const char* fname); - void dumpAsXML(FILE* f,int page); - void dumpComplex(FILE* f, int page, const std::vector<std::string>& backgroundImages); - int dumpComplexHeaders(FILE * const file, FILE *& pageFile, int page); - - // marks the position of the fonts that belong to current page (for noframes) - int fontsPageMarker; - HtmlFontAccu *fonts; - HtmlLinks *links; - std::vector<HtmlImage*> *imgList; - - GooString *DocName; - int pageWidth; - int pageHeight; - int firstPage; // used to begin the numeration of pages - - friend class HtmlOutputDev; + const HtmlFont *getFont(HtmlString *hStr) const { return fonts->Get(hStr->fontpos); } + + double fontSize; // current font size + bool rawOrder; // keep strings in content stream order + + HtmlString *curStr; // currently active string + + HtmlString *yxStrings; // strings in y-major order + HtmlString *xyStrings; // strings in x-major order + HtmlString *yxCur1, *yxCur2; // cursors for yxStrings list + + void setDocName(const char *fname); + void dumpAsXML(FILE *f, int page); + void dumpComplex(FILE *f, int page, const std::vector<std::string> &backgroundImages); + int dumpComplexHeaders(FILE *const file, FILE *&pageFile, int page); + + // marks the position of the fonts that belong to current page (for noframes) + int fontsPageMarker; + HtmlFontAccu *fonts; + HtmlLinks *links; + std::vector<HtmlImage *> *imgList; + + GooString *DocName; + int pageWidth; + int pageHeight; + int firstPage; // used to begin the numeration of pages + + friend class HtmlOutputDev; }; //------------------------------------------------------------------------ // HtmlMetaVar //------------------------------------------------------------------------ -class HtmlMetaVar { +class HtmlMetaVar +{ public: HtmlMetaVar(const char *_name, const char *_content); - ~HtmlMetaVar(); - + ~HtmlMetaVar(); + HtmlMetaVar(const HtmlMetaVar &) = delete; - HtmlMetaVar& operator=(const HtmlMetaVar &) = delete; + HtmlMetaVar &operator=(const HtmlMetaVar &) = delete; - GooString* toString() const; + GooString *toString() const; private: - GooString *name; GooString *content; }; @@ -220,135 +210,112 @@ private: // HtmlOutputDev //------------------------------------------------------------------------ -class HtmlOutputDev: public OutputDev { +class HtmlOutputDev : public OutputDev +{ public: + // Open a text output file. If <fileName> is nullptr, no file is written + // (this is useful, e.g., for searching text). If <useASCII7> is true, + // text is converted to 7-bit ASCII; otherwise, text is converted to + // 8-bit ISO Latin-1. <useASCII7> should also be set for Japanese + // (EUC-JP) text. If <rawOrder> is true, the text is kept in content + // stream order. + HtmlOutputDev(Catalog *catalogA, const char *fileName, const char *title, const char *author, const char *keywords, const char *subject, const char *date, bool rawOrder, int firstPage = 1, bool outline = false); + + // Destructor. + ~HtmlOutputDev() override; + + // Check if file was successfully created. + virtual bool isOk() { return ok; } + + //---- get info about output device + + // Does this device use upside-down coordinates? + // (Upside-down means (0,0) is the top left corner of the page.) + bool upsideDown() override { return true; } + + // Does this device use drawChar() or drawString()? + bool useDrawChar() override { return true; } + + // Does this device use beginType3Char/endType3Char? Otherwise, + // text in Type 3 fonts will be drawn with drawChar/drawString. + bool interpretType3Chars() override { return false; } + + // Does this device need non-text content? + bool needNonText() override { return true; } + + //----- initialization and control + + bool checkPageSlice(Page *p, double hDPI, double vDPI, int rotate, bool useMediaBox, bool crop, int sliceX, int sliceY, int sliceW, int sliceH, bool printing, bool (*abortCheckCbk)(void *data) = nullptr, + void *abortCheckCbkData = nullptr, bool (*annotDisplayDecideCbk)(Annot *annot, void *user_data) = nullptr, void *annotDisplayDecideCbkData = nullptr) override + { + docPage = p; + return true; + } + + // Start a page. + void startPage(int pageNum, GfxState *state, XRef *xref) override; + + // End a page. + void endPage() override; + + // add a background image to the list of background images, + // as this seems to be done outside other processing. takes ownership of img. + void addBackgroundImage(const std::string &img); + + //----- update text state + void updateFont(GfxState *state) override; + + //----- text drawing + void beginString(GfxState *state, const GooString *s) override; + void endString(GfxState *state) override; + void drawChar(GfxState *state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, const Unicode *u, int uLen) override; + + void drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, bool invert, bool interpolate, bool inlineImg) override; + void drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool interpolate, const int *maskColors, bool inlineImg) override; + + // new feature + virtual int DevType() { return 1234; } + + int getPageWidth() { return maxPageWidth; } + int getPageHeight() { return maxPageHeight; } - // Open a text output file. If <fileName> is nullptr, no file is written - // (this is useful, e.g., for searching text). If <useASCII7> is true, - // text is converted to 7-bit ASCII; otherwise, text is converted to - // 8-bit ISO Latin-1. <useASCII7> should also be set for Japanese - // (EUC-JP) text. If <rawOrder> is true, the text is kept in content - // stream order. - HtmlOutputDev(Catalog *catalogA, const char *fileName, const char *title, - const char *author, - const char *keywords, - const char *subject, - const char *date, - bool rawOrder, - int firstPage = 1, - bool outline = false); - - // Destructor. - ~HtmlOutputDev() override; - - // Check if file was successfully created. - virtual bool isOk() { return ok; } - - //---- get info about output device - - // Does this device use upside-down coordinates? - // (Upside-down means (0,0) is the top left corner of the page.) - bool upsideDown() override { return true; } - - // Does this device use drawChar() or drawString()? - bool useDrawChar() override { return true; } - - // Does this device use beginType3Char/endType3Char? Otherwise, - // text in Type 3 fonts will be drawn with drawChar/drawString. - bool interpretType3Chars() override { return false; } - - // Does this device need non-text content? - bool needNonText() override { return true; } - - //----- initialization and control - - bool checkPageSlice(Page *p, double hDPI, double vDPI, - int rotate, bool useMediaBox, bool crop, - int sliceX, int sliceY, int sliceW, int sliceH, - bool printing, - bool (* abortCheckCbk)(void *data) = nullptr, - void * abortCheckCbkData = nullptr, - bool (*annotDisplayDecideCbk)(Annot *annot, void *user_data) = nullptr, - void *annotDisplayDecideCbkData = nullptr) override - { - docPage = p; - return true; - } - - - // Start a page. - void startPage(int pageNum, GfxState *state, XRef *xref) override; - - // End a page. - void endPage() override; - - // add a background image to the list of background images, - // as this seems to be done outside other processing. takes ownership of img. - void addBackgroundImage(const std::string& img); - - //----- update text state - void updateFont(GfxState *state) override; - - //----- text drawing - void beginString(GfxState *state, const GooString *s) override; - void endString(GfxState *state) override; - void drawChar(GfxState *state, double x, double y, - double dx, double dy, - double originX, double originY, - CharCode code, int nBytes, const Unicode *u, int uLen) override; - - void drawImageMask(GfxState *state, Object *ref, - Stream *str, - int width, int height, bool invert, - bool interpolate, bool inlineImg) override; - void drawImage(GfxState *state, Object *ref, Stream *str, - int width, int height, GfxImageColorMap *colorMap, - bool interpolate, const int *maskColors, bool inlineImg) override; - - //new feature - virtual int DevType() {return 1234;} - - int getPageWidth() { return maxPageWidth; } - int getPageHeight() { return maxPageHeight; } - - bool dumpDocOutline(PDFDoc* doc); + bool dumpDocOutline(PDFDoc *doc); private: - // convert encoding into a HTML standard, or encoding->c_str if not - // recognized. - static std::string mapEncodingToHtml(const std::string &encoding); - void doProcessLink(AnnotLink *link); - GooString* getLinkDest(AnnotLink *link); - void dumpMetaVars(FILE *); - void doFrame(int firstPage); - bool newHtmlOutlineLevel(FILE *output, const std::vector<OutlineItem*> *outlines, int level = 1); - void newXmlOutlineLevel(FILE *output, const std::vector<OutlineItem*> *outlines); - int getOutlinePageNum(OutlineItem *item); - void drawJpegImage(GfxState *state, Stream *str); - void drawPngImage(GfxState *state, Stream *str, int width, int height, - GfxImageColorMap *colorMap, bool isMask = false); - GooString *createImageFileName(const char *ext); - - FILE *fContentsFrame; - FILE *page; // html file - //FILE *tin; // image log file - //bool write; - bool needClose; // need to close the file? - HtmlPage *pages; // text for the current page - bool rawOrder; // keep text in content stream order - bool doOutline; // output document outline - bool ok; // set up ok? - bool dumpJPEG; - int pageNum; - int maxPageWidth; - int maxPageHeight; - GooString *Docname; - GooString *docTitle; - std::vector<HtmlMetaVar*> *glMetaVars; - Catalog *catalog; - Page *docPage; - std::vector<std::string> backgroundImages; - friend class HtmlPage; + // convert encoding into a HTML standard, or encoding->c_str if not + // recognized. + static std::string mapEncodingToHtml(const std::string &encoding); + void doProcessLink(AnnotLink *link); + GooString *getLinkDest(AnnotLink *link); + void dumpMetaVars(FILE *); + void doFrame(int firstPage); + bool newHtmlOutlineLevel(FILE *output, const std::vector<OutlineItem *> *outlines, int level = 1); + void newXmlOutlineLevel(FILE *output, const std::vector<OutlineItem *> *outlines); + int getOutlinePageNum(OutlineItem *item); + void drawJpegImage(GfxState *state, Stream *str); + void drawPngImage(GfxState *state, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool isMask = false); + GooString *createImageFileName(const char *ext); + + FILE *fContentsFrame; + FILE *page; // html file + // FILE *tin; // image log file + // bool write; + bool needClose; // need to close the file? + HtmlPage *pages; // text for the current page + bool rawOrder; // keep text in content stream order + bool doOutline; // output document outline + bool ok; // set up ok? + bool dumpJPEG; + int pageNum; + int maxPageWidth; + int maxPageHeight; + GooString *Docname; + GooString *docTitle; + std::vector<HtmlMetaVar *> *glMetaVars; + Catalog *catalog; + Page *docPage; + std::vector<std::string> backgroundImages; + friend class HtmlPage; }; #endif diff --git a/utils/HtmlUtils.h b/utils/HtmlUtils.h index c6ccdc12..019e9873 100644 --- a/utils/HtmlUtils.h +++ b/utils/HtmlUtils.h @@ -22,29 +22,34 @@ // Returns true iff the difference between a and b is less than the threshold // We always use fuzzy math when comparing decimal numbers due to imprecision -inline bool is_within(double a, double thresh, double b) { - return fabs(a-b) < thresh; +inline bool is_within(double a, double thresh, double b) +{ + return fabs(a - b) < thresh; } -inline bool rot_matrices_equal(const double * const mat0, const double * const mat1) { - return is_within(mat0[0], .1, mat1[0]) && is_within(mat0[1], .1, mat1[1]) && - is_within(mat0[2], .1, mat1[2]) && is_within(mat0[3], .1, mat1[3]); +inline bool rot_matrices_equal(const double *const mat0, const double *const mat1) +{ + return is_within(mat0[0], .1, mat1[0]) && is_within(mat0[1], .1, mat1[1]) && is_within(mat0[2], .1, mat1[2]) && is_within(mat0[3], .1, mat1[3]); } // rotation is (cos q, sin q, -sin q, cos q, 0, 0) // sin q is zero iff there is no rotation, or 180 deg. rotation; // for 180 rotation, cos q will be negative -inline bool isMatRotOrSkew(const double * const mat) { - return mat[0] < 0 || !is_within(mat[1], .1, 0); +inline bool isMatRotOrSkew(const double *const mat) +{ + return mat[0] < 0 || !is_within(mat[1], .1, 0); } // Alters the matrix so that it does not scale a vector's x component; // If the matrix does not skew, then that will also normalize the y // component, keeping any rotation, but removing scaling. -inline void normalizeRotMat(double *mat) { - double scale = fabs(mat[0] + mat[1]); - if (!scale) return; - for (int i = 0; i < 4; i++) mat[i] /= scale; +inline void normalizeRotMat(double *mat) +{ + double scale = fabs(mat[0] + mat[1]); + if (!scale) + return; + for (int i = 0; i < 4; i++) + mat[i] /= scale; } #endif /* HTMLUTILS_H_ */ diff --git a/utils/ImageOutputDev.cc b/utils/ImageOutputDev.cc index 02684467..552a3b15 100644 --- a/utils/ImageOutputDev.cc +++ b/utils/ImageOutputDev.cc @@ -50,45 +50,46 @@ #include "JBIG2Stream.h" #include "ImageOutputDev.h" -ImageOutputDev::ImageOutputDev(char *fileRootA, bool pageNamesA, bool listImagesA) { - listImages = listImagesA; - if (!listImages) { - fileRoot = copyString(fileRootA); - fileName = (char *)gmalloc(strlen(fileRoot) + 45); - } - outputPNG = false; - outputTiff = false; - dumpJPEG = false; - dumpJP2 = false; - dumpJBIG2 = false; - dumpCCITT = false; - pageNames = pageNamesA; - imgNum = 0; - pageNum = 0; - ok = true; - if (listImages) { - printf("page num type width height color comp bpc enc interp object ID x-ppi y-ppi size ratio\n"); - printf("--------------------------------------------------------------------------------------------\n"); - } +ImageOutputDev::ImageOutputDev(char *fileRootA, bool pageNamesA, bool listImagesA) +{ + listImages = listImagesA; + if (!listImages) { + fileRoot = copyString(fileRootA); + fileName = (char *)gmalloc(strlen(fileRoot) + 45); + } + outputPNG = false; + outputTiff = false; + dumpJPEG = false; + dumpJP2 = false; + dumpJBIG2 = false; + dumpCCITT = false; + pageNames = pageNamesA; + imgNum = 0; + pageNum = 0; + ok = true; + if (listImages) { + printf("page num type width height color comp bpc enc interp object ID x-ppi y-ppi size ratio\n"); + printf("--------------------------------------------------------------------------------------------\n"); + } } - -ImageOutputDev::~ImageOutputDev() { - if (!listImages) { - gfree(fileName); - gfree(fileRoot); - } +ImageOutputDev::~ImageOutputDev() +{ + if (!listImages) { + gfree(fileName); + gfree(fileRoot); + } } -void ImageOutputDev::setFilename(const char *fileExt) { - if (pageNames) { - sprintf(fileName, "%s-%03d-%03d.%s", fileRoot, pageNum, imgNum, fileExt); - } else { - sprintf(fileName, "%s-%03d.%s", fileRoot, imgNum, fileExt); - } +void ImageOutputDev::setFilename(const char *fileExt) +{ + if (pageNames) { + sprintf(fileName, "%s-%03d-%03d.%s", fileRoot, pageNum, imgNum, fileExt); + } else { + sprintf(fileName, "%s-%03d.%s", fileRoot, imgNum, fileExt); + } } - // Print a floating point number between 0 - 9999 using 4 characters // eg '1.23', '12.3', ' 123', '1234' // @@ -97,634 +98,607 @@ void ImageOutputDev::setFilename(const char *fileExt) { // outputs "10.00" instead of "9.99". static void printNumber(double d) { - char buf[10]; - - if (d < 10.0) { - sprintf(buf, "%4.2f", d); - buf[4] = 0; - printf("%s", buf); - } else if (d < 100.0) { - sprintf(buf, "%4.1f", d); - if (!isdigit(buf[3])) { - buf[3] = 0; - printf(" %s", buf); + char buf[10]; + + if (d < 10.0) { + sprintf(buf, "%4.2f", d); + buf[4] = 0; + printf("%s", buf); + } else if (d < 100.0) { + sprintf(buf, "%4.1f", d); + if (!isdigit(buf[3])) { + buf[3] = 0; + printf(" %s", buf); + } else { + printf("%s", buf); + } } else { - printf("%s", buf); + printf("%4.0f", d); } - } else { - printf("%4.0f", d); - } } -void ImageOutputDev::listImage(GfxState *state, Object *ref, Stream *str, - int width, int height, - GfxImageColorMap *colorMap, - bool interpolate, bool inlineImg, - ImageType imageType) { - const char *type; - const char *colorspace; - const char *enc; - int components, bpc; - - printf("%4d %5d ", pageNum, imgNum); - type = ""; - switch (imageType) { - case imgImage: - type = "image"; - break; - case imgStencil: - type = "stencil"; - break; - case imgMask: - type = "mask"; - break; - case imgSmask: - type = "smask"; - break; - } - printf("%-7s %5d %5d ", type, width, height); - - colorspace = "-"; - /* masks and stencils default to ncomps = 1 and bpc = 1 */ - components = 1; - bpc = 1; - if (colorMap && colorMap->isOk()) { - switch (colorMap->getColorSpace()->getMode()) { - case csDeviceGray: - case csCalGray: - colorspace = "gray"; +void ImageOutputDev::listImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool interpolate, bool inlineImg, ImageType imageType) +{ + const char *type; + const char *colorspace; + const char *enc; + int components, bpc; + + printf("%4d %5d ", pageNum, imgNum); + type = ""; + switch (imageType) { + case imgImage: + type = "image"; break; - case csDeviceRGB: - case csCalRGB: - colorspace = "rgb"; + case imgStencil: + type = "stencil"; break; - case csDeviceCMYK: - colorspace = "cmyk"; + case imgMask: + type = "mask"; break; - case csLab: - colorspace = "lab"; + case imgSmask: + type = "smask"; break; - case csICCBased: - colorspace = "icc"; + } + printf("%-7s %5d %5d ", type, width, height); + + colorspace = "-"; + /* masks and stencils default to ncomps = 1 and bpc = 1 */ + components = 1; + bpc = 1; + if (colorMap && colorMap->isOk()) { + switch (colorMap->getColorSpace()->getMode()) { + case csDeviceGray: + case csCalGray: + colorspace = "gray"; + break; + case csDeviceRGB: + case csCalRGB: + colorspace = "rgb"; + break; + case csDeviceCMYK: + colorspace = "cmyk"; + break; + case csLab: + colorspace = "lab"; + break; + case csICCBased: + colorspace = "icc"; + break; + case csIndexed: + colorspace = "index"; + break; + case csSeparation: + colorspace = "sep"; + break; + case csDeviceN: + colorspace = "devn"; + break; + case csPattern: + default: + colorspace = "-"; + break; + } + components = colorMap->getNumPixelComps(); + bpc = colorMap->getBits(); + } + printf("%-5s %2d %2d ", colorspace, components, bpc); + + switch (str->getKind()) { + case strCCITTFax: + enc = "ccitt"; break; - case csIndexed: - colorspace = "index"; + case strDCT: + enc = "jpeg"; break; - case csSeparation: - colorspace = "sep"; + case strJPX: + enc = "jpx"; break; - case csDeviceN: - colorspace = "devn"; + case strJBIG2: + enc = "jbig2"; break; - case csPattern: - default: - colorspace = "-"; + case strFile: + case strFlate: + case strCachedFile: + case strASCIIHex: + case strASCII85: + case strLZW: + case strRunLength: + case strWeird: + default: + enc = "image"; break; } - components = colorMap->getNumPixelComps(); - bpc = colorMap->getBits(); - } - printf("%-5s %2d %2d ", colorspace, components, bpc); - - switch (str->getKind()) { - case strCCITTFax: - enc = "ccitt"; - break; - case strDCT: - enc = "jpeg"; - break; - case strJPX: - enc = "jpx"; - break; - case strJBIG2: - enc = "jbig2"; - break; - case strFile: - case strFlate: - case strCachedFile: - case strASCIIHex: - case strASCII85: - case strLZW: - case strRunLength: - case strWeird: - default: - enc = "image"; - break; - } - printf("%-5s ", enc); - - printf("%-3s ", interpolate ? "yes" : "no"); - - if (inlineImg) { - printf("[inline] "); - } else if (ref->isRef()) { - const Ref imageRef = ref->getRef(); - if (imageRef.gen >= 100000) { - printf("[none] "); - } else { - printf(" %6d %2d ", imageRef.num, imageRef.gen); - } - } else { - printf("[none] "); - } - - const double *mat = state->getCTM(); - double width2 = mat[0] + mat[2]; - double height2 = mat[1] + mat[3]; - double xppi = fabs(width*72.0/width2) + 0.5; - double yppi = fabs(height*72.0/height2) + 0.5; - if (xppi < 1.0) - printf("%5.3f ", xppi); - else - printf("%5.0f ", xppi); - if (yppi < 1.0) - printf("%5.3f ", yppi); - else - printf("%5.0f ", yppi); - - Goffset embedSize = -1; - if (inlineImg) - embedSize = getInlineImageLength(str, width, height, colorMap); - else - embedSize = str->getBaseStream()->getLength(); - - long long imageSize = 0; - if (colorMap && colorMap->isOk()) - imageSize = ((long long)width * height * colorMap->getNumPixelComps() * colorMap->getBits())/8; - else - imageSize = (long long)width*height/8; // mask - - double ratio = -1.0; - if (imageSize > 0) - ratio = 100.0*embedSize/imageSize; - - if (embedSize < 0) { - printf(" - "); - } else if (embedSize <= 9999) { - printf("%4lldB", embedSize); - } else { - double d = embedSize/1024.0; - if (d <= 9999.0) { - printNumber(d); - putchar('K'); + printf("%-5s ", enc); + + printf("%-3s ", interpolate ? "yes" : "no"); + + if (inlineImg) { + printf("[inline] "); + } else if (ref->isRef()) { + const Ref imageRef = ref->getRef(); + if (imageRef.gen >= 100000) { + printf("[none] "); + } else { + printf(" %6d %2d ", imageRef.num, imageRef.gen); + } } else { - d /= 1024.0; - if (d <= 9999.0) { - printNumber(d); - putchar('M'); - } else { - d /= 1024.0; - printNumber(d); - putchar('G'); - } + printf("[none] "); } - } - if (ratio > 9.9) - printf(" %3.0f%%\n", ratio); - else if (ratio >= 0.0) - printf(" %3.1f%%\n", ratio); - else - printf(" - \n"); - - ++imgNum; + const double *mat = state->getCTM(); + double width2 = mat[0] + mat[2]; + double height2 = mat[1] + mat[3]; + double xppi = fabs(width * 72.0 / width2) + 0.5; + double yppi = fabs(height * 72.0 / height2) + 0.5; + if (xppi < 1.0) + printf("%5.3f ", xppi); + else + printf("%5.0f ", xppi); + if (yppi < 1.0) + printf("%5.3f ", yppi); + else + printf("%5.0f ", yppi); -} + Goffset embedSize = -1; + if (inlineImg) + embedSize = getInlineImageLength(str, width, height, colorMap); + else + embedSize = str->getBaseStream()->getLength(); -long ImageOutputDev::getInlineImageLength(Stream *str, int width, int height, - GfxImageColorMap *colorMap) { - long len; + long long imageSize = 0; + if (colorMap && colorMap->isOk()) + imageSize = ((long long)width * height * colorMap->getNumPixelComps() * colorMap->getBits()) / 8; + else + imageSize = (long long)width * height / 8; // mask - if (colorMap) { - ImageStream *imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), - colorMap->getBits()); - imgStr->reset(); - for (int y = 0; y < height; y++) - imgStr->getLine(); + double ratio = -1.0; + if (imageSize > 0) + ratio = 100.0 * embedSize / imageSize; - imgStr->close(); - delete imgStr; - } else { - str->reset(); - for (int y = 0; y < height; y++) { - int size = (width + 7)/8; - for (int x = 0; x < size; x++) - str->getChar(); + if (embedSize < 0) { + printf(" - "); + } else if (embedSize <= 9999) { + printf("%4lldB", embedSize); + } else { + double d = embedSize / 1024.0; + if (d <= 9999.0) { + printNumber(d); + putchar('K'); + } else { + d /= 1024.0; + if (d <= 9999.0) { + printNumber(d); + putchar('M'); + } else { + d /= 1024.0; + printNumber(d); + putchar('G'); + } + } } - } - - EmbedStream *embedStr = (EmbedStream *) (str->getBaseStream()); - embedStr->rewind(); - len = 0; - while (embedStr->getChar() != EOF) - len++; - embedStr->restore(); + if (ratio > 9.9) + printf(" %3.0f%%\n", ratio); + else if (ratio >= 0.0) + printf(" %3.1f%%\n", ratio); + else + printf(" - \n"); - return len; + ++imgNum; } -void ImageOutputDev::writeRawImage(Stream *str, const char *ext) { - FILE *f; - int c; +long ImageOutputDev::getInlineImageLength(Stream *str, int width, int height, GfxImageColorMap *colorMap) +{ + long len; + + if (colorMap) { + ImageStream *imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), colorMap->getBits()); + imgStr->reset(); + for (int y = 0; y < height; y++) + imgStr->getLine(); - // open the image file - setFilename(ext); - ++imgNum; - if (!(f = fopen(fileName, "wb"))) { - error(errIO, -1, "Couldn't open image file '{0:s}'", fileName); - return; - } + imgStr->close(); + delete imgStr; + } else { + str->reset(); + for (int y = 0; y < height; y++) { + int size = (width + 7) / 8; + for (int x = 0; x < size; x++) + str->getChar(); + } + } - // initialize stream - str = str->getNextStream(); - str->reset(); + EmbedStream *embedStr = (EmbedStream *)(str->getBaseStream()); + embedStr->rewind(); + len = 0; + while (embedStr->getChar() != EOF) + len++; - // copy the stream - while ((c = str->getChar()) != EOF) - fputc(c, f); + embedStr->restore(); - str->close(); - fclose(f); + return len; } -void ImageOutputDev::writeImageFile(ImgWriter *writer, ImageFormat format, const char *ext, - Stream *str, int width, int height, GfxImageColorMap *colorMap) { - FILE *f = nullptr; /* squelch bogus compiler warning */ - ImageStream *imgStr = nullptr; - unsigned char *row; - unsigned char *rowp; - unsigned char *p; - GfxRGB rgb; - GfxCMYK cmyk; - GfxGray gray; - unsigned char zero[gfxColorMaxComps]; - int invert_bits; - - if (writer) { +void ImageOutputDev::writeRawImage(Stream *str, const char *ext) +{ + FILE *f; + int c; + + // open the image file setFilename(ext); ++imgNum; if (!(f = fopen(fileName, "wb"))) { - error(errIO, -1, "Couldn't open image file '{0:s}'", fileName); - return; - } - - if (!writer->init(f, width, height, 72, 72)) { - error(errIO, -1, "Error writing '{0:s}'", fileName); - return; + error(errIO, -1, "Couldn't open image file '{0:s}'", fileName); + return; } - } - if (format != imgMonochrome) { - // initialize stream - imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), - colorMap->getBits()); - imgStr->reset(); - } else { // initialize stream + str = str->getNextStream(); str->reset(); - } - - int pixelSize = sizeof(unsigned int); - if (format == imgRGB48) - pixelSize = 2*sizeof(unsigned int); - - row = (unsigned char *) gmallocn(width, pixelSize); - - // PDF masks use 0 = draw current color, 1 = leave unchanged. - // We invert this to provide the standard interpretation of alpha - // (0 = transparent, 1 = opaque). If the colorMap already inverts - // the mask we leave the data unchanged. - invert_bits = 0xff; - if (colorMap) { - memset(zero, 0, sizeof(zero)); - colorMap->getGray(zero, &gray); - if (colToByte(gray) == 0) - invert_bits = 0x00; - } - - // for each line... - for (int y = 0; y < height; y++) { - switch (format) { - case imgRGB: - p = imgStr->getLine(); - rowp = row; - for (int x = 0; x < width; ++x) { - if (p) { - colorMap->getRGB(p, &rgb); - *rowp++ = colToByte(rgb.r); - *rowp++ = colToByte(rgb.g); - *rowp++ = colToByte(rgb.b); - p += colorMap->getNumPixelComps(); - } else { - *rowp++ = 0; - *rowp++ = 0; - *rowp++ = 0; + + // copy the stream + while ((c = str->getChar()) != EOF) + fputc(c, f); + + str->close(); + fclose(f); +} + +void ImageOutputDev::writeImageFile(ImgWriter *writer, ImageFormat format, const char *ext, Stream *str, int width, int height, GfxImageColorMap *colorMap) +{ + FILE *f = nullptr; /* squelch bogus compiler warning */ + ImageStream *imgStr = nullptr; + unsigned char *row; + unsigned char *rowp; + unsigned char *p; + GfxRGB rgb; + GfxCMYK cmyk; + GfxGray gray; + unsigned char zero[gfxColorMaxComps]; + int invert_bits; + + if (writer) { + setFilename(ext); + ++imgNum; + if (!(f = fopen(fileName, "wb"))) { + error(errIO, -1, "Couldn't open image file '{0:s}'", fileName); + return; + } + + if (!writer->init(f, width, height, 72, 72)) { + error(errIO, -1, "Error writing '{0:s}'", fileName); + return; } - } - if (writer) - writer->writeRow(&row); - break; - - case imgRGB48: { - p = imgStr->getLine(); - unsigned short *rowp16 = reinterpret_cast<unsigned short*>(row); - for (int x = 0; x < width; ++x) { - if (p) { - colorMap->getRGB(p, &rgb); - *rowp16++ = colToShort(rgb.r); - *rowp16++ = colToShort(rgb.g); - *rowp16++ = colToShort(rgb.b); - p += colorMap->getNumPixelComps(); - } else { - *rowp16++ = 0; - *rowp16++ = 0; - *rowp16++ = 0; - } - } - if (writer) - writer->writeRow(&row); - break; } - case imgCMYK: - p = imgStr->getLine(); - rowp = row; - for (int x = 0; x < width; ++x) { - if (p) { - colorMap->getCMYK(p, &cmyk); - *rowp++ = colToByte(cmyk.c); - *rowp++ = colToByte(cmyk.m); - *rowp++ = colToByte(cmyk.y); - *rowp++ = colToByte(cmyk.k); - p += colorMap->getNumPixelComps(); - } else { - *rowp++ = 0; - *rowp++ = 0; - *rowp++ = 0; - *rowp++ = 0; + if (format != imgMonochrome) { + // initialize stream + imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), colorMap->getBits()); + imgStr->reset(); + } else { + // initialize stream + str->reset(); + } + + int pixelSize = sizeof(unsigned int); + if (format == imgRGB48) + pixelSize = 2 * sizeof(unsigned int); + + row = (unsigned char *)gmallocn(width, pixelSize); + + // PDF masks use 0 = draw current color, 1 = leave unchanged. + // We invert this to provide the standard interpretation of alpha + // (0 = transparent, 1 = opaque). If the colorMap already inverts + // the mask we leave the data unchanged. + invert_bits = 0xff; + if (colorMap) { + memset(zero, 0, sizeof(zero)); + colorMap->getGray(zero, &gray); + if (colToByte(gray) == 0) + invert_bits = 0x00; + } + + // for each line... + for (int y = 0; y < height; y++) { + switch (format) { + case imgRGB: + p = imgStr->getLine(); + rowp = row; + for (int x = 0; x < width; ++x) { + if (p) { + colorMap->getRGB(p, &rgb); + *rowp++ = colToByte(rgb.r); + *rowp++ = colToByte(rgb.g); + *rowp++ = colToByte(rgb.b); + p += colorMap->getNumPixelComps(); + } else { + *rowp++ = 0; + *rowp++ = 0; + *rowp++ = 0; + } + } + if (writer) + writer->writeRow(&row); + break; + + case imgRGB48: { + p = imgStr->getLine(); + unsigned short *rowp16 = reinterpret_cast<unsigned short *>(row); + for (int x = 0; x < width; ++x) { + if (p) { + colorMap->getRGB(p, &rgb); + *rowp16++ = colToShort(rgb.r); + *rowp16++ = colToShort(rgb.g); + *rowp16++ = colToShort(rgb.b); + p += colorMap->getNumPixelComps(); + } else { + *rowp16++ = 0; + *rowp16++ = 0; + *rowp16++ = 0; + } + } + if (writer) + writer->writeRow(&row); + break; } - } - if (writer) - writer->writeRow(&row); - break; - - case imgGray: - p = imgStr->getLine(); - rowp = row; - for (int x = 0; x < width; ++x) { - if (p) { - colorMap->getGray(p, &gray); - *rowp++ = colToByte(gray); - p += colorMap->getNumPixelComps(); - } else { - *rowp++ = 0; + + case imgCMYK: + p = imgStr->getLine(); + rowp = row; + for (int x = 0; x < width; ++x) { + if (p) { + colorMap->getCMYK(p, &cmyk); + *rowp++ = colToByte(cmyk.c); + *rowp++ = colToByte(cmyk.m); + *rowp++ = colToByte(cmyk.y); + *rowp++ = colToByte(cmyk.k); + p += colorMap->getNumPixelComps(); + } else { + *rowp++ = 0; + *rowp++ = 0; + *rowp++ = 0; + *rowp++ = 0; + } + } + if (writer) + writer->writeRow(&row); + break; + + case imgGray: + p = imgStr->getLine(); + rowp = row; + for (int x = 0; x < width; ++x) { + if (p) { + colorMap->getGray(p, &gray); + *rowp++ = colToByte(gray); + p += colorMap->getNumPixelComps(); + } else { + *rowp++ = 0; + } + } + if (writer) + writer->writeRow(&row); + break; + + case imgMonochrome: + int size = (width + 7) / 8; + for (int x = 0; x < size; x++) + row[x] = str->getChar() ^ invert_bits; + if (writer) + writer->writeRow(&row); + break; } - } - if (writer) - writer->writeRow(&row); - break; - - case imgMonochrome: - int size = (width + 7)/8; - for (int x = 0; x < size; x++) - row[x] = str->getChar() ^ invert_bits; - if (writer) - writer->writeRow(&row); - break; } - } - - gfree(row); - if (format != imgMonochrome) { - imgStr->close(); - delete imgStr; - } - str->close(); - if (writer) { - writer->close(); - fclose(f); - } + + gfree(row); + if (format != imgMonochrome) { + imgStr->close(); + delete imgStr; + } + str->close(); + if (writer) { + writer->close(); + fclose(f); + } } -void ImageOutputDev::writeImage(GfxState *state, Object *ref, Stream *str, - int width, int height, - GfxImageColorMap *colorMap, bool inlineImg) { - ImageFormat format; - EmbedStream *embedStr; - - if (inlineImg) { - embedStr = (EmbedStream *) (str->getBaseStream()); - // Record the stream. This determines the size. - getInlineImageLength(str, width, height, colorMap); - // Reading the stream again will return EOF at end of recording. - embedStr->rewind(); - } - - if (dumpJPEG && str->getKind() == strDCT) { - // dump JPEG file - writeRawImage(str, "jpg"); - - } else if (dumpJP2 && str->getKind() == strJPX && !inlineImg) { - // dump JPEG2000 file - writeRawImage(str, "jp2"); - - } else if (dumpJBIG2 && str->getKind() == strJBIG2 && !inlineImg) { - // dump JBIG2 globals stream if available - JBIG2Stream *jb2Str = static_cast<JBIG2Stream *>(str); - Object *globals = jb2Str->getGlobalsStream(); - if (globals->isStream()) { - FILE *f; - int c; - Stream *globalsStr = globals->getStream(); - - setFilename("jb2g"); - if (!(f = fopen(fileName, "wb"))) { - error(errIO, -1, "Couldn't open image file '{0:s}'", fileName); - return; - } - globalsStr->reset(); - while ((c = globalsStr->getChar()) != EOF) - fputc(c, f); - globalsStr->close(); - fclose(f); +void ImageOutputDev::writeImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool inlineImg) +{ + ImageFormat format; + EmbedStream *embedStr; + + if (inlineImg) { + embedStr = (EmbedStream *)(str->getBaseStream()); + // Record the stream. This determines the size. + getInlineImageLength(str, width, height, colorMap); + // Reading the stream again will return EOF at end of recording. + embedStr->rewind(); } - // dump JBIG2 embedded file - writeRawImage(str, "jb2e"); + if (dumpJPEG && str->getKind() == strDCT) { + // dump JPEG file + writeRawImage(str, "jpg"); + + } else if (dumpJP2 && str->getKind() == strJPX && !inlineImg) { + // dump JPEG2000 file + writeRawImage(str, "jp2"); + + } else if (dumpJBIG2 && str->getKind() == strJBIG2 && !inlineImg) { + // dump JBIG2 globals stream if available + JBIG2Stream *jb2Str = static_cast<JBIG2Stream *>(str); + Object *globals = jb2Str->getGlobalsStream(); + if (globals->isStream()) { + FILE *f; + int c; + Stream *globalsStr = globals->getStream(); + + setFilename("jb2g"); + if (!(f = fopen(fileName, "wb"))) { + error(errIO, -1, "Couldn't open image file '{0:s}'", fileName); + return; + } + globalsStr->reset(); + while ((c = globalsStr->getChar()) != EOF) + fputc(c, f); + globalsStr->close(); + fclose(f); + } - } else if (dumpCCITT && str->getKind() == strCCITTFax) { - // write CCITT parameters - CCITTFaxStream *ccittStr = static_cast<CCITTFaxStream *>(str); - FILE *f; - setFilename("params"); - if (!(f = fopen(fileName, "wb"))) { - error(errIO, -1, "Couldn't open image file '{0:s}'", fileName); - return; - } - if (ccittStr->getEncoding() < 0) - fprintf(f, "-4 "); - else if (ccittStr->getEncoding() == 0) - fprintf(f, "-1 "); - else - fprintf(f, "-2 "); + // dump JBIG2 embedded file + writeRawImage(str, "jb2e"); + + } else if (dumpCCITT && str->getKind() == strCCITTFax) { + // write CCITT parameters + CCITTFaxStream *ccittStr = static_cast<CCITTFaxStream *>(str); + FILE *f; + setFilename("params"); + if (!(f = fopen(fileName, "wb"))) { + error(errIO, -1, "Couldn't open image file '{0:s}'", fileName); + return; + } + if (ccittStr->getEncoding() < 0) + fprintf(f, "-4 "); + else if (ccittStr->getEncoding() == 0) + fprintf(f, "-1 "); + else + fprintf(f, "-2 "); - if (ccittStr->getEndOfLine()) - fprintf(f, "-A "); - else - fprintf(f, "-P "); + if (ccittStr->getEndOfLine()) + fprintf(f, "-A "); + else + fprintf(f, "-P "); - fprintf(f, "-X %d ", ccittStr->getColumns()); + fprintf(f, "-X %d ", ccittStr->getColumns()); - if (ccittStr->getBlackIs1()) - fprintf(f, "-W "); - else - fprintf(f, "-B "); + if (ccittStr->getBlackIs1()) + fprintf(f, "-W "); + else + fprintf(f, "-B "); - fprintf(f, "-M\n"); // PDF uses MSB first + fprintf(f, "-M\n"); // PDF uses MSB first - fclose(f); + fclose(f); - // dump CCITT file - writeRawImage(str, "ccitt"); + // dump CCITT file + writeRawImage(str, "ccitt"); - } else if (outputPNG && !(outputTiff && colorMap && - (colorMap->getColorSpace()->getMode() == csDeviceCMYK || - (colorMap->getColorSpace()->getMode() == csICCBased && - colorMap->getNumPixelComps() == 4)))) { - // output in PNG format + } else if (outputPNG && !(outputTiff && colorMap && (colorMap->getColorSpace()->getMode() == csDeviceCMYK || (colorMap->getColorSpace()->getMode() == csICCBased && colorMap->getNumPixelComps() == 4)))) { + // output in PNG format #ifdef ENABLE_LIBPNG - ImgWriter *writer; - - if (!colorMap || (colorMap->getNumPixelComps() == 1 && colorMap->getBits() == 1)) { - writer = new PNGWriter(PNGWriter::MONOCHROME); - format = imgMonochrome; - } else if (colorMap->getColorSpace()->getMode() == csDeviceGray || - colorMap->getColorSpace()->getMode() == csCalGray) { - writer = new PNGWriter(PNGWriter::GRAY); - format = imgGray; - } else if ((colorMap->getColorSpace()->getMode() == csDeviceRGB || - colorMap->getColorSpace()->getMode() == csCalRGB || - (colorMap->getColorSpace()->getMode() == csICCBased && colorMap->getNumPixelComps() == 3)) && - colorMap->getBits() > 8) { - writer = new PNGWriter(PNGWriter::RGB48); - format = imgRGB48; - } else { - writer = new PNGWriter(PNGWriter::RGB); - format = imgRGB; - } + ImgWriter *writer; + + if (!colorMap || (colorMap->getNumPixelComps() == 1 && colorMap->getBits() == 1)) { + writer = new PNGWriter(PNGWriter::MONOCHROME); + format = imgMonochrome; + } else if (colorMap->getColorSpace()->getMode() == csDeviceGray || colorMap->getColorSpace()->getMode() == csCalGray) { + writer = new PNGWriter(PNGWriter::GRAY); + format = imgGray; + } else if ((colorMap->getColorSpace()->getMode() == csDeviceRGB || colorMap->getColorSpace()->getMode() == csCalRGB || (colorMap->getColorSpace()->getMode() == csICCBased && colorMap->getNumPixelComps() == 3)) + && colorMap->getBits() > 8) { + writer = new PNGWriter(PNGWriter::RGB48); + format = imgRGB48; + } else { + writer = new PNGWriter(PNGWriter::RGB); + format = imgRGB; + } - writeImageFile(writer, format, "png", str, width, height, colorMap); + writeImageFile(writer, format, "png", str, width, height, colorMap); - delete writer; + delete writer; #endif - } else if (outputTiff) { - // output in TIFF format + } else if (outputTiff) { + // output in TIFF format #ifdef ENABLE_LIBTIFF - ImgWriter *writer; - - if (!colorMap || (colorMap->getNumPixelComps() == 1 && colorMap->getBits() == 1)) { - writer = new TiffWriter(TiffWriter::MONOCHROME); - format = imgMonochrome; - } else if (colorMap->getColorSpace()->getMode() == csDeviceGray || - colorMap->getColorSpace()->getMode() == csCalGray) { - writer = new TiffWriter(TiffWriter::GRAY); - format = imgGray; - } else if (colorMap->getColorSpace()->getMode() == csDeviceCMYK || - (colorMap->getColorSpace()->getMode() == csICCBased && colorMap->getNumPixelComps() == 4)) { - writer = new TiffWriter(TiffWriter::CMYK); - format = imgCMYK; - } else if ((colorMap->getColorSpace()->getMode() == csDeviceRGB || - colorMap->getColorSpace()->getMode() == csCalRGB || - (colorMap->getColorSpace()->getMode() == csICCBased && colorMap->getNumPixelComps() == 3)) && - colorMap->getBits() > 8) { - writer = new TiffWriter(TiffWriter::RGB48); - format = imgRGB48; - } else { - writer = new TiffWriter(TiffWriter::RGB); - format = imgRGB; - } + ImgWriter *writer; + + if (!colorMap || (colorMap->getNumPixelComps() == 1 && colorMap->getBits() == 1)) { + writer = new TiffWriter(TiffWriter::MONOCHROME); + format = imgMonochrome; + } else if (colorMap->getColorSpace()->getMode() == csDeviceGray || colorMap->getColorSpace()->getMode() == csCalGray) { + writer = new TiffWriter(TiffWriter::GRAY); + format = imgGray; + } else if (colorMap->getColorSpace()->getMode() == csDeviceCMYK || (colorMap->getColorSpace()->getMode() == csICCBased && colorMap->getNumPixelComps() == 4)) { + writer = new TiffWriter(TiffWriter::CMYK); + format = imgCMYK; + } else if ((colorMap->getColorSpace()->getMode() == csDeviceRGB || colorMap->getColorSpace()->getMode() == csCalRGB || (colorMap->getColorSpace()->getMode() == csICCBased && colorMap->getNumPixelComps() == 3)) + && colorMap->getBits() > 8) { + writer = new TiffWriter(TiffWriter::RGB48); + format = imgRGB48; + } else { + writer = new TiffWriter(TiffWriter::RGB); + format = imgRGB; + } - writeImageFile(writer, format, "tif", str, width, height, colorMap); + writeImageFile(writer, format, "tif", str, width, height, colorMap); - delete writer; + delete writer; #endif - } else { - // output in PPM/PBM format - ImgWriter *writer; - - if (!colorMap || (colorMap->getNumPixelComps() == 1 && colorMap->getBits() == 1)) { - writer = new NetPBMWriter(NetPBMWriter::MONOCHROME); - format = imgMonochrome; } else { - writer = new NetPBMWriter(NetPBMWriter::RGB); - format = imgRGB; - } + // output in PPM/PBM format + ImgWriter *writer; - writeImageFile(writer, format, - format == imgRGB ? "ppm" : "pbm", - str, width, height, colorMap); + if (!colorMap || (colorMap->getNumPixelComps() == 1 && colorMap->getBits() == 1)) { + writer = new NetPBMWriter(NetPBMWriter::MONOCHROME); + format = imgMonochrome; + } else { + writer = new NetPBMWriter(NetPBMWriter::RGB); + format = imgRGB; + } - delete writer; - } + writeImageFile(writer, format, format == imgRGB ? "ppm" : "pbm", str, width, height, colorMap); - if (inlineImg) - embedStr->restore(); + delete writer; + } + + if (inlineImg) + embedStr->restore(); } -bool ImageOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Catalog *cat, Object *str, - const double *pmat, int paintType, int tilingType, Dict *resDict, - const double *mat, const double *bbox, - int x0, int y0, int x1, int y1, - double xStep, double yStep) { - return true; - // do nothing -- this avoids the potentially slow loop in Gfx.cc +bool ImageOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Catalog *cat, Object *str, const double *pmat, int paintType, int tilingType, Dict *resDict, const double *mat, const double *bbox, int x0, int y0, int x1, int y1, + double xStep, double yStep) +{ + return true; + // do nothing -- this avoids the potentially slow loop in Gfx.cc } -void ImageOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, - int width, int height, bool invert, - bool interpolate, bool inlineImg) { - if (listImages) - listImage(state, ref, str, width, height, nullptr, interpolate, inlineImg, imgStencil); - else - writeImage(state, ref, str, width, height, nullptr, inlineImg); +void ImageOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, bool invert, bool interpolate, bool inlineImg) +{ + if (listImages) + listImage(state, ref, str, width, height, nullptr, interpolate, inlineImg, imgStencil); + else + writeImage(state, ref, str, width, height, nullptr, inlineImg); } -void ImageOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, - int width, int height, - GfxImageColorMap *colorMap, - bool interpolate, const int *maskColors, bool inlineImg) { - if (listImages) - listImage(state, ref, str, width, height, colorMap, interpolate, inlineImg, imgImage); - else - writeImage(state, ref, str, width, height, colorMap, inlineImg); +void ImageOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool interpolate, const int *maskColors, bool inlineImg) +{ + if (listImages) + listImage(state, ref, str, width, height, colorMap, interpolate, inlineImg, imgImage); + else + writeImage(state, ref, str, width, height, colorMap, inlineImg); } -void ImageOutputDev::drawMaskedImage( - GfxState *state, Object *ref, Stream *str, - int width, int height, GfxImageColorMap *colorMap, bool interpolate, - Stream *maskStr, int maskWidth, int maskHeight, bool maskInvert, bool maskInterpolate) { - if (listImages) { - listImage(state, ref, str, width, height, colorMap, interpolate, false, imgImage); - listImage(state, ref, str, maskWidth, maskHeight, nullptr, maskInterpolate, false, imgMask); - } else { - writeImage(state, ref, str, width, height, colorMap, false); - writeImage(state, ref, maskStr, maskWidth, maskHeight, nullptr, false); - } +void ImageOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool interpolate, Stream *maskStr, int maskWidth, int maskHeight, bool maskInvert, bool maskInterpolate) +{ + if (listImages) { + listImage(state, ref, str, width, height, colorMap, interpolate, false, imgImage); + listImage(state, ref, str, maskWidth, maskHeight, nullptr, maskInterpolate, false, imgMask); + } else { + writeImage(state, ref, str, width, height, colorMap, false); + writeImage(state, ref, maskStr, maskWidth, maskHeight, nullptr, false); + } } -void ImageOutputDev::drawSoftMaskedImage( - GfxState *state, Object *ref, Stream *str, - int width, int height, GfxImageColorMap *colorMap, bool interpolate, - Stream *maskStr, int maskWidth, int maskHeight, - GfxImageColorMap *maskColorMap, bool maskInterpolate) { - if (listImages) { - listImage(state, ref, str, width, height, colorMap, interpolate, false, imgImage); - listImage(state, ref, maskStr, maskWidth, maskHeight, maskColorMap, maskInterpolate, false, imgSmask); - } else { - writeImage(state, ref, str, width, height, colorMap, false); - writeImage(state, ref, maskStr, maskWidth, maskHeight, maskColorMap, false); - } +void ImageOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool interpolate, Stream *maskStr, int maskWidth, int maskHeight, GfxImageColorMap *maskColorMap, + bool maskInterpolate) +{ + if (listImages) { + listImage(state, ref, str, width, height, colorMap, interpolate, false, imgImage); + listImage(state, ref, maskStr, maskWidth, maskHeight, maskColorMap, maskInterpolate, false, imgSmask); + } else { + writeImage(state, ref, str, width, height, colorMap, false); + writeImage(state, ref, maskStr, maskWidth, maskHeight, maskColorMap, false); + } } diff --git a/utils/ImageOutputDev.h b/utils/ImageOutputDev.h index 34c670fb..a665f3f8 100644 --- a/utils/ImageOutputDev.h +++ b/utils/ImageOutputDev.h @@ -41,136 +41,114 @@ class GfxState; // ImageOutputDev //------------------------------------------------------------------------ -class ImageOutputDev: public OutputDev { +class ImageOutputDev : public OutputDev +{ public: - enum ImageType { - imgImage, - imgStencil, - imgMask, - imgSmask - }; - enum ImageFormat { - imgRGB, - imgRGB48, - imgGray, - imgMonochrome, - imgCMYK - }; - - // Create an OutputDev which will write images to files named - // <fileRoot>-NNN.<type> or <fileRoot>-PPP-NNN.<type>, if - // <pageNames> is set. Normally, all images are written as PBM - // (.pbm) or PPM (.ppm) files unless PNG or Tiff output is enabled - // (PNG is used if both are enabled). If Jpeg is enabled, JPEG images - // are written as JPEG (.jpg) files. - ImageOutputDev(char *fileRootA, bool pageNamesA, bool listImagesA); - - // Destructor. - ~ImageOutputDev() override; - - // Use PNG format for output - void enablePNG(bool png) { outputPNG = png; } - - // Use TIFF format for output - void enableTiff(bool tiff) { outputTiff = tiff; } - - // Use Jpeg format for Jpeg files - void enableJpeg(bool jpeg) { dumpJPEG = jpeg; } - - // Use Jpeg2000 format for Jpeg2000 files - void enableJpeg2000(bool jp2) { dumpJP2 = jp2; } - - // Use JBIG2 format for JBIG2 files - void enableJBig2(bool jbig2) { dumpJBIG2 = jbig2; } - - // Use CCITT format for CCITT files - void enableCCITT(bool ccitt) { dumpCCITT = ccitt; } - - // Check if file was successfully created. - virtual bool isOk() { return ok; } - - // Does this device use tilingPatternFill()? If this returns false, - // tiling pattern fills will be reduced to a series of other drawing - // operations. - bool useTilingPatternFill() override { return true; } - - // Does this device use beginType3Char/endType3Char? Otherwise, - // text in Type 3 fonts will be drawn with drawChar/drawString. - bool interpretType3Chars() override { return false; } - - // Does this device need non-text content? - bool needNonText() override { return true; } - - // Start a page - void startPage(int pageNumA, GfxState *state, XRef *xref) override - { pageNum = pageNumA; } - - //---- get info about output device - - // Does this device use upside-down coordinates? - // (Upside-down means (0,0) is the top left corner of the page.) - bool upsideDown() override { return true; } - - // Does this device use drawChar() or drawString()? - bool useDrawChar() override { return false; } - - //----- path painting - bool tilingPatternFill(GfxState *state, Gfx *gfx, Catalog *cat, Object *str, - const double *pmat, int paintType, int tilingType, Dict *resDict, - const double *mat, const double *bbox, - int x0, int y0, int x1, int y1, - double xStep, double yStep) override; - - //----- image drawing - void drawImageMask(GfxState *state, Object *ref, Stream *str, - int width, int height, bool invert, - bool interpolate, bool inlineImg) override; - void drawImage(GfxState *state, Object *ref, Stream *str, - int width, int height, GfxImageColorMap *colorMap, - bool interpolate, const int *maskColors, bool inlineImg) override; - void drawMaskedImage(GfxState *state, Object *ref, Stream *str, - int width, int height, - GfxImageColorMap *colorMap, - bool interpolate, - Stream *maskStr, int maskWidth, int maskHeight, - bool maskInvert, bool maskInterpolate) override; - void drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str, - int width, int height, - GfxImageColorMap *colorMap, - bool interpolate, - Stream *maskStr, - int maskWidth, int maskHeight, - GfxImageColorMap *maskColorMap, - bool maskInterpolate) override; + enum ImageType + { + imgImage, + imgStencil, + imgMask, + imgSmask + }; + enum ImageFormat + { + imgRGB, + imgRGB48, + imgGray, + imgMonochrome, + imgCMYK + }; + + // Create an OutputDev which will write images to files named + // <fileRoot>-NNN.<type> or <fileRoot>-PPP-NNN.<type>, if + // <pageNames> is set. Normally, all images are written as PBM + // (.pbm) or PPM (.ppm) files unless PNG or Tiff output is enabled + // (PNG is used if both are enabled). If Jpeg is enabled, JPEG images + // are written as JPEG (.jpg) files. + ImageOutputDev(char *fileRootA, bool pageNamesA, bool listImagesA); + + // Destructor. + ~ImageOutputDev() override; + + // Use PNG format for output + void enablePNG(bool png) { outputPNG = png; } + + // Use TIFF format for output + void enableTiff(bool tiff) { outputTiff = tiff; } + + // Use Jpeg format for Jpeg files + void enableJpeg(bool jpeg) { dumpJPEG = jpeg; } + + // Use Jpeg2000 format for Jpeg2000 files + void enableJpeg2000(bool jp2) { dumpJP2 = jp2; } + + // Use JBIG2 format for JBIG2 files + void enableJBig2(bool jbig2) { dumpJBIG2 = jbig2; } + + // Use CCITT format for CCITT files + void enableCCITT(bool ccitt) { dumpCCITT = ccitt; } + + // Check if file was successfully created. + virtual bool isOk() { return ok; } + + // Does this device use tilingPatternFill()? If this returns false, + // tiling pattern fills will be reduced to a series of other drawing + // operations. + bool useTilingPatternFill() override { return true; } + + // Does this device use beginType3Char/endType3Char? Otherwise, + // text in Type 3 fonts will be drawn with drawChar/drawString. + bool interpretType3Chars() override { return false; } + + // Does this device need non-text content? + bool needNonText() override { return true; } + + // Start a page + void startPage(int pageNumA, GfxState *state, XRef *xref) override { pageNum = pageNumA; } + + //---- get info about output device + + // Does this device use upside-down coordinates? + // (Upside-down means (0,0) is the top left corner of the page.) + bool upsideDown() override { return true; } + + // Does this device use drawChar() or drawString()? + bool useDrawChar() override { return false; } + + //----- path painting + bool tilingPatternFill(GfxState *state, Gfx *gfx, Catalog *cat, Object *str, const double *pmat, int paintType, int tilingType, Dict *resDict, const double *mat, const double *bbox, int x0, int y0, int x1, int y1, double xStep, + double yStep) override; + + //----- image drawing + void drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, bool invert, bool interpolate, bool inlineImg) override; + void drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool interpolate, const int *maskColors, bool inlineImg) override; + void drawMaskedImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool interpolate, Stream *maskStr, int maskWidth, int maskHeight, bool maskInvert, bool maskInterpolate) override; + void drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool interpolate, Stream *maskStr, int maskWidth, int maskHeight, GfxImageColorMap *maskColorMap, + bool maskInterpolate) override; private: - // Sets the output filename with a given file extension - void setFilename(const char *fileExt); - void listImage(GfxState *state, Object *ref, Stream *str, - int width, int height, - GfxImageColorMap *colorMap, - bool interpolate, bool inlineImg, - ImageType imageType); - void writeImage(GfxState *state, Object *ref, Stream *str, - int width, int height, GfxImageColorMap *colorMap, bool inlineImg); - void writeRawImage(Stream *str, const char *ext); - void writeImageFile(ImgWriter *writer, ImageFormat format, const char *ext, - Stream *str, int width, int height, GfxImageColorMap *colorMap); - long getInlineImageLength(Stream *str, int width, int height, GfxImageColorMap *colorMap); - - char *fileRoot; // root of output file names - char *fileName; // buffer for output file names - bool listImages; // list images instead of dumping - bool dumpJPEG; // set to dump native JPEG files - bool dumpJP2; // set to dump native JPEG2000 files - bool dumpJBIG2; // set to dump native JBIG2 files - bool dumpCCITT; // set to dump native CCITT files - bool outputPNG; // set to output in PNG format - bool outputTiff; // set to output in TIFF format - bool pageNames; // set to include page number in file names - int pageNum; // current page number - int imgNum; // current image number - bool ok; // set up ok? + // Sets the output filename with a given file extension + void setFilename(const char *fileExt); + void listImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool interpolate, bool inlineImg, ImageType imageType); + void writeImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool inlineImg); + void writeRawImage(Stream *str, const char *ext); + void writeImageFile(ImgWriter *writer, ImageFormat format, const char *ext, Stream *str, int width, int height, GfxImageColorMap *colorMap); + long getInlineImageLength(Stream *str, int width, int height, GfxImageColorMap *colorMap); + + char *fileRoot; // root of output file names + char *fileName; // buffer for output file names + bool listImages; // list images instead of dumping + bool dumpJPEG; // set to dump native JPEG files + bool dumpJP2; // set to dump native JPEG2000 files + bool dumpJBIG2; // set to dump native JBIG2 files + bool dumpCCITT; // set to dump native CCITT files + bool outputPNG; // set to output in PNG format + bool outputTiff; // set to output in TIFF format + bool pageNames; // set to include page number in file names + int pageNum; // current page number + int imgNum; // current image number + bool ok; // set up ok? }; #endif diff --git a/utils/InMemoryFile.cc b/utils/InMemoryFile.cc index 56225381..b4e09e60 100644 --- a/utils/InMemoryFile.cc +++ b/utils/InMemoryFile.cc @@ -20,14 +20,10 @@ #include <cstring> #include <sstream> -InMemoryFile::InMemoryFile() - : iohead(0) - , fptr(nullptr) -{ -} +InMemoryFile::InMemoryFile() : iohead(0), fptr(nullptr) { } #ifdef HAVE_IN_MEMORY_FILE_FOPENCOOKIE -ssize_t InMemoryFile::_read(char* buf, size_t sz) +ssize_t InMemoryFile::_read(char *buf, size_t sz) { auto toRead = std::min<size_t>(data.size() - iohead, sz); memcpy(&buf[0], &data[iohead], toRead); @@ -35,21 +31,27 @@ ssize_t InMemoryFile::_read(char* buf, size_t sz) return toRead; } -ssize_t InMemoryFile::_write(const char* buf, size_t sz) +ssize_t InMemoryFile::_write(const char *buf, size_t sz) { if (iohead + sz > data.size()) data.resize(iohead + sz); memcpy(&data[iohead], buf, sz); iohead += sz; - return sz; + return sz; } -int InMemoryFile::_seek(off64_t* offset, int whence) +int InMemoryFile::_seek(off64_t *offset, int whence) { switch (whence) { - case SEEK_SET: iohead = (*offset); break; - case SEEK_CUR: iohead += (*offset); break; - case SEEK_END: iohead -= (*offset); break; + case SEEK_SET: + iohead = (*offset); + break; + case SEEK_CUR: + iohead += (*offset); + break; + case SEEK_END: + iohead -= (*offset); + break; } (*offset) = std::min<off64_t>(std::max<off64_t>(iohead, 0l), data.size()); iohead = static_cast<size_t>(*offset); @@ -57,7 +59,7 @@ int InMemoryFile::_seek(off64_t* offset, int whence) } #endif // def HAVE_IN_MEMORY_FILE_FOPENCOOKIE -FILE* InMemoryFile::open(const char* mode) +FILE *InMemoryFile::open(const char *mode) { #ifdef HAVE_IN_MEMORY_FILE_FOPENCOOKIE if (fptr != nullptr) { @@ -65,14 +67,18 @@ FILE* InMemoryFile::open(const char* mode) return nullptr; // maybe there's some legit reason for it, whoever comes up with one can remove this line } static const cookie_io_functions_t methods = { - /* .read = */ [](void* self, char* buf, size_t sz) { return ((InMemoryFile*)self)->_read(buf, sz); }, - /* .write = */ [](void* self, const char* buf, size_t sz) { return ((InMemoryFile*)self)->_write(buf, sz); }, - /* .seek = */ [](void* self, off64_t* offset, int whence) { return ((InMemoryFile*)self)->_seek(offset, whence); }, - /* .close = */ [](void* self) { ((InMemoryFile*)self)->fptr = nullptr; return 0; }, + /* .read = */ [](void *self, char *buf, size_t sz) { return ((InMemoryFile *)self)->_read(buf, sz); }, + /* .write = */ [](void *self, const char *buf, size_t sz) { return ((InMemoryFile *)self)->_write(buf, sz); }, + /* .seek = */ [](void *self, off64_t *offset, int whence) { return ((InMemoryFile *)self)->_seek(offset, whence); }, + /* .close = */ + [](void *self) { + ((InMemoryFile *)self)->fptr = nullptr; + return 0; + }, }; return fptr = fopencookie(this, mode, methods); #else - fprintf (stderr, "If you can read this, your platform does not support the features necessary to achieve your goals."); + fprintf(stderr, "If you can read this, your platform does not support the features necessary to achieve your goals."); return nullptr; #endif } diff --git a/utils/InMemoryFile.h b/utils/InMemoryFile.h index 5a6ab9e8..5e16ad09 100644 --- a/utils/InMemoryFile.h +++ b/utils/InMemoryFile.h @@ -22,20 +22,21 @@ #include <vector> #if defined(__USE_GNU) && !defined(__ANDROID_API__) -# define HAVE_IN_MEMORY_FILE (1) -# define HAVE_IN_MEMORY_FILE_FOPENCOOKIE (1) // used internally +# define HAVE_IN_MEMORY_FILE (1) +# define HAVE_IN_MEMORY_FILE_FOPENCOOKIE (1) // used internally #endif -class InMemoryFile { +class InMemoryFile +{ private: size_t iohead; std::vector<char> data; FILE *fptr; #ifdef HAVE_IN_MEMORY_FILE_FOPENCOOKIE - ssize_t _read(char* buf, size_t sz); - ssize_t _write(const char* buf, size_t sz); - int _seek(off64_t* offset, int whence); + ssize_t _read(char *buf, size_t sz); + ssize_t _write(const char *buf, size_t sz); + int _seek(off64_t *offset, int whence); #endif public: @@ -44,10 +45,9 @@ public: public: /* Returns a file handle for this file. This is scoped to this object * and must be fclosed() by the caller before destruction. */ - FILE* open(const char* mode); + FILE *open(const char *mode); - const std::vector<char>& getBuffer() const - { return data; } + const std::vector<char> &getBuffer() const { return data; } }; #endif // IN_MEMORY_FILE_H diff --git a/utils/Win32Console.cc b/utils/Win32Console.cc index 60520ba5..ea46e17b 100644 --- a/utils/Win32Console.cc +++ b/utils/Win32Console.cc @@ -13,14 +13,14 @@ #ifdef _WIN32 -#include "goo/gmem.h" -#include "UTF.h" +# include "goo/gmem.h" +# include "UTF.h" -#define WIN32_CONSOLE_IMPL -#include "Win32Console.h" +# define WIN32_CONSOLE_IMPL +# include "Win32Console.h" -#include <windows.h> -#include <shellapi.h> +# include <windows.h> +# include <shellapi.h> static const int BUF_SIZE = 4096; static int bufLen = 0; @@ -36,132 +36,129 @@ static HANDLE consoleHandle = nullptr; // writes. static void flush(bool all = false) { - int nchars = 0; - - if (all || bufLen > BUF_SIZE/2) { - nchars = bufLen; - } else if (bufLen > 0) { - // find num chars up to and including last '\n' - for (nchars = bufLen; nchars > 0; --nchars) { - if (buf[nchars-1] == '\n') - break; + int nchars = 0; + + if (all || bufLen > BUF_SIZE / 2) { + nchars = bufLen; + } else if (bufLen > 0) { + // find num chars up to and including last '\n' + for (nchars = bufLen; nchars > 0; --nchars) { + if (buf[nchars - 1] == '\n') + break; + } } - } - - if (nchars > 0) { - DWORD wlen = utf8ToUtf16(buf, (uint16_t*)wbuf, BUF_SIZE, nchars); - WriteConsoleW(consoleHandle, wbuf, wlen, &wlen, nullptr); - if (nchars < bufLen) { - memmove(buf, buf + nchars, bufLen - nchars); - bufLen -= nchars; - } else { - bufLen = 0; + + if (nchars > 0) { + DWORD wlen = utf8ToUtf16(buf, (uint16_t *)wbuf, BUF_SIZE, nchars); + WriteConsoleW(consoleHandle, wbuf, wlen, &wlen, nullptr); + if (nchars < bufLen) { + memmove(buf, buf + nchars, bufLen - nchars); + bufLen -= nchars; + } else { + bufLen = 0; + } } - } } static inline bool streamIsConsole(FILE *stream) { - return ((stream == stdout && stdoutIsConsole) || (stream == stderr && stderrIsConsole)); + return ((stream == stdout && stdoutIsConsole) || (stream == stderr && stderrIsConsole)); } int win32_fprintf(FILE *stream, ...) { - va_list args; - int ret = 0; - - va_start(args, stream); - const char *format = va_arg(args, const char *); - if (streamIsConsole(stream)) { - ret = vsnprintf(buf + bufLen, BUF_SIZE - bufLen, format, args); - bufLen += ret; - if (ret >= BUF_SIZE - bufLen) { - // output was truncated - buf[BUF_SIZE - 1] = 0; - bufLen = BUF_SIZE - 1; + va_list args; + int ret = 0; + + va_start(args, stream); + const char *format = va_arg(args, const char *); + if (streamIsConsole(stream)) { + ret = vsnprintf(buf + bufLen, BUF_SIZE - bufLen, format, args); + bufLen += ret; + if (ret >= BUF_SIZE - bufLen) { + // output was truncated + buf[BUF_SIZE - 1] = 0; + bufLen = BUF_SIZE - 1; + } + flush(); + } else { + vfprintf(stream, format, args); } - flush(); - } else { - vfprintf(stream, format, args); - } - va_end(args); + va_end(args); - return ret; + return ret; } size_t win32_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream) { - size_t ret = 0; - - if (streamIsConsole(stream)) { - int n = size * nmemb; - if (n > BUF_SIZE - bufLen - 1) - n = BUF_SIZE - bufLen - 1; - memcpy(buf + bufLen, ptr, n); - bufLen += n; - buf[bufLen] = 0; - flush(); - } else { - ret = fwrite(ptr, size, nmemb, stream); - } - - return ret; -} + size_t ret = 0; + + if (streamIsConsole(stream)) { + int n = size * nmemb; + if (n > BUF_SIZE - bufLen - 1) + n = BUF_SIZE - bufLen - 1; + memcpy(buf + bufLen, ptr, n); + bufLen += n; + buf[bufLen] = 0; + flush(); + } else { + ret = fwrite(ptr, size, nmemb, stream); + } + return ret; +} Win32Console::Win32Console(int *argc, char **argv[]) { - LPWSTR *wargv; - fpos_t pos; - - argList = nullptr; - privateArgList = nullptr; - wargv = CommandLineToArgvW(GetCommandLineW(), &numArgs); - if (wargv) { - argList = new char*[numArgs]; - privateArgList = new char*[numArgs]; - for (int i = 0; i < numArgs; i++) { - argList[i] = utf16ToUtf8((uint16_t*)(wargv[i])); - // parseArgs will rearrange the argv list so we keep our own copy - // to use for freeing all the strings - privateArgList[i] = argList[i]; + LPWSTR *wargv; + fpos_t pos; + + argList = nullptr; + privateArgList = nullptr; + wargv = CommandLineToArgvW(GetCommandLineW(), &numArgs); + if (wargv) { + argList = new char *[numArgs]; + privateArgList = new char *[numArgs]; + for (int i = 0; i < numArgs; i++) { + argList[i] = utf16ToUtf8((uint16_t *)(wargv[i])); + // parseArgs will rearrange the argv list so we keep our own copy + // to use for freeing all the strings + privateArgList[i] = argList[i]; + } + LocalFree(wargv); + *argc = numArgs; + *argv = argList; } - LocalFree(wargv); - *argc = numArgs; - *argv = argList; - } - - bufLen = 0; - buf[0] = 0; - wbuf[0] = 0; - - // check if stdout or stderr redirected - // GetFileType() returns CHAR for console and special devices COMx, PRN, CON, NUL etc - // fgetpos() succeeds on all CHAR devices except console and CON. - - stdoutIsConsole = (GetFileType(GetStdHandle(STD_OUTPUT_HANDLE)) == FILE_TYPE_CHAR) - && (fgetpos(stdout, &pos) != 0); - - stderrIsConsole = (GetFileType(GetStdHandle(STD_ERROR_HANDLE)) == FILE_TYPE_CHAR) - && (fgetpos(stderr, &pos) != 0); - - // Need a handle to the console. Doesn't matter if we use stdout or stderr as - // long as the handle output is to the console. - if (stdoutIsConsole) - consoleHandle = GetStdHandle(STD_OUTPUT_HANDLE); - else if (stderrIsConsole) - consoleHandle = GetStdHandle(STD_ERROR_HANDLE); + + bufLen = 0; + buf[0] = 0; + wbuf[0] = 0; + + // check if stdout or stderr redirected + // GetFileType() returns CHAR for console and special devices COMx, PRN, CON, NUL etc + // fgetpos() succeeds on all CHAR devices except console and CON. + + stdoutIsConsole = (GetFileType(GetStdHandle(STD_OUTPUT_HANDLE)) == FILE_TYPE_CHAR) && (fgetpos(stdout, &pos) != 0); + + stderrIsConsole = (GetFileType(GetStdHandle(STD_ERROR_HANDLE)) == FILE_TYPE_CHAR) && (fgetpos(stderr, &pos) != 0); + + // Need a handle to the console. Doesn't matter if we use stdout or stderr as + // long as the handle output is to the console. + if (stdoutIsConsole) + consoleHandle = GetStdHandle(STD_OUTPUT_HANDLE); + else if (stderrIsConsole) + consoleHandle = GetStdHandle(STD_ERROR_HANDLE); } Win32Console::~Win32Console() { - flush(true); - if (argList) { - for (int i = 0; i < numArgs; i++) - gfree(privateArgList[i]); - delete[] argList; - delete[] privateArgList; - } + flush(true); + if (argList) { + for (int i = 0; i < numArgs; i++) + gfree(privateArgList[i]); + delete[] argList; + delete[] privateArgList; + } } #endif // _WIN32 diff --git a/utils/Win32Console.h b/utils/Win32Console.h index a1ef6efc..a2d13924 100644 --- a/utils/Win32Console.h +++ b/utils/Win32Console.h @@ -26,33 +26,34 @@ // Ensure stdio.h is included before redefining stdio functions. We need to provide // our own declarations for the redefined functions because win32 stdio.h functions // have DLL export decorations. -#include <cstdio> +# include <cstdio> -#ifndef WIN32_CONSOLE_IMPL // don't redefine in Win32Console.cc so we can call original functions -#define printf(...) win32_fprintf(stdout, __VA_ARGS__) -#define fprintf(stream, ...) win32_fprintf(stream, __VA_ARGS__) -#define puts(s) win32_fprintf(stdout, "%s\n", s) -#define fputs(s, stream) win32_fprintf(stream, "%s", s) -#define putc(c) win32_fprintf(stdout, "%c", c) -#define putchar(c) win32_fprintf(stdout, "%c", c) -#define fputc(c, stream) win32_fprintf(stream, "%c", c) -#define fwrite(ptr, size, nmemb, stream) win32_fwrite(ptr, size, nmemb, stream) -#endif +# ifndef WIN32_CONSOLE_IMPL // don't redefine in Win32Console.cc so we can call original functions +# define printf(...) win32_fprintf(stdout, __VA_ARGS__) +# define fprintf(stream, ...) win32_fprintf(stream, __VA_ARGS__) +# define puts(s) win32_fprintf(stdout, "%s\n", s) +# define fputs(s, stream) win32_fprintf(stream, "%s", s) +# define putc(c) win32_fprintf(stdout, "%c", c) +# define putchar(c) win32_fprintf(stdout, "%c", c) +# define fputc(c, stream) win32_fprintf(stream, "%c", c) +# define fwrite(ptr, size, nmemb, stream) win32_fwrite(ptr, size, nmemb, stream) +# endif extern "C" { - int win32_fprintf(FILE *stream, ...); - size_t win32_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream); +int win32_fprintf(FILE *stream, ...); +size_t win32_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream); } class Win32Console { public: - Win32Console(int *argc, char **argv[]); - ~Win32Console(); + Win32Console(int *argc, char **argv[]); + ~Win32Console(); + private: - int numArgs; - char **argList; - char **privateArgList; + int numArgs; + char **argList; + char **privateArgList; }; #else @@ -62,7 +63,7 @@ private: class Win32Console { public: - Win32Console(int *argc, char ***argv) {} + Win32Console(int *argc, char ***argv) { } }; #endif // _WIN32 diff --git a/utils/numberofcharacters.h b/utils/numberofcharacters.h index 1b01faba..3915b0d8 100644 --- a/utils/numberofcharacters.h +++ b/utils/numberofcharacters.h @@ -13,14 +13,13 @@ static int numberOfCharacters(unsigned int n) { - int charNum = 0; - while (n >= 10) - { - n = n / 10; + int charNum = 0; + while (n >= 10) { + n = n / 10; + charNum++; + } charNum++; - } - charNum++; - return charNum; + return charNum; } #endif diff --git a/utils/parseargs.cc b/utils/parseargs.cc index 9baa4567..89e9453f 100644 --- a/utils/parseargs.cc +++ b/utils/parseargs.cc @@ -36,185 +36,191 @@ static const ArgDesc *findArg(const ArgDesc *args, char *arg); static bool grabArg(const ArgDesc *arg, int i, int *argc, char *argv[]); -bool parseArgs(const ArgDesc *args, int *argc, char *argv[]) { - const ArgDesc *arg; - int i, j; - bool ok; - - ok = true; - i = 1; - while (i < *argc) { - if (!strcmp(argv[i], "--")) { - --*argc; - for (j = i; j < *argc; ++j) - argv[j] = argv[j+1]; - break; - } else if ((arg = findArg(args, argv[i]))) { - if (!grabArg(arg, i, argc, argv)) - ok = false; - } else { - ++i; +bool parseArgs(const ArgDesc *args, int *argc, char *argv[]) +{ + const ArgDesc *arg; + int i, j; + bool ok; + + ok = true; + i = 1; + while (i < *argc) { + if (!strcmp(argv[i], "--")) { + --*argc; + for (j = i; j < *argc; ++j) + argv[j] = argv[j + 1]; + break; + } else if ((arg = findArg(args, argv[i]))) { + if (!grabArg(arg, i, argc, argv)) + ok = false; + } else { + ++i; + } } - } - return ok; + return ok; } -void printUsage(const char *program, const char *otherArgs, const ArgDesc *args) { - const ArgDesc *arg; - const char *typ; - int w, w1; - - w = 0; - for (arg = args; arg->arg; ++arg) { - if ((w1 = strlen(arg->arg)) > w) - w = w1; - } - - fprintf(stderr, "Usage: %s [options]", program); - if (otherArgs) - fprintf(stderr, " %s", otherArgs); - fprintf(stderr, "\n"); - - for (arg = args; arg->arg; ++arg) { - fprintf(stderr, " %s", arg->arg); - w1 = 9 + w - strlen(arg->arg); - switch (arg->kind) { - case argInt: - case argIntDummy: - typ = " <int>"; - break; - case argFP: - case argFPDummy: - typ = " <fp>"; - break; - case argString: - case argStringDummy: - case argGooString: - typ = " <string>"; - break; - case argFlag: - case argFlagDummy: - default: - typ = ""; - break; +void printUsage(const char *program, const char *otherArgs, const ArgDesc *args) +{ + const ArgDesc *arg; + const char *typ; + int w, w1; + + w = 0; + for (arg = args; arg->arg; ++arg) { + if ((w1 = strlen(arg->arg)) > w) + w = w1; } - fprintf(stderr, "%-*s", w1, typ); - if (arg->usage) - fprintf(stderr, ": %s", arg->usage); + + fprintf(stderr, "Usage: %s [options]", program); + if (otherArgs) + fprintf(stderr, " %s", otherArgs); fprintf(stderr, "\n"); - } + + for (arg = args; arg->arg; ++arg) { + fprintf(stderr, " %s", arg->arg); + w1 = 9 + w - strlen(arg->arg); + switch (arg->kind) { + case argInt: + case argIntDummy: + typ = " <int>"; + break; + case argFP: + case argFPDummy: + typ = " <fp>"; + break; + case argString: + case argStringDummy: + case argGooString: + typ = " <string>"; + break; + case argFlag: + case argFlagDummy: + default: + typ = ""; + break; + } + fprintf(stderr, "%-*s", w1, typ); + if (arg->usage) + fprintf(stderr, ": %s", arg->usage); + fprintf(stderr, "\n"); + } } -static const ArgDesc *findArg(const ArgDesc *args, char *arg) { - const ArgDesc *p; +static const ArgDesc *findArg(const ArgDesc *args, char *arg) +{ + const ArgDesc *p; - for (p = args; p->arg; ++p) { - if (p->kind < argFlagDummy && !strcmp(p->arg, arg)) - return p; - } - return nullptr; + for (p = args; p->arg; ++p) { + if (p->kind < argFlagDummy && !strcmp(p->arg, arg)) + return p; + } + return nullptr; } -static bool grabArg(const ArgDesc *arg, int i, int *argc, char *argv[]) { - int n; - int j; - bool ok; - - ok = true; - n = 0; - switch (arg->kind) { - case argFlag: - *(bool *)arg->val = true; - n = 1; - break; - case argInt: - if (i + 1 < *argc && isInt(argv[i+1])) { - *(int *)arg->val = atoi(argv[i+1]); - n = 2; - } else { - ok = false; - n = 1; - } - break; - case argFP: - if (i + 1 < *argc && isFP(argv[i+1])) { - *(double *)arg->val = gatof(argv[i+1]); - n = 2; - } else { - ok = false; - n = 1; - } - break; - case argString: - if (i + 1 < *argc) { - strncpy((char *)arg->val, argv[i+1], arg->size - 1); - ((char *)arg->val)[arg->size - 1] = '\0'; - n = 2; - } else { - ok = false; - n = 1; +static bool grabArg(const ArgDesc *arg, int i, int *argc, char *argv[]) +{ + int n; + int j; + bool ok; + + ok = true; + n = 0; + switch (arg->kind) { + case argFlag: + *(bool *)arg->val = true; + n = 1; + break; + case argInt: + if (i + 1 < *argc && isInt(argv[i + 1])) { + *(int *)arg->val = atoi(argv[i + 1]); + n = 2; + } else { + ok = false; + n = 1; + } + break; + case argFP: + if (i + 1 < *argc && isFP(argv[i + 1])) { + *(double *)arg->val = gatof(argv[i + 1]); + n = 2; + } else { + ok = false; + n = 1; + } + break; + case argString: + if (i + 1 < *argc) { + strncpy((char *)arg->val, argv[i + 1], arg->size - 1); + ((char *)arg->val)[arg->size - 1] = '\0'; + n = 2; + } else { + ok = false; + n = 1; + } + break; + case argGooString: + if (i + 1 < *argc) { + ((GooString *)arg->val)->Set(argv[i + 1]); + n = 2; + } else { + ok = false; + n = 1; + } + break; + default: + fprintf(stderr, "Internal error in arg table\n"); + n = 1; + break; } - break; - case argGooString: - if (i + 1 < *argc) { - ((GooString*)arg->val)->Set(argv[i+1]); - n = 2; - } else { - ok = false; - n = 1; + if (n > 0) { + *argc -= n; + for (j = i; j < *argc; ++j) + argv[j] = argv[j + n]; } - break; - default: - fprintf(stderr, "Internal error in arg table\n"); - n = 1; - break; - } - if (n > 0) { - *argc -= n; - for (j = i; j < *argc; ++j) - argv[j] = argv[j+n]; - } - return ok; + return ok; } -bool isInt(const char *s) { - if (*s == '-' || *s == '+') - ++s; - while (isdigit(*s)) - ++s; - if (*s) - return false; - return true; +bool isInt(const char *s) +{ + if (*s == '-' || *s == '+') + ++s; + while (isdigit(*s)) + ++s; + if (*s) + return false; + return true; } -bool isFP(const char *s) { - int n; - - if (*s == '-' || *s == '+') - ++s; - n = 0; - while (isdigit(*s)) { - ++s; - ++n; - } - if (*s == '.') - ++s; - while (isdigit(*s)) { - ++s; - ++n; - } - if (n > 0 && (*s == 'e' || *s == 'E')) { - ++s; +bool isFP(const char *s) +{ + int n; + if (*s == '-' || *s == '+') - ++s; + ++s; n = 0; - if (!isdigit(*s)) - return false; - do { - ++s; - } while (isdigit(*s)); - } - if (*s) - return false; - return true; + while (isdigit(*s)) { + ++s; + ++n; + } + if (*s == '.') + ++s; + while (isdigit(*s)) { + ++s; + ++n; + } + if (n > 0 && (*s == 'e' || *s == 'E')) { + ++s; + if (*s == '-' || *s == '+') + ++s; + n = 0; + if (!isdigit(*s)) + return false; + do { + ++s; + } while (isdigit(*s)); + } + if (*s) + return false; + return true; } diff --git a/utils/parseargs.h b/utils/parseargs.h index c3ebb7cd..d927e56e 100644 --- a/utils/parseargs.h +++ b/utils/parseargs.h @@ -31,34 +31,36 @@ extern "C" { /* * Argument kinds. */ -typedef enum { - argFlag, /* flag (present / not-present) */ - /* [val: bool *] */ - argInt, /* integer arg */ - /* [val: int *] */ - argFP, /* floating point arg */ - /* [val: double *] */ - argString, /* string arg */ - /* [val: char *] */ - argGooString, /* string arg */ - /* [val: GooString *] */ - /* dummy entries -- these show up in the usage listing only; */ - /* useful for X args, for example */ - argFlagDummy, - argIntDummy, - argFPDummy, - argStringDummy +typedef enum +{ + argFlag, /* flag (present / not-present) */ + /* [val: bool *] */ + argInt, /* integer arg */ + /* [val: int *] */ + argFP, /* floating point arg */ + /* [val: double *] */ + argString, /* string arg */ + /* [val: char *] */ + argGooString, /* string arg */ + /* [val: GooString *] */ + /* dummy entries -- these show up in the usage listing only; */ + /* useful for X args, for example */ + argFlagDummy, + argIntDummy, + argFPDummy, + argStringDummy } ArgKind; /* * Argument descriptor. */ -typedef struct { - const char *arg; /* the command line switch */ - ArgKind kind; /* kind of arg */ - void *val; /* place to store value */ - int size; /* for argString: size of string */ - const char *usage; /* usage string */ +typedef struct +{ + const char *arg; /* the command line switch */ + ArgKind kind; /* kind of arg */ + void *val; /* place to store value */ + int size; /* for argString: size of string */ + const char *usage; /* usage string */ } ArgDesc; /* diff --git a/utils/pdfattach.cc b/utils/pdfattach.cc index f4f71cbb..192a0096 100644 --- a/utils/pdfattach.cc +++ b/utils/pdfattach.cc @@ -27,86 +27,79 @@ static bool doReplace = false; static bool printVersion = false; static bool printHelp = false; -static const ArgDesc argDesc[] = { - {"-replace", argFlag, &doReplace, 0, - "replace embedded file with same name (if it exists)"}, - {"-v", argFlag, &printVersion, 0, - "print copyright and version info"}, - {"-h", argFlag, &printHelp, 0, - "print usage information"}, - {"-help", argFlag, &printHelp, 0, - "print usage information"}, - {"--help", argFlag, &printHelp, 0, - "print usage information"}, - {"-?", argFlag, &printHelp, 0, - "print usage information"}, - { } -}; +static const ArgDesc argDesc[] = { { "-replace", argFlag, &doReplace, 0, "replace embedded file with same name (if it exists)" }, + { "-v", argFlag, &printVersion, 0, "print copyright and version info" }, + { "-h", argFlag, &printHelp, 0, "print usage information" }, + { "-help", argFlag, &printHelp, 0, "print usage information" }, + { "--help", argFlag, &printHelp, 0, "print usage information" }, + { "-?", argFlag, &printHelp, 0, "print usage information" }, + {} }; static bool fileExists(const char *filePath) { - FILE *f = openFile(filePath, "r"); - if (f != nullptr) { - fclose(f); - return true; - } - return false; + FILE *f = openFile(filePath, "r"); + if (f != nullptr) { + fclose(f); + return true; + } + return false; } -int main(int argc, char *argv[]) { - Win32Console win32Console(&argc, &argv); - - // parse args - const bool ok = parseArgs(argDesc, &argc, argv); - if (!ok || argc != 4 || printVersion || printHelp) { - fprintf(stderr, "pdfattach version %s\n", PACKAGE_VERSION); - fprintf(stderr, "%s\n", popplerCopyright); - fprintf(stderr, "%s\n", xpdfCopyright); - if (!printVersion) { - printUsage("pdfattach", "<input-PDF-file> <file-to-attach> <output-PDF-file>", argDesc); +int main(int argc, char *argv[]) +{ + Win32Console win32Console(&argc, &argv); + + // parse args + const bool ok = parseArgs(argDesc, &argc, argv); + if (!ok || argc != 4 || printVersion || printHelp) { + fprintf(stderr, "pdfattach version %s\n", PACKAGE_VERSION); + fprintf(stderr, "%s\n", popplerCopyright); + fprintf(stderr, "%s\n", xpdfCopyright); + if (!printVersion) { + printUsage("pdfattach", "<input-PDF-file> <file-to-attach> <output-PDF-file>", argDesc); + } + return 99; } - return 99; - } - const GooString pdfFileName(argv[1]); - const GooString attachFilePath(argv[2]); - - // init GlobalParams - globalParams = std::make_unique<GlobalParams>(); - - // open PDF file - std::unique_ptr<PDFDoc> doc(PDFDocFactory().createPDFDoc(pdfFileName, nullptr, nullptr)); - - if (!doc->isOk()) { - fprintf(stderr, "Couldn't open %s\n", pdfFileName.c_str()); - return 1; - } - - std::unique_ptr<GooFile> attachFile(GooFile::open(&attachFilePath)); - if (!attachFile) { - fprintf(stderr, "Couldn't open %s\n", attachFilePath.c_str()); - return 2; - } - - if (fileExists(argv[3])) { - fprintf(stderr, "File %s already exists.\n", argv[3]); - return 3; - } - - const std::string attachFileName = gbasename(attachFilePath.c_str()); - - if (!doReplace && doc->getCatalog()->hasEmbeddedFile(attachFileName)) { - fprintf(stderr, "There is already an embedded file named %s.\n", attachFileName.c_str()); - return 4; - } - - doc->getCatalog()->addEmbeddedFile(attachFile.get(), attachFileName); - - const GooString outputPdfFilePath(argv[3]); - const int saveResult = doc->saveAs(&outputPdfFilePath); - if (saveResult != errNone) { - fprintf(stderr, "Couldn't save the file properly.\n"); - return 5; - } - - return 0; + const GooString pdfFileName(argv[1]); + const GooString attachFilePath(argv[2]); + + // init GlobalParams + globalParams = std::make_unique<GlobalParams>(); + + // open PDF file + std::unique_ptr<PDFDoc> doc(PDFDocFactory().createPDFDoc(pdfFileName, nullptr, nullptr)); + + if (!doc->isOk()) { + fprintf(stderr, "Couldn't open %s\n", pdfFileName.c_str()); + return 1; + } + + std::unique_ptr<GooFile> attachFile(GooFile::open(&attachFilePath)); + if (!attachFile) { + fprintf(stderr, "Couldn't open %s\n", attachFilePath.c_str()); + return 2; + } + + if (fileExists(argv[3])) { + fprintf(stderr, "File %s already exists.\n", argv[3]); + return 3; + } + + const std::string attachFileName = gbasename(attachFilePath.c_str()); + + if (!doReplace && doc->getCatalog()->hasEmbeddedFile(attachFileName)) { + fprintf(stderr, "There is already an embedded file named %s.\n", attachFileName.c_str()); + return 4; + } + + doc->getCatalog()->addEmbeddedFile(attachFile.get(), attachFileName); + + const GooString outputPdfFilePath(argv[3]); + const int saveResult = doc->saveAs(&outputPdfFilePath); + if (saveResult != errNone) { + fprintf(stderr, "Couldn't save the file properly.\n"); + return 5; + } + + return 0; } diff --git a/utils/pdfdetach.cc b/utils/pdfdetach.cc index 9e3efca5..78846ea4 100644 --- a/utils/pdfdetach.cc +++ b/utils/pdfdetach.cc @@ -55,299 +55,282 @@ static char userPassword[33] = "\001"; static bool printVersion = false; static bool printHelp = false; -static const ArgDesc argDesc[] = { - {"-list", argFlag, &doList, 0, - "list all embedded files"}, - {"-save", argInt, &saveNum, 0, - "save the specified embedded file (file number)"}, - {"-savefile",argString, &saveFile, sizeof(saveFile), - "save the specified embedded file (file name)"}, - {"-saveall", argFlag, &saveAll, 0, - "save all embedded files"}, - {"-o", argString, savePath, sizeof(savePath), - "file name for the saved embedded file"}, - {"-enc", argString, textEncName, sizeof(textEncName), - "output text encoding name"}, - {"-opw", argString, ownerPassword, sizeof(ownerPassword), - "owner password (for encrypted files)"}, - {"-upw", argString, userPassword, sizeof(userPassword), - "user password (for encrypted files)"}, - {"-v", argFlag, &printVersion, 0, - "print copyright and version info"}, - {"-h", argFlag, &printHelp, 0, - "print usage information"}, - {"-help", argFlag, &printHelp, 0, - "print usage information"}, - {"--help", argFlag, &printHelp, 0, - "print usage information"}, - {"-?", argFlag, &printHelp, 0, - "print usage information"}, - { } -}; +static const ArgDesc argDesc[] = { { "-list", argFlag, &doList, 0, "list all embedded files" }, + { "-save", argInt, &saveNum, 0, "save the specified embedded file (file number)" }, + { "-savefile", argString, &saveFile, sizeof(saveFile), "save the specified embedded file (file name)" }, + { "-saveall", argFlag, &saveAll, 0, "save all embedded files" }, + { "-o", argString, savePath, sizeof(savePath), "file name for the saved embedded file" }, + { "-enc", argString, textEncName, sizeof(textEncName), "output text encoding name" }, + { "-opw", argString, ownerPassword, sizeof(ownerPassword), "owner password (for encrypted files)" }, + { "-upw", argString, userPassword, sizeof(userPassword), "user password (for encrypted files)" }, + { "-v", argFlag, &printVersion, 0, "print copyright and version info" }, + { "-h", argFlag, &printHelp, 0, "print usage information" }, + { "-help", argFlag, &printHelp, 0, "print usage information" }, + { "--help", argFlag, &printHelp, 0, "print usage information" }, + { "-?", argFlag, &printHelp, 0, "print usage information" }, + {} }; -int main(int argc, char *argv[]) { - GooString *fileName; - const UnicodeMap *uMap; - GooString *ownerPW, *userPW; - PDFDoc *doc; - char uBuf[8]; - char path[1024]; - char *p; - bool ok; - bool hasSaveFile; - int exitCode; - std::vector<FileSpec*> embeddedFiles; - int nFiles, nPages, n, i, j; - FileSpec *fileSpec; - Page *page; - Annots *annots; - Annot *annot; - const GooString *s1; - Unicode u; - bool isUnicode; +int main(int argc, char *argv[]) +{ + GooString *fileName; + const UnicodeMap *uMap; + GooString *ownerPW, *userPW; + PDFDoc *doc; + char uBuf[8]; + char path[1024]; + char *p; + bool ok; + bool hasSaveFile; + int exitCode; + std::vector<FileSpec *> embeddedFiles; + int nFiles, nPages, n, i, j; + FileSpec *fileSpec; + Page *page; + Annots *annots; + Annot *annot; + const GooString *s1; + Unicode u; + bool isUnicode; - Win32Console win32Console(&argc, &argv); - exitCode = 99; + Win32Console win32Console(&argc, &argv); + exitCode = 99; - // parse args - ok = parseArgs(argDesc, &argc, argv); - hasSaveFile = strlen(saveFile) > 0; - if ((doList ? 1 : 0) + - ((saveNum != 0) ? 1 : 0) + - ((hasSaveFile != 0) ? 1 : 0) + - (saveAll ? 1 : 0) != 1) { - ok = false; - } - if (!ok || argc != 2 || printVersion || printHelp) { - fprintf(stderr, "pdfdetach version %s\n", PACKAGE_VERSION); - fprintf(stderr, "%s\n", popplerCopyright); - fprintf(stderr, "%s\n", xpdfCopyright); - if (!printVersion) { - printUsage("pdfdetach", "<PDF-file>", argDesc); + // parse args + ok = parseArgs(argDesc, &argc, argv); + hasSaveFile = strlen(saveFile) > 0; + if ((doList ? 1 : 0) + ((saveNum != 0) ? 1 : 0) + ((hasSaveFile != 0) ? 1 : 0) + (saveAll ? 1 : 0) != 1) { + ok = false; } - goto err0; - } - fileName = new GooString(argv[1]); - - // read config file - globalParams = std::make_unique<GlobalParams>(); - if (textEncName[0]) { - globalParams->setTextEncoding(textEncName); - } - - // get mapping to output encoding - if (!(uMap = globalParams->getTextEncoding())) { - error(errConfig, -1, "Couldn't get text encoding"); - delete fileName; - goto err0; - } - - // open PDF file - if (ownerPassword[0] != '\001') { - ownerPW = new GooString(ownerPassword); - } else { - ownerPW = nullptr; - } - if (userPassword[0] != '\001') { - userPW = new GooString(userPassword); - } else { - userPW = nullptr; - } + if (!ok || argc != 2 || printVersion || printHelp) { + fprintf(stderr, "pdfdetach version %s\n", PACKAGE_VERSION); + fprintf(stderr, "%s\n", popplerCopyright); + fprintf(stderr, "%s\n", xpdfCopyright); + if (!printVersion) { + printUsage("pdfdetach", "<PDF-file>", argDesc); + } + goto err0; + } + fileName = new GooString(argv[1]); - doc = PDFDocFactory().createPDFDoc(*fileName, ownerPW, userPW); + // read config file + globalParams = std::make_unique<GlobalParams>(); + if (textEncName[0]) { + globalParams->setTextEncoding(textEncName); + } - if (userPW) { - delete userPW; - } - if (ownerPW) { - delete ownerPW; - } - if (!doc->isOk()) { - exitCode = 1; - goto err2; - } + // get mapping to output encoding + if (!(uMap = globalParams->getTextEncoding())) { + error(errConfig, -1, "Couldn't get text encoding"); + delete fileName; + goto err0; + } - for (i = 0; i < doc->getCatalog()->numEmbeddedFiles(); ++i) - embeddedFiles.push_back(doc->getCatalog()->embeddedFile(i)); + // open PDF file + if (ownerPassword[0] != '\001') { + ownerPW = new GooString(ownerPassword); + } else { + ownerPW = nullptr; + } + if (userPassword[0] != '\001') { + userPW = new GooString(userPassword); + } else { + userPW = nullptr; + } - nPages = doc->getCatalog()->getNumPages(); - for (i = 0; i < nPages; ++i) { - page = doc->getCatalog()->getPage(i + 1); - if (!page) - continue; - annots = page->getAnnots(); - if (!annots) - break; + doc = PDFDocFactory().createPDFDoc(*fileName, ownerPW, userPW); - for (j = 0; j < annots->getNumAnnots(); ++j) { - annot = annots->getAnnot(j); - if (annot->getType() != Annot::typeFileAttachment) - continue; - embeddedFiles.push_back(new FileSpec(static_cast<AnnotFileAttachment *>(annot)->getFile())); + if (userPW) { + delete userPW; + } + if (ownerPW) { + delete ownerPW; + } + if (!doc->isOk()) { + exitCode = 1; + goto err2; } - } - nFiles = embeddedFiles.size(); + for (i = 0; i < doc->getCatalog()->numEmbeddedFiles(); ++i) + embeddedFiles.push_back(doc->getCatalog()->embeddedFile(i)); - // list embedded files - if (doList) { - printf("%d embedded files\n", nFiles); - for (i = 0; i < nFiles; ++i) { - fileSpec = embeddedFiles[i]; - printf("%d: ", i+1); - s1 = fileSpec->getFileName(); - if (!s1) { - exitCode = 3; - goto err2; - } - if (s1->hasUnicodeMarker()) { - isUnicode = true; - j = 2; - } else { - isUnicode = false; - j = 0; - } - while (j < s1->getLength()) { - if (isUnicode) { - u = ((s1->getChar(j) & 0xff) << 8) | (s1->getChar(j+1) & 0xff); - j += 2; - } else { - u = pdfDocEncoding[s1->getChar(j) & 0xff]; - ++j; + nPages = doc->getCatalog()->getNumPages(); + for (i = 0; i < nPages; ++i) { + page = doc->getCatalog()->getPage(i + 1); + if (!page) + continue; + annots = page->getAnnots(); + if (!annots) + break; + + for (j = 0; j < annots->getNumAnnots(); ++j) { + annot = annots->getAnnot(j); + if (annot->getType() != Annot::typeFileAttachment) + continue; + embeddedFiles.push_back(new FileSpec(static_cast<AnnotFileAttachment *>(annot)->getFile())); } - n = uMap->mapUnicode(u, uBuf, sizeof(uBuf)); - fwrite(uBuf, 1, n, stdout); - } - fputc('\n', stdout); } - // save all embedded files - } else if (saveAll) { - for (i = 0; i < nFiles; ++i) { - fileSpec = embeddedFiles[i]; - if (savePath[0]) { - n = strlen(savePath); - if (n > (int)sizeof(path) - 2) { - n = sizeof(path) - 2; - } - memcpy(path, savePath, n); - path[n] = '/'; - p = path + n + 1; - } else { - p = path; - } - s1 = fileSpec->getFileName(); - if (!s1) { - exitCode = 3; - goto err2; - } - if (s1->hasUnicodeMarker()) { - isUnicode = true; - j = 2; - } else { - isUnicode = false; - j = 0; - } - while (j < s1->getLength()) { - if (isUnicode) { - u = ((s1->getChar(j) & 0xff) << 8) | (s1->getChar(j+1) & 0xff); - j += 2; - } else { - u = pdfDocEncoding[s1->getChar(j) & 0xff]; - ++j; + nFiles = embeddedFiles.size(); + + // list embedded files + if (doList) { + printf("%d embedded files\n", nFiles); + for (i = 0; i < nFiles; ++i) { + fileSpec = embeddedFiles[i]; + printf("%d: ", i + 1); + s1 = fileSpec->getFileName(); + if (!s1) { + exitCode = 3; + goto err2; + } + if (s1->hasUnicodeMarker()) { + isUnicode = true; + j = 2; + } else { + isUnicode = false; + j = 0; + } + while (j < s1->getLength()) { + if (isUnicode) { + u = ((s1->getChar(j) & 0xff) << 8) | (s1->getChar(j + 1) & 0xff); + j += 2; + } else { + u = pdfDocEncoding[s1->getChar(j) & 0xff]; + ++j; + } + n = uMap->mapUnicode(u, uBuf, sizeof(uBuf)); + fwrite(uBuf, 1, n, stdout); + } + fputc('\n', stdout); } - n = uMap->mapUnicode(u, uBuf, sizeof(uBuf)); - if (p + n >= path + sizeof(path)) - break; - memcpy(p, uBuf, n); - p += n; - } - *p = '\0'; - auto *embFile = fileSpec->getEmbeddedFile(); - if (!embFile || !embFile->isOk()) { - exitCode = 3; - goto err2; - } - if (!embFile->save(path)) { - error(errIO, -1, "Error saving embedded file as '{0:s}'", p); - exitCode = 2; - goto err2; - } - } + // save all embedded files + } else if (saveAll) { + for (i = 0; i < nFiles; ++i) { + fileSpec = embeddedFiles[i]; + if (savePath[0]) { + n = strlen(savePath); + if (n > (int)sizeof(path) - 2) { + n = sizeof(path) - 2; + } + memcpy(path, savePath, n); + path[n] = '/'; + p = path + n + 1; + } else { + p = path; + } + s1 = fileSpec->getFileName(); + if (!s1) { + exitCode = 3; + goto err2; + } + if (s1->hasUnicodeMarker()) { + isUnicode = true; + j = 2; + } else { + isUnicode = false; + j = 0; + } + while (j < s1->getLength()) { + if (isUnicode) { + u = ((s1->getChar(j) & 0xff) << 8) | (s1->getChar(j + 1) & 0xff); + j += 2; + } else { + u = pdfDocEncoding[s1->getChar(j) & 0xff]; + ++j; + } + n = uMap->mapUnicode(u, uBuf, sizeof(uBuf)); + if (p + n >= path + sizeof(path)) + break; + memcpy(p, uBuf, n); + p += n; + } + *p = '\0'; - // save an embedded file - } else { - if (hasSaveFile) { - for (i = 0; i < nFiles; ++i) { - fileSpec = embeddedFiles[i]; - s1 = fileSpec->getFileName(); - if (strcmp(s1->c_str(), saveFile) == 0) { - saveNum = i + 1; - break; + auto *embFile = fileSpec->getEmbeddedFile(); + if (!embFile || !embFile->isOk()) { + exitCode = 3; + goto err2; + } + if (!embFile->save(path)) { + error(errIO, -1, "Error saving embedded file as '{0:s}'", p); + exitCode = 2; + goto err2; + } } - } - } - if (saveNum < 1 || saveNum > nFiles) { - error(errCommandLine, -1, hasSaveFile ? "Invalid file name" : "Invalid file number"); - goto err2; - } - fileSpec = embeddedFiles[saveNum - 1]; - if (savePath[0]) { - p = savePath; + // save an embedded file } else { - p = path; - s1 = fileSpec->getFileName(); - if (!s1) { - exitCode = 3; - goto err2; - } - if (s1->hasUnicodeMarker()) { - isUnicode = true; - j = 2; - } else { - isUnicode = false; - j = 0; - } - while (j < s1->getLength()) { - if (isUnicode) { - u = ((s1->getChar(j) & 0xff) << 8) | (s1->getChar(j+1) & 0xff); - j += 2; + if (hasSaveFile) { + for (i = 0; i < nFiles; ++i) { + fileSpec = embeddedFiles[i]; + s1 = fileSpec->getFileName(); + if (strcmp(s1->c_str(), saveFile) == 0) { + saveNum = i + 1; + break; + } + } + } + if (saveNum < 1 || saveNum > nFiles) { + error(errCommandLine, -1, hasSaveFile ? "Invalid file name" : "Invalid file number"); + goto err2; + } + + fileSpec = embeddedFiles[saveNum - 1]; + if (savePath[0]) { + p = savePath; } else { - u = pdfDocEncoding[s1->getChar(j) & 0xff]; - ++j; + p = path; + s1 = fileSpec->getFileName(); + if (!s1) { + exitCode = 3; + goto err2; + } + if (s1->hasUnicodeMarker()) { + isUnicode = true; + j = 2; + } else { + isUnicode = false; + j = 0; + } + while (j < s1->getLength()) { + if (isUnicode) { + u = ((s1->getChar(j) & 0xff) << 8) | (s1->getChar(j + 1) & 0xff); + j += 2; + } else { + u = pdfDocEncoding[s1->getChar(j) & 0xff]; + ++j; + } + n = uMap->mapUnicode(u, uBuf, sizeof(uBuf)); + if (p + n >= path + sizeof(path)) + break; + memcpy(p, uBuf, n); + p += n; + } + *p = '\0'; + p = path; } - n = uMap->mapUnicode(u, uBuf, sizeof(uBuf)); - if (p + n >= path + sizeof(path)) - break; - memcpy(p, uBuf, n); - p += n; - } - *p = '\0'; - p = path; - } - auto *embFile = fileSpec->getEmbeddedFile(); - if (!embFile || !embFile->isOk()) { - exitCode = 3; - goto err2; - } - if (!embFile->save(p)) { - error(errIO, -1, "Error saving embedded file as '{0:s}'", p); - exitCode = 2; - goto err2; + auto *embFile = fileSpec->getEmbeddedFile(); + if (!embFile || !embFile->isOk()) { + exitCode = 3; + goto err2; + } + if (!embFile->save(p)) { + error(errIO, -1, "Error saving embedded file as '{0:s}'", p); + exitCode = 2; + goto err2; + } } - } - exitCode = 0; + exitCode = 0; - // clean up - err2: - for (auto& file : embeddedFiles) - delete file; - delete doc; - err0: + // clean up +err2: + for (auto &file : embeddedFiles) + delete file; + delete doc; +err0: - return exitCode; + return exitCode; } diff --git a/utils/pdffonts.cc b/utils/pdffonts.cc index 598a4913..09a09d5b 100644 --- a/utils/pdffonts.cc +++ b/utils/pdffonts.cc @@ -44,20 +44,7 @@ #include "FontInfo.h" #include "Win32Console.h" -static const char *fontTypeNames[] = { - "unknown", - "Type 1", - "Type 1C", - "Type 1C (OT)", - "Type 3", - "TrueType", - "TrueType (OT)", - "CID Type 0", - "CID Type 0C", - "CID Type 0C (OT)", - "CID TrueType", - "CID TrueType (OT)" -}; +static const char *fontTypeNames[] = { "unknown", "Type 1", "Type 1C", "Type 1C (OT)", "Type 3", "TrueType", "TrueType (OT)", "CID Type 0", "CID Type 0C", "CID Type 0C (OT)", "CID TrueType", "CID TrueType (OT)" }; static int firstPage = 1; static int lastPage = 0; @@ -67,135 +54,112 @@ static char userPassword[33] = "\001"; static bool printVersion = false; static bool printHelp = false; -static const ArgDesc argDesc[] = { - {"-f", argInt, &firstPage, 0, - "first page to examine"}, - {"-l", argInt, &lastPage, 0, - "last page to examine"}, - {"-subst", argFlag, &showSubst, 0, - "show font substitutions"}, - {"-opw", argString, ownerPassword, sizeof(ownerPassword), - "owner password (for encrypted files)"}, - {"-upw", argString, userPassword, sizeof(userPassword), - "user password (for encrypted files)"}, - {"-v", argFlag, &printVersion, 0, - "print copyright and version info"}, - {"-h", argFlag, &printHelp, 0, - "print usage information"}, - {"-help", argFlag, &printHelp, 0, - "print usage information"}, - {"--help", argFlag, &printHelp, 0, - "print usage information"}, - {"-?", argFlag, &printHelp, 0, - "print usage information"}, - {} -}; - -int main(int argc, char *argv[]) { - std::unique_ptr<GooString> ownerPW, userPW; - bool ok; - - Win32Console win32Console(&argc, &argv); - - // parse args - ok = parseArgs(argDesc, &argc, argv); - if (!ok || argc != 2 || printVersion || printHelp) { - fprintf(stderr, "pdffonts version %s\n", PACKAGE_VERSION); - fprintf(stderr, "%s\n", popplerCopyright); - fprintf(stderr, "%s\n", xpdfCopyright); - if (!printVersion) { - printUsage("pdffonts", "<PDF-file>", argDesc); +static const ArgDesc argDesc[] = { { "-f", argInt, &firstPage, 0, "first page to examine" }, + { "-l", argInt, &lastPage, 0, "last page to examine" }, + { "-subst", argFlag, &showSubst, 0, "show font substitutions" }, + { "-opw", argString, ownerPassword, sizeof(ownerPassword), "owner password (for encrypted files)" }, + { "-upw", argString, userPassword, sizeof(userPassword), "user password (for encrypted files)" }, + { "-v", argFlag, &printVersion, 0, "print copyright and version info" }, + { "-h", argFlag, &printHelp, 0, "print usage information" }, + { "-help", argFlag, &printHelp, 0, "print usage information" }, + { "--help", argFlag, &printHelp, 0, "print usage information" }, + { "-?", argFlag, &printHelp, 0, "print usage information" }, + {} }; + +int main(int argc, char *argv[]) +{ + std::unique_ptr<GooString> ownerPW, userPW; + bool ok; + + Win32Console win32Console(&argc, &argv); + + // parse args + ok = parseArgs(argDesc, &argc, argv); + if (!ok || argc != 2 || printVersion || printHelp) { + fprintf(stderr, "pdffonts version %s\n", PACKAGE_VERSION); + fprintf(stderr, "%s\n", popplerCopyright); + fprintf(stderr, "%s\n", xpdfCopyright); + if (!printVersion) { + printUsage("pdffonts", "<PDF-file>", argDesc); + } + if (printVersion || printHelp) + return 0; + return 99; + } + + std::string fileName(argv[1]); + if (fileName == "-") { + fileName = "fd://0"; + } + + // read config file + globalParams = std::make_unique<GlobalParams>(); + + // open PDF file + if (ownerPassword[0] != '\001') { + ownerPW = std::make_unique<GooString>(ownerPassword); + } + if (userPassword[0] != '\001') { + userPW = std::make_unique<GooString>(userPassword); + } + + auto doc = std::unique_ptr<PDFDoc>(PDFDocFactory().createPDFDoc(GooString(fileName), ownerPW.get(), userPW.get())); + + if (!doc->isOk()) { + return 1; } - if (printVersion || printHelp) - return 0; - return 99; - } - - std::string fileName(argv[1]); - if (fileName == "-") { - fileName = "fd://0"; - } - - // read config file - globalParams = std::make_unique<GlobalParams>(); - - // open PDF file - if (ownerPassword[0] != '\001') { - ownerPW = std::make_unique<GooString>(ownerPassword); - } - if (userPassword[0] != '\001') { - userPW = std::make_unique<GooString>(userPassword); - } - - auto doc = std::unique_ptr<PDFDoc>(PDFDocFactory().createPDFDoc(GooString(fileName), ownerPW.get(), userPW.get())); - - if (!doc->isOk()) { - return 1; - } - - // get page range - if (firstPage < 1) { - firstPage = 1; - } - if (lastPage < 1 || lastPage > doc->getNumPages()) { - lastPage = doc->getNumPages(); - } - if (lastPage < firstPage) { - fprintf(stderr, - "Wrong page range given: the first page (%d) can not be after the last page (%d).\n", - firstPage, lastPage); - return 99; - } - - // get the fonts - { - FontInfoScanner scanner(doc.get(), firstPage - 1); - const std::vector<FontInfo*> fonts = scanner.scan(lastPage - firstPage + 1); - - if (showSubst) { - // print the font substitutions - printf("name object ID substitute font substitute font file\n"); - printf("------------------------------------ --------- ------------------------------------ ------------------------------------\n"); - for (const FontInfo* font : fonts) { - if (font->getFile()) { - printf("%-36s", - font->getName() ? font->getName()->c_str() : "[none]"); - const Ref fontRef = font->getRef(); - if (fontRef.gen >= 100000) { - printf(" [none]"); - } else { - printf(" %6d %2d", fontRef.num, fontRef.gen); + + // get page range + if (firstPage < 1) { + firstPage = 1; + } + if (lastPage < 1 || lastPage > doc->getNumPages()) { + lastPage = doc->getNumPages(); + } + if (lastPage < firstPage) { + fprintf(stderr, "Wrong page range given: the first page (%d) can not be after the last page (%d).\n", firstPage, lastPage); + return 99; + } + + // get the fonts + { + FontInfoScanner scanner(doc.get(), firstPage - 1); + const std::vector<FontInfo *> fonts = scanner.scan(lastPage - firstPage + 1); + + if (showSubst) { + // print the font substitutions + printf("name object ID substitute font substitute font file\n"); + printf("------------------------------------ --------- ------------------------------------ ------------------------------------\n"); + for (const FontInfo *font : fonts) { + if (font->getFile()) { + printf("%-36s", font->getName() ? font->getName()->c_str() : "[none]"); + const Ref fontRef = font->getRef(); + if (fontRef.gen >= 100000) { + printf(" [none]"); + } else { + printf(" %6d %2d", fontRef.num, fontRef.gen); + } + printf(" %-36s %s\n", font->getSubstituteName() ? font->getSubstituteName()->c_str() : "[none]", font->getFile()->c_str()); + } + delete font; + } + } else { + // print the font info + printf("name type encoding emb sub uni object ID\n"); + printf("------------------------------------ ----------------- ---------------- --- --- --- ---------\n"); + for (const FontInfo *font : fonts) { + printf("%-36s %-17s %-16s %-3s %-3s %-3s", font->getName() ? font->getName()->c_str() : "[none]", fontTypeNames[font->getType()], font->getEncoding()->c_str(), font->getEmbedded() ? "yes" : "no", + font->getSubset() ? "yes" : "no", font->getToUnicode() ? "yes" : "no"); + const Ref fontRef = font->getRef(); + if (fontRef.gen >= 100000) { + printf(" [none]\n"); + } else { + printf(" %6d %2d\n", fontRef.num, fontRef.gen); + } + delete font; } - printf(" %-36s %s\n", - font->getSubstituteName() ? font->getSubstituteName()->c_str() : "[none]", - font->getFile()->c_str()); - } - delete font; - } - } else { - // print the font info - printf("name type encoding emb sub uni object ID\n"); - printf("------------------------------------ ----------------- ---------------- --- --- --- ---------\n"); - for (const FontInfo* font : fonts) { - printf("%-36s %-17s %-16s %-3s %-3s %-3s", - font->getName() ? font->getName()->c_str() : "[none]", - fontTypeNames[font->getType()], - font->getEncoding()->c_str(), - font->getEmbedded() ? "yes" : "no", - font->getSubset() ? "yes" : "no", - font->getToUnicode() ? "yes" : "no"); - const Ref fontRef = font->getRef(); - if (fontRef.gen >= 100000) { - printf(" [none]\n"); - } else { - printf(" %6d %2d\n", fontRef.num, fontRef.gen); - } - delete font; } } - } - return 0; + return 0; } - - diff --git a/utils/pdfimages.cc b/utils/pdfimages.cc index 6ae92b34..46cebf98 100644 --- a/utils/pdfimages.cc +++ b/utils/pdfimages.cc @@ -69,173 +69,148 @@ static bool quiet = false; static bool printVersion = false; static bool printHelp = false; -static const ArgDesc argDesc[] = { - {"-f", argInt, &firstPage, 0, - "first page to convert"}, - {"-l", argInt, &lastPage, 0, - "last page to convert"}, +static const ArgDesc argDesc[] = { { "-f", argInt, &firstPage, 0, "first page to convert" }, + { "-l", argInt, &lastPage, 0, "last page to convert" }, #ifdef ENABLE_LIBPNG - {"-png", argFlag, &enablePNG, 0, - "change the default output format to PNG"}, + { "-png", argFlag, &enablePNG, 0, "change the default output format to PNG" }, #endif #ifdef ENABLE_LIBTIFF - {"-tiff", argFlag, &enableTiff, 0, - "change the default output format to TIFF"}, + { "-tiff", argFlag, &enableTiff, 0, "change the default output format to TIFF" }, #endif - {"-j", argFlag, &dumpJPEG, 0, - "write JPEG images as JPEG files"}, - {"-jp2", argFlag, &dumpJP2, 0, - "write JPEG2000 images as JP2 files"}, - {"-jbig2", argFlag, &dumpJBIG2, 0, - "write JBIG2 images as JBIG2 files"}, - {"-ccitt", argFlag, &dumpCCITT, 0, - "write CCITT images as CCITT files"}, - {"-all", argFlag, &allFormats, 0, - "equivalent to -png -tiff -j -jp2 -jbig2 -ccitt"}, - {"-list", argFlag, &listImages, 0, - "print list of images instead of saving"}, - {"-opw", argString, ownerPassword, sizeof(ownerPassword), - "owner password (for encrypted files)"}, - {"-upw", argString, userPassword, sizeof(userPassword), - "user password (for encrypted files)"}, - {"-p", argFlag, &pageNames, 0, - "include page numbers in output file names"}, - {"-q", argFlag, &quiet, 0, - "don't print any messages or errors"}, - {"-v", argFlag, &printVersion, 0, - "print copyright and version info"}, - {"-h", argFlag, &printHelp, 0, - "print usage information"}, - {"-help", argFlag, &printHelp, 0, - "print usage information"}, - {"--help", argFlag, &printHelp, 0, - "print usage information"}, - {"-?", argFlag, &printHelp, 0, - "print usage information"}, - {} -}; - -int main(int argc, char *argv[]) { - PDFDoc *doc; - GooString *fileName; - char *imgRoot = nullptr; - GooString *ownerPW, *userPW; - ImageOutputDev *imgOut; - bool ok; - int exitCode; - - Win32Console win32Console(&argc, &argv); - exitCode = 99; - - // parse args - ok = parseArgs(argDesc, &argc, argv); - if (!ok || (listImages && argc != 2) || (!listImages && argc != 3) || printVersion || printHelp) { - fprintf(stderr, "pdfimages version %s\n", PACKAGE_VERSION); - fprintf(stderr, "%s\n", popplerCopyright); - fprintf(stderr, "%s\n", xpdfCopyright); - if (!printVersion) { - printUsage("pdfimages", "<PDF-file> <image-root>", argDesc); + { "-j", argFlag, &dumpJPEG, 0, "write JPEG images as JPEG files" }, + { "-jp2", argFlag, &dumpJP2, 0, "write JPEG2000 images as JP2 files" }, + { "-jbig2", argFlag, &dumpJBIG2, 0, "write JBIG2 images as JBIG2 files" }, + { "-ccitt", argFlag, &dumpCCITT, 0, "write CCITT images as CCITT files" }, + { "-all", argFlag, &allFormats, 0, "equivalent to -png -tiff -j -jp2 -jbig2 -ccitt" }, + { "-list", argFlag, &listImages, 0, "print list of images instead of saving" }, + { "-opw", argString, ownerPassword, sizeof(ownerPassword), "owner password (for encrypted files)" }, + { "-upw", argString, userPassword, sizeof(userPassword), "user password (for encrypted files)" }, + { "-p", argFlag, &pageNames, 0, "include page numbers in output file names" }, + { "-q", argFlag, &quiet, 0, "don't print any messages or errors" }, + { "-v", argFlag, &printVersion, 0, "print copyright and version info" }, + { "-h", argFlag, &printHelp, 0, "print usage information" }, + { "-help", argFlag, &printHelp, 0, "print usage information" }, + { "--help", argFlag, &printHelp, 0, "print usage information" }, + { "-?", argFlag, &printHelp, 0, "print usage information" }, + {} }; + +int main(int argc, char *argv[]) +{ + PDFDoc *doc; + GooString *fileName; + char *imgRoot = nullptr; + GooString *ownerPW, *userPW; + ImageOutputDev *imgOut; + bool ok; + int exitCode; + + Win32Console win32Console(&argc, &argv); + exitCode = 99; + + // parse args + ok = parseArgs(argDesc, &argc, argv); + if (!ok || (listImages && argc != 2) || (!listImages && argc != 3) || printVersion || printHelp) { + fprintf(stderr, "pdfimages version %s\n", PACKAGE_VERSION); + fprintf(stderr, "%s\n", popplerCopyright); + fprintf(stderr, "%s\n", xpdfCopyright); + if (!printVersion) { + printUsage("pdfimages", "<PDF-file> <image-root>", argDesc); + } + if (printVersion || printHelp) + exitCode = 0; + goto err0; } - if (printVersion || printHelp) - exitCode = 0; - goto err0; - } - fileName = new GooString(argv[1]); - if (!listImages) - imgRoot = argv[2]; - - // read config file - globalParams = std::make_unique<GlobalParams>(); - if (quiet) { - globalParams->setErrQuiet(quiet); - } - - // open PDF file - if (ownerPassword[0] != '\001') { - ownerPW = new GooString(ownerPassword); - } else { - ownerPW = nullptr; - } - if (userPassword[0] != '\001') { - userPW = new GooString(userPassword); - } else { - userPW = nullptr; - } - if (fileName->cmp("-") == 0) { - delete fileName; - fileName = new GooString("fd://0"); - } - - doc = PDFDocFactory().createPDFDoc(*fileName, ownerPW, userPW); - delete fileName; - - if (userPW) { - delete userPW; - } - if (ownerPW) { - delete ownerPW; - } - if (!doc->isOk()) { - exitCode = 1; - goto err1; - } - - // check for copy permission + fileName = new GooString(argv[1]); + if (!listImages) + imgRoot = argv[2]; + + // read config file + globalParams = std::make_unique<GlobalParams>(); + if (quiet) { + globalParams->setErrQuiet(quiet); + } + + // open PDF file + if (ownerPassword[0] != '\001') { + ownerPW = new GooString(ownerPassword); + } else { + ownerPW = nullptr; + } + if (userPassword[0] != '\001') { + userPW = new GooString(userPassword); + } else { + userPW = nullptr; + } + if (fileName->cmp("-") == 0) { + delete fileName; + fileName = new GooString("fd://0"); + } + + doc = PDFDocFactory().createPDFDoc(*fileName, ownerPW, userPW); + delete fileName; + + if (userPW) { + delete userPW; + } + if (ownerPW) { + delete ownerPW; + } + if (!doc->isOk()) { + exitCode = 1; + goto err1; + } + + // check for copy permission #ifdef ENFORCE_PERMISSIONS - if (!doc->okToCopy()) { - error(errNotAllowed, -1, "Copying of images from this document is not allowed."); - exitCode = 3; - goto err1; - } + if (!doc->okToCopy()) { + error(errNotAllowed, -1, "Copying of images from this document is not allowed."); + exitCode = 3; + goto err1; + } #endif - // get page range - if (firstPage < 1) - firstPage = 1; - if (firstPage > doc->getNumPages()) { - error(errCommandLine, -1, - "Wrong page range given: the first page ({0:d}) can not be larger then the number of pages in the document ({1:d}).", - firstPage, doc->getNumPages()); - goto err1; - } - if (lastPage < 1 || lastPage > doc->getNumPages()) - lastPage = doc->getNumPages(); - if (lastPage < firstPage) { - error(errCommandLine, -1, - "Wrong page range given: the first page ({0:d}) can not be after the last page ({1:d}).", - firstPage, lastPage); - goto err1; - } - - // write image files - imgOut = new ImageOutputDev(imgRoot, pageNames, listImages); - if (imgOut->isOk()) { - if (allFormats) { - imgOut->enablePNG(true); - imgOut->enableTiff(true); - imgOut->enableJpeg(true); - imgOut->enableJpeg2000(true); - imgOut->enableJBig2(true); - imgOut->enableCCITT(true); - } else { - imgOut->enablePNG(enablePNG); - imgOut->enableTiff(enableTiff); - imgOut->enableJpeg(dumpJPEG); - imgOut->enableJpeg2000(dumpJP2); - imgOut->enableJBig2(dumpJBIG2); - imgOut->enableCCITT(dumpCCITT); + // get page range + if (firstPage < 1) + firstPage = 1; + if (firstPage > doc->getNumPages()) { + error(errCommandLine, -1, "Wrong page range given: the first page ({0:d}) can not be larger then the number of pages in the document ({1:d}).", firstPage, doc->getNumPages()); + goto err1; + } + if (lastPage < 1 || lastPage > doc->getNumPages()) + lastPage = doc->getNumPages(); + if (lastPage < firstPage) { + error(errCommandLine, -1, "Wrong page range given: the first page ({0:d}) can not be after the last page ({1:d}).", firstPage, lastPage); + goto err1; + } + + // write image files + imgOut = new ImageOutputDev(imgRoot, pageNames, listImages); + if (imgOut->isOk()) { + if (allFormats) { + imgOut->enablePNG(true); + imgOut->enableTiff(true); + imgOut->enableJpeg(true); + imgOut->enableJpeg2000(true); + imgOut->enableJBig2(true); + imgOut->enableCCITT(true); + } else { + imgOut->enablePNG(enablePNG); + imgOut->enableTiff(enableTiff); + imgOut->enableJpeg(dumpJPEG); + imgOut->enableJpeg2000(dumpJP2); + imgOut->enableJBig2(dumpJBIG2); + imgOut->enableCCITT(dumpCCITT); + } + doc->displayPages(imgOut, firstPage, lastPage, 72, 72, 0, true, false, false); } - doc->displayPages(imgOut, firstPage, lastPage, 72, 72, 0, - true, false, false); - } - delete imgOut; + delete imgOut; - exitCode = 0; + exitCode = 0; - // clean up - err1: - delete doc; - err0: + // clean up +err1: + delete doc; +err0: - return exitCode; + return exitCode; } diff --git a/utils/pdfinfo.cc b/utils/pdfinfo.cc index 2b5eb02e..6180dbea 100644 --- a/utils/pdfinfo.cc +++ b/utils/pdfinfo.cc @@ -69,7 +69,6 @@ #include "StructElement.h" #include "Win32Console.h" - static int firstPage = 1; static int lastPage = 0; static bool printBoxes = false; @@ -87,906 +86,871 @@ static bool printStructure = false; static bool printStructureText = false; static bool printDests = false; -static const ArgDesc argDesc[] = { - {"-f", argInt, &firstPage, 0, - "first page to convert"}, - {"-l", argInt, &lastPage, 0, - "last page to convert"}, - {"-box", argFlag, &printBoxes, 0, - "print the page bounding boxes"}, - {"-meta", argFlag, &printMetadata, 0, - "print the document metadata (XML)"}, - {"-js", argFlag, &printJS, 0, - "print all JavaScript in the PDF"}, - {"-struct", argFlag, &printStructure, 0, - "print the logical document structure (for tagged files)"}, - {"-struct-text", argFlag, &printStructureText, 0, - "print text contents along with document structure (for tagged files)"}, - {"-isodates", argFlag, &isoDates, 0, - "print the dates in ISO-8601 format"}, - {"-rawdates", argFlag, &rawDates, 0, - "print the undecoded date strings directly from the PDF file"}, - {"-dests", argFlag, &printDests, 0, - "print all named destinations in the PDF"}, - {"-enc", argString, textEncName, sizeof(textEncName), - "output text encoding name"}, - {"-listenc",argFlag, &printEnc, 0, - "list available encodings"}, - {"-opw", argString, ownerPassword, sizeof(ownerPassword), - "owner password (for encrypted files)"}, - {"-upw", argString, userPassword, sizeof(userPassword), - "user password (for encrypted files)"}, - {"-v", argFlag, &printVersion, 0, - "print copyright and version info"}, - {"-h", argFlag, &printHelp, 0, - "print usage information"}, - {"-help", argFlag, &printHelp, 0, - "print usage information"}, - {"--help", argFlag, &printHelp, 0, - "print usage information"}, - {"-?", argFlag, &printHelp, 0, - "print usage information"}, - {} -}; - -static void printInfoString(Dict *infoDict, const char *key, const char *text, - const UnicodeMap *uMap) { - const GooString *s1; - Unicode *u; - char buf[8]; - int i, n, len; - - Object obj = infoDict->lookup(key); - if (obj.isString()) { - fputs(text, stdout); - s1 = obj.getString(); - len = TextStringToUCS4(s1, &u); - for (i = 0; i < len; i++) { - n = uMap->mapUnicode(u[i], buf, sizeof(buf)); - fwrite(buf, 1, n, stdout); - } - gfree(u); - fputc('\n', stdout); - } +static const ArgDesc argDesc[] = { { "-f", argInt, &firstPage, 0, "first page to convert" }, + { "-l", argInt, &lastPage, 0, "last page to convert" }, + { "-box", argFlag, &printBoxes, 0, "print the page bounding boxes" }, + { "-meta", argFlag, &printMetadata, 0, "print the document metadata (XML)" }, + { "-js", argFlag, &printJS, 0, "print all JavaScript in the PDF" }, + { "-struct", argFlag, &printStructure, 0, "print the logical document structure (for tagged files)" }, + { "-struct-text", argFlag, &printStructureText, 0, "print text contents along with document structure (for tagged files)" }, + { "-isodates", argFlag, &isoDates, 0, "print the dates in ISO-8601 format" }, + { "-rawdates", argFlag, &rawDates, 0, "print the undecoded date strings directly from the PDF file" }, + { "-dests", argFlag, &printDests, 0, "print all named destinations in the PDF" }, + { "-enc", argString, textEncName, sizeof(textEncName), "output text encoding name" }, + { "-listenc", argFlag, &printEnc, 0, "list available encodings" }, + { "-opw", argString, ownerPassword, sizeof(ownerPassword), "owner password (for encrypted files)" }, + { "-upw", argString, userPassword, sizeof(userPassword), "user password (for encrypted files)" }, + { "-v", argFlag, &printVersion, 0, "print copyright and version info" }, + { "-h", argFlag, &printHelp, 0, "print usage information" }, + { "-help", argFlag, &printHelp, 0, "print usage information" }, + { "--help", argFlag, &printHelp, 0, "print usage information" }, + { "-?", argFlag, &printHelp, 0, "print usage information" }, + {} }; + +static void printInfoString(Dict *infoDict, const char *key, const char *text, const UnicodeMap *uMap) +{ + const GooString *s1; + Unicode *u; + char buf[8]; + int i, n, len; + + Object obj = infoDict->lookup(key); + if (obj.isString()) { + fputs(text, stdout); + s1 = obj.getString(); + len = TextStringToUCS4(s1, &u); + for (i = 0; i < len; i++) { + n = uMap->mapUnicode(u[i], buf, sizeof(buf)); + fwrite(buf, 1, n, stdout); + } + gfree(u); + fputc('\n', stdout); + } } -static void printInfoDate(Dict *infoDict, const char *key, const char *text) { - const char *s; - int year, mon, day, hour, min, sec, tz_hour, tz_minute; - char tz; - struct tm tmStruct; - time_t time; - char buf[256]; - - Object obj = infoDict->lookup(key); - if (obj.isString()) { - fputs(text, stdout); - s = obj.getString()->c_str(); - // TODO do something with the timezone info - if ( parseDateString( s, &year, &mon, &day, &hour, &min, &sec, &tz, &tz_hour, &tz_minute ) ) { - tmStruct.tm_year = year - 1900; - tmStruct.tm_mon = mon - 1; - tmStruct.tm_mday = day; - tmStruct.tm_hour = hour; - tmStruct.tm_min = min; - tmStruct.tm_sec = sec; - tmStruct.tm_wday = -1; - tmStruct.tm_yday = -1; - tmStruct.tm_isdst = -1; - // compute the tm_wday and tm_yday fields - time = timegm(&tmStruct); - if (time != (time_t)-1) { - int offset = (tz_hour*60 + tz_minute)*60; - if (tz == '-') - offset *= -1; - time -= offset; - localtime_r(&time, &tmStruct); - strftime(buf, sizeof(buf), "%c %Z", &tmStruct); - fputs(buf, stdout); - } else { - fputs(s, stdout); - } - } else { - fputs(s, stdout); +static void printInfoDate(Dict *infoDict, const char *key, const char *text) +{ + const char *s; + int year, mon, day, hour, min, sec, tz_hour, tz_minute; + char tz; + struct tm tmStruct; + time_t time; + char buf[256]; + + Object obj = infoDict->lookup(key); + if (obj.isString()) { + fputs(text, stdout); + s = obj.getString()->c_str(); + // TODO do something with the timezone info + if (parseDateString(s, &year, &mon, &day, &hour, &min, &sec, &tz, &tz_hour, &tz_minute)) { + tmStruct.tm_year = year - 1900; + tmStruct.tm_mon = mon - 1; + tmStruct.tm_mday = day; + tmStruct.tm_hour = hour; + tmStruct.tm_min = min; + tmStruct.tm_sec = sec; + tmStruct.tm_wday = -1; + tmStruct.tm_yday = -1; + tmStruct.tm_isdst = -1; + // compute the tm_wday and tm_yday fields + time = timegm(&tmStruct); + if (time != (time_t)-1) { + int offset = (tz_hour * 60 + tz_minute) * 60; + if (tz == '-') + offset *= -1; + time -= offset; + localtime_r(&time, &tmStruct); + strftime(buf, sizeof(buf), "%c %Z", &tmStruct); + fputs(buf, stdout); + } else { + fputs(s, stdout); + } + } else { + fputs(s, stdout); + } + fputc('\n', stdout); } - fputc('\n', stdout); - } } static void printISODate(Dict *infoDict, const char *key, const char *text) { - const char *s; - int year, mon, day, hour, min, sec, tz_hour, tz_minute; - char tz; - - Object obj = infoDict->lookup(key); - if (obj.isString()) { - fputs(text, stdout); - s = obj.getString()->c_str(); - if ( parseDateString( s, &year, &mon, &day, &hour, &min, &sec, &tz, &tz_hour, &tz_minute ) ) { - fprintf(stdout, "%04d-%02d-%02dT%02d:%02d:%02d", year, mon, day, hour, min, sec); - if (tz_hour == 0 && tz_minute == 0) { - fprintf(stdout, "Z"); - } else { - fprintf(stdout, "%c%02d", tz, tz_hour); - if (tz_minute) - fprintf(stdout, ":%02d", tz_minute); - } - } else { - fputs(s, stdout); + const char *s; + int year, mon, day, hour, min, sec, tz_hour, tz_minute; + char tz; + + Object obj = infoDict->lookup(key); + if (obj.isString()) { + fputs(text, stdout); + s = obj.getString()->c_str(); + if (parseDateString(s, &year, &mon, &day, &hour, &min, &sec, &tz, &tz_hour, &tz_minute)) { + fprintf(stdout, "%04d-%02d-%02dT%02d:%02d:%02d", year, mon, day, hour, min, sec); + if (tz_hour == 0 && tz_minute == 0) { + fprintf(stdout, "Z"); + } else { + fprintf(stdout, "%c%02d", tz, tz_hour); + if (tz_minute) + fprintf(stdout, ":%02d", tz_minute); + } + } else { + fputs(s, stdout); + } + fputc('\n', stdout); } - fputc('\n', stdout); - } } -static void printBox(const char *text, const PDFRectangle *box) { - printf("%s%8.2f %8.2f %8.2f %8.2f\n", - text, box->x1, box->y1, box->x2, box->y2); +static void printBox(const char *text, const PDFRectangle *box) +{ + printf("%s%8.2f %8.2f %8.2f %8.2f\n", text, box->x1, box->y1, box->x2, box->y2); } -static void printIndent(unsigned indent) { - while (indent--) { - putchar(' '); - putchar(' '); - } +static void printIndent(unsigned indent) +{ + while (indent--) { + putchar(' '); + putchar(' '); + } } static void printAttribute(const Attribute *attribute, unsigned indent) { - printIndent(indent); - printf(" /%s ", attribute->getTypeName()); - if (attribute->getType() == Attribute::UserProperty) { - GooString *name = attribute->getName(); - printf("(%s) ", name->c_str()); - delete name; - } - attribute->getValue()->print(stdout); - if (attribute->getFormattedValue()) { - printf(" \"%s\"", attribute->getFormattedValue()); - } - if (attribute->isHidden()) { - printf(" [hidden]"); - } + printIndent(indent); + printf(" /%s ", attribute->getTypeName()); + if (attribute->getType() == Attribute::UserProperty) { + GooString *name = attribute->getName(); + printf("(%s) ", name->c_str()); + delete name; + } + attribute->getValue()->print(stdout); + if (attribute->getFormattedValue()) { + printf(" \"%s\"", attribute->getFormattedValue()); + } + if (attribute->isHidden()) { + printf(" [hidden]"); + } } -static void printStruct(const StructElement *element, unsigned indent) { - if (element->isObjectRef()) { - printIndent(indent); - printf("Object %i %i\n", element->getObjectRef().num, element->getObjectRef().gen); - return; - } +static void printStruct(const StructElement *element, unsigned indent) +{ + if (element->isObjectRef()) { + printIndent(indent); + printf("Object %i %i\n", element->getObjectRef().num, element->getObjectRef().gen); + return; + } - if (printStructureText && element->isContent()) { - GooString *text = element->getText(false); - printIndent(indent); - if (text) { - printf("\"%s\"\n", text->c_str()); - } else { - printf("(No content?)\n"); - } - delete text; - } - - if (!element->isContent()) { - printIndent(indent); - printf("%s", element->getTypeName()); - if (element->getID()) { - printf(" <%s>", element->getID()->c_str()); - } - if (element->getTitle()) { - printf(" \"%s\"", element->getTitle()->c_str()); - } - if (element->getRevision() > 0) { - printf(" r%u", element->getRevision()); - } - if (element->isInline() || element->isBlock()) { - printf(" (%s)", element->isInline() ? "inline" : "block"); - } - if (element->getNumAttributes()) { - putchar(':'); - for (unsigned i = 0; i < element->getNumAttributes(); i++) { - putchar('\n'); - printAttribute(element->getAttribute(i), indent + 1); - } - } - - putchar('\n'); - for (unsigned i = 0; i < element->getNumChildren(); i++) { - printStruct(element->getChild(i), indent + 1); - } - } + if (printStructureText && element->isContent()) { + GooString *text = element->getText(false); + printIndent(indent); + if (text) { + printf("\"%s\"\n", text->c_str()); + } else { + printf("(No content?)\n"); + } + delete text; + } + + if (!element->isContent()) { + printIndent(indent); + printf("%s", element->getTypeName()); + if (element->getID()) { + printf(" <%s>", element->getID()->c_str()); + } + if (element->getTitle()) { + printf(" \"%s\"", element->getTitle()->c_str()); + } + if (element->getRevision() > 0) { + printf(" r%u", element->getRevision()); + } + if (element->isInline() || element->isBlock()) { + printf(" (%s)", element->isInline() ? "inline" : "block"); + } + if (element->getNumAttributes()) { + putchar(':'); + for (unsigned i = 0; i < element->getNumAttributes(); i++) { + putchar('\n'); + printAttribute(element->getAttribute(i), indent + 1); + } + } + + putchar('\n'); + for (unsigned i = 0; i < element->getNumChildren(); i++) { + printStruct(element->getChild(i), indent + 1); + } + } } -struct GooStringCompare { - bool operator() (GooString* lhs, GooString* rhs) const { - return lhs->cmp(const_cast<GooString*>(rhs)) < 0; - } +struct GooStringCompare +{ + bool operator()(GooString *lhs, GooString *rhs) const { return lhs->cmp(const_cast<GooString *>(rhs)) < 0; } }; -static void printLinkDest(const std::unique_ptr<LinkDest>& dest) { - GooString s; +static void printLinkDest(const std::unique_ptr<LinkDest> &dest) +{ + GooString s; - switch (dest->getKind()) { + switch (dest->getKind()) { case destXYZ: - s.append("[ XYZ "); - if (dest->getChangeLeft()) { - s.appendf("{0:4.0g} ", dest->getLeft()); - } else { - s.append("null "); - } - if (dest->getChangeTop()) { - s.appendf("{0:4.0g} ", dest->getTop()); - } else { - s.append("null "); - } - if (dest->getChangeZoom()) { - s.appendf("{0:4.2f} ", dest->getZoom()); - } else { - s.append("null "); - } - break; + s.append("[ XYZ "); + if (dest->getChangeLeft()) { + s.appendf("{0:4.0g} ", dest->getLeft()); + } else { + s.append("null "); + } + if (dest->getChangeTop()) { + s.appendf("{0:4.0g} ", dest->getTop()); + } else { + s.append("null "); + } + if (dest->getChangeZoom()) { + s.appendf("{0:4.2f} ", dest->getZoom()); + } else { + s.append("null "); + } + break; case destFit: - s.append("[ Fit "); - break; + s.append("[ Fit "); + break; case destFitH: - if (dest->getChangeTop()) { - s.appendf("[ FitH {0:4.0g} ", dest->getTop()); - } else { - s.append("[ FitH null "); - } - break; + if (dest->getChangeTop()) { + s.appendf("[ FitH {0:4.0g} ", dest->getTop()); + } else { + s.append("[ FitH null "); + } + break; case destFitV: - if (dest->getChangeLeft()) { - s.appendf("[ FitV {0:4.0g} ", dest->getLeft()); - } else { - s.append("[ FitV null "); - } - break; + if (dest->getChangeLeft()) { + s.appendf("[ FitV {0:4.0g} ", dest->getLeft()); + } else { + s.append("[ FitV null "); + } + break; case destFitR: - s.appendf("[ FitR {0:4.0g} {1:4.0g} {2:4.0g} {3:4.0g} ", - dest->getLeft(), - dest->getBottom(), - dest->getRight(), - dest->getTop()); - break; + s.appendf("[ FitR {0:4.0g} {1:4.0g} {2:4.0g} {3:4.0g} ", dest->getLeft(), dest->getBottom(), dest->getRight(), dest->getTop()); + break; case destFitB: - s.append("[ FitB "); - break; + s.append("[ FitB "); + break; case destFitBH: - if (dest->getChangeTop()) { - s.appendf("[ FitBH {0:4.0g} ", dest->getTop()); - } else { - s.append("[ FitBH null "); - } - break; + if (dest->getChangeTop()) { + s.appendf("[ FitBH {0:4.0g} ", dest->getTop()); + } else { + s.append("[ FitBH null "); + } + break; case destFitBV: - if (dest->getChangeLeft()) { - s.appendf("[ FitBV {0:4.0g} ", dest->getLeft()); - } else { - s.append("[ FitBV null "); - } - break; - } - - s.append(" "); - s.setChar(26, ']'); - s.setChar(27, '\0'); - printf("%s", s.c_str()); -} - -static void printDestinations(PDFDoc *doc, const UnicodeMap *uMap) { - std::map<Ref,std::map<GooString*,std::unique_ptr<LinkDest>,GooStringCompare> > map; - - int numDests = doc->getCatalog()->numDestNameTree(); - for (int i = 0; i < numDests; i++) { - GooString *name = new GooString(doc->getCatalog()->getDestNameTreeName(i)); - std::unique_ptr<LinkDest> dest = doc->getCatalog()->getDestNameTreeDest(i); - if (dest && dest->isPageRef()) { - Ref pageRef = dest->getPageRef(); - map[pageRef].insert(std::make_pair(name, std::move(dest))); - } else { - delete name; + if (dest->getChangeLeft()) { + s.appendf("[ FitBV {0:4.0g} ", dest->getLeft()); + } else { + s.append("[ FitBV null "); + } + break; } - } - numDests = doc->getCatalog()->numDests(); - for (int i = 0; i < numDests; i++) { - GooString *name = new GooString(doc->getCatalog()->getDestsName(i)); - std::unique_ptr<LinkDest> dest = doc->getCatalog()->getDestsDest(i); - if (dest && dest->isPageRef()) { - Ref pageRef = dest->getPageRef(); - map[pageRef].insert(std::make_pair(name, std::move(dest))); - } else { - delete name; - } - } - - printf("Page Destination Name\n"); - for (int i = firstPage; i <= lastPage; i++) { - Ref *ref = doc->getCatalog()->getPageRef(i); - if (ref) { - auto pageDests = map.find(*ref); - if (pageDests != map.end()) { - for (auto& it: pageDests->second) { - printf("%4d ", i); - printLinkDest(it.second); - printf(" \""); - Unicode *u; - char buf[8]; - const int len = TextStringToUCS4(it.first, &u); - for (int j = 0; j < len; j++) { - const int n = uMap->mapUnicode(u[j], buf, sizeof(buf)); - fwrite(buf, 1, n, stdout); - } - gfree(u); - printf("\"\n"); - delete it.first; - } - } - } - } + s.append(" "); + s.setChar(26, ']'); + s.setChar(27, '\0'); + printf("%s", s.c_str()); } -static void printPdfSubtype(PDFDoc *doc, const UnicodeMap *uMap) { - const Object info = doc->getDocInfo(); - if (info.isDict()) { - const PDFSubtype pdftype = doc->getPDFSubtype(); - - if ((pdftype == subtypeNull) | (pdftype == subtypeNone)) { - return; +static void printDestinations(PDFDoc *doc, const UnicodeMap *uMap) +{ + std::map<Ref, std::map<GooString *, std::unique_ptr<LinkDest>, GooStringCompare>> map; + + int numDests = doc->getCatalog()->numDestNameTree(); + for (int i = 0; i < numDests; i++) { + GooString *name = new GooString(doc->getCatalog()->getDestNameTreeName(i)); + std::unique_ptr<LinkDest> dest = doc->getCatalog()->getDestNameTreeDest(i); + if (dest && dest->isPageRef()) { + Ref pageRef = dest->getPageRef(); + map[pageRef].insert(std::make_pair(name, std::move(dest))); + } else { + delete name; + } } - std::unique_ptr<GooString> part; - std::unique_ptr<GooString> abbr; - std::unique_ptr<GooString> standard; - std::unique_ptr<GooString> typeExp; - std::unique_ptr<GooString> confExp; + numDests = doc->getCatalog()->numDests(); + for (int i = 0; i < numDests; i++) { + GooString *name = new GooString(doc->getCatalog()->getDestsName(i)); + std::unique_ptr<LinkDest> dest = doc->getCatalog()->getDestsDest(i); + if (dest && dest->isPageRef()) { + Ref pageRef = dest->getPageRef(); + map[pageRef].insert(std::make_pair(name, std::move(dest))); + } else { + delete name; + } + } - // Form title from PDFSubtype - switch (pdftype) - { - case subtypePDFA: - printInfoString(info.getDict(), "GTS_PDFA1Version", "PDF subtype: ", uMap); - typeExp = std::make_unique<GooString>("ISO 19005 - Electronic document file format for long-term preservation (PDF/A)"); - standard = std::make_unique<GooString>("ISO 19005"); - abbr = std::make_unique<GooString>("PDF/A"); - break; - case subtypePDFE: - printInfoString(info.getDict(), "GTS_PDFEVersion", "PDF subtype: ", uMap); - typeExp = std::make_unique<GooString>("ISO 24517 - Engineering document format using PDF (PDF/E)"); - standard = std::make_unique<GooString>("ISO 24517"); - abbr = std::make_unique<GooString>("PDF/E"); - break; - case subtypePDFUA: - printInfoString(info.getDict(), "GTS_PDFUAVersion", "PDF subtype: ", uMap); - typeExp = std::make_unique<GooString>("ISO 14289 - Electronic document file format enhancement for accessibility (PDF/UA)"); - standard = std::make_unique<GooString>("ISO 14289"); - abbr = std::make_unique<GooString>("PDF/UA"); - break; - case subtypePDFVT: - printInfoString(info.getDict(), "GTS_PDFVTVersion", "PDF subtype: ", uMap); - typeExp = std::make_unique<GooString>("ISO 16612 - Electronic document file format for variable data exchange (PDF/VT)"); - standard = std::make_unique<GooString>("ISO 16612"); - abbr = std::make_unique<GooString>("PDF/VT"); - break; - case subtypePDFX: - printInfoString(info.getDict(), "GTS_PDFXVersion", "PDF subtype: ", uMap); - typeExp = std::make_unique<GooString>("ISO 15930 - Electronic document file format for prepress digital data exchange (PDF/X)"); - standard = std::make_unique<GooString>("ISO 15930"); - abbr = std::make_unique<GooString>("PDF/X"); - break; - case subtypeNone: - case subtypeNull: - default: - return; + printf("Page Destination Name\n"); + for (int i = firstPage; i <= lastPage; i++) { + Ref *ref = doc->getCatalog()->getPageRef(i); + if (ref) { + auto pageDests = map.find(*ref); + if (pageDests != map.end()) { + for (auto &it : pageDests->second) { + printf("%4d ", i); + printLinkDest(it.second); + printf(" \""); + Unicode *u; + char buf[8]; + const int len = TextStringToUCS4(it.first, &u); + for (int j = 0; j < len; j++) { + const int n = uMap->mapUnicode(u[j], buf, sizeof(buf)); + fwrite(buf, 1, n, stdout); + } + gfree(u); + printf("\"\n"); + delete it.first; + } + } + } } +} - // Form the abbreviation from PDFSubtypePart and PDFSubtype - const PDFSubtypePart subpart = doc->getPDFSubtypePart(); - switch (pdftype) { - case subtypePDFX: - switch (subpart) { - case subtypePart1: - abbr->append("-1:2001"); - break; - case subtypePart2: - abbr->append("-2"); - break; - case subtypePart3: - abbr->append("-3:2002"); - break; - case subtypePart4: - abbr->append("-1:2003"); - break; - case subtypePart5: - abbr->append("-2"); +static void printPdfSubtype(PDFDoc *doc, const UnicodeMap *uMap) +{ + const Object info = doc->getDocInfo(); + if (info.isDict()) { + const PDFSubtype pdftype = doc->getPDFSubtype(); + + if ((pdftype == subtypeNull) | (pdftype == subtypeNone)) { + return; + } + + std::unique_ptr<GooString> part; + std::unique_ptr<GooString> abbr; + std::unique_ptr<GooString> standard; + std::unique_ptr<GooString> typeExp; + std::unique_ptr<GooString> confExp; + + // Form title from PDFSubtype + switch (pdftype) { + case subtypePDFA: + printInfoString(info.getDict(), "GTS_PDFA1Version", "PDF subtype: ", uMap); + typeExp = std::make_unique<GooString>("ISO 19005 - Electronic document file format for long-term preservation (PDF/A)"); + standard = std::make_unique<GooString>("ISO 19005"); + abbr = std::make_unique<GooString>("PDF/A"); break; - case subtypePart6: - abbr->append("-3:2003"); + case subtypePDFE: + printInfoString(info.getDict(), "GTS_PDFEVersion", "PDF subtype: ", uMap); + typeExp = std::make_unique<GooString>("ISO 24517 - Engineering document format using PDF (PDF/E)"); + standard = std::make_unique<GooString>("ISO 24517"); + abbr = std::make_unique<GooString>("PDF/E"); break; - case subtypePart7: - abbr->append("-4"); + case subtypePDFUA: + printInfoString(info.getDict(), "GTS_PDFUAVersion", "PDF subtype: ", uMap); + typeExp = std::make_unique<GooString>("ISO 14289 - Electronic document file format enhancement for accessibility (PDF/UA)"); + standard = std::make_unique<GooString>("ISO 14289"); + abbr = std::make_unique<GooString>("PDF/UA"); break; - case subtypePart8: - abbr->append("-5"); + case subtypePDFVT: + printInfoString(info.getDict(), "GTS_PDFVTVersion", "PDF subtype: ", uMap); + typeExp = std::make_unique<GooString>("ISO 16612 - Electronic document file format for variable data exchange (PDF/VT)"); + standard = std::make_unique<GooString>("ISO 16612"); + abbr = std::make_unique<GooString>("PDF/VT"); break; - default: + case subtypePDFX: + printInfoString(info.getDict(), "GTS_PDFXVersion", "PDF subtype: ", uMap); + typeExp = std::make_unique<GooString>("ISO 15930 - Electronic document file format for prepress digital data exchange (PDF/X)"); + standard = std::make_unique<GooString>("ISO 15930"); + abbr = std::make_unique<GooString>("PDF/X"); break; + case subtypeNone: + case subtypeNull: + default: + return; } - break; - case subtypeNone: - case subtypeNull: - break; - default: - abbr->appendf("-{0:d}", subpart); - break; - } - // Form standard from PDFSubtypePart - switch (subpart) { - case subtypePartNone: - case subtypePartNull: - break; - default: - standard->appendf("-{0:d}", subpart); - break; - } - - // Form the subtitle from PDFSubtypePart and PDFSubtype - switch (pdftype) { - case subtypePDFA: - switch (subpart) { - case subtypePart1: - part = std::make_unique<GooString>("Use of PDF 1.4"); - break; - case subtypePart2: - part = std::make_unique<GooString>("Use of ISO 32000-1"); + // Form the abbreviation from PDFSubtypePart and PDFSubtype + const PDFSubtypePart subpart = doc->getPDFSubtypePart(); + switch (pdftype) { + case subtypePDFX: + switch (subpart) { + case subtypePart1: + abbr->append("-1:2001"); + break; + case subtypePart2: + abbr->append("-2"); + break; + case subtypePart3: + abbr->append("-3:2002"); + break; + case subtypePart4: + abbr->append("-1:2003"); + break; + case subtypePart5: + abbr->append("-2"); + break; + case subtypePart6: + abbr->append("-3:2003"); + break; + case subtypePart7: + abbr->append("-4"); + break; + case subtypePart8: + abbr->append("-5"); + break; + default: + break; + } break; - case subtypePart3: - part = std::make_unique<GooString>("Use of ISO 32000-1 with support for embedded files"); + case subtypeNone: + case subtypeNull: break; - default: + default: + abbr->appendf("-{0:d}", subpart); break; - } - break; - case subtypePDFE: - switch (subpart) { - case subtypePart1: - part = std::make_unique<GooString>("Use of PDF 1.6"); - break; - default: - break; - } - break; - case subtypePDFUA: + } + + // Form standard from PDFSubtypePart switch (subpart) { - case subtypePart1: - part = std::make_unique<GooString>("Use of ISO 32000-1"); + case subtypePartNone: + case subtypePartNull: break; - case subtypePart2: - part = std::make_unique<GooString>("Use of ISO 32000-2"); + default: + standard->appendf("-{0:d}", subpart); break; - case subtypePart3: - part = std::make_unique<GooString>("Use of ISO 32000-1 with support for embedded files"); + } + + // Form the subtitle from PDFSubtypePart and PDFSubtype + switch (pdftype) { + case subtypePDFA: + switch (subpart) { + case subtypePart1: + part = std::make_unique<GooString>("Use of PDF 1.4"); + break; + case subtypePart2: + part = std::make_unique<GooString>("Use of ISO 32000-1"); + break; + case subtypePart3: + part = std::make_unique<GooString>("Use of ISO 32000-1 with support for embedded files"); + break; + default: + break; + } break; - default: + case subtypePDFE: + switch (subpart) { + case subtypePart1: + part = std::make_unique<GooString>("Use of PDF 1.6"); + break; + default: + break; + } break; - } - break; - case subtypePDFVT: - switch (subpart) { - case subtypePart1: - part = std::make_unique<GooString>("Using PPML 2.1 and PDF 1.4"); + case subtypePDFUA: + switch (subpart) { + case subtypePart1: + part = std::make_unique<GooString>("Use of ISO 32000-1"); + break; + case subtypePart2: + part = std::make_unique<GooString>("Use of ISO 32000-2"); + break; + case subtypePart3: + part = std::make_unique<GooString>("Use of ISO 32000-1 with support for embedded files"); + break; + default: + break; + } break; - case subtypePart2: - part = std::make_unique<GooString>("Using PDF/X-4 and PDF/X-5 (PDF/VT-1 and PDF/VT-2)"); + case subtypePDFVT: + switch (subpart) { + case subtypePart1: + part = std::make_unique<GooString>("Using PPML 2.1 and PDF 1.4"); + break; + case subtypePart2: + part = std::make_unique<GooString>("Using PDF/X-4 and PDF/X-5 (PDF/VT-1 and PDF/VT-2)"); + break; + case subtypePart3: + part = std::make_unique<GooString>("Using PDF/X-6 (PDF/VT-3)"); + break; + default: + break; + } break; - case subtypePart3: - part = std::make_unique<GooString>("Using PDF/X-6 (PDF/VT-3)"); + case subtypePDFX: + switch (subpart) { + case subtypePart1: + part = std::make_unique<GooString>("Complete exchange using CMYK data (PDF/X-1 and PDF/X-1a)"); + break; + case subtypePart3: + part = std::make_unique<GooString>("Complete exchange suitable for colour-managed workflows (PDF/X-3)"); + break; + case subtypePart4: + part = std::make_unique<GooString>("Complete exchange of CMYK and spot colour printing data using PDF 1.4 (PDF/X-1a)"); + break; + case subtypePart5: + part = std::make_unique<GooString>("Partial exchange of printing data using PDF 1.4 (PDF/X-2) [Withdrawn]"); + break; + case subtypePart6: + part = std::make_unique<GooString>("Complete exchange of printing data suitable for colour-managed workflows using PDF 1.4 (PDF/X-3)"); + break; + case subtypePart7: + part = std::make_unique<GooString>("Complete exchange of printing data (PDF/X-4) and partial exchange of printing data with external profile reference (PDF/X-4p) using PDF 1.6"); + break; + case subtypePart8: + part = std::make_unique<GooString>("Partial exchange of printing data using PDF 1.6 (PDF/X-5)"); + break; + default: + break; + } break; - default: + default: break; - } - break; - case subtypePDFX: - switch (subpart) { - case subtypePart1: - part = std::make_unique<GooString>("Complete exchange using CMYK data (PDF/X-1 and PDF/X-1a)"); + } + + // Form Conformance explanation from PDFSubtypeConformance + switch (doc->getPDFSubtypeConformance()) { + case subtypeConfA: + confExp = std::make_unique<GooString>("Level A, Accessible"); break; - case subtypePart3: - part = std::make_unique<GooString>("Complete exchange suitable for colour-managed workflows (PDF/X-3)"); + case subtypeConfB: + confExp = std::make_unique<GooString>("Level B, Basic"); break; - case subtypePart4: - part = std::make_unique<GooString>("Complete exchange of CMYK and spot colour printing data using PDF 1.4 (PDF/X-1a)"); + case subtypeConfG: + confExp = std::make_unique<GooString>("Level G, External graphical content"); break; - case subtypePart5: - part = std::make_unique<GooString>("Partial exchange of printing data using PDF 1.4 (PDF/X-2) [Withdrawn]"); + case subtypeConfN: + confExp = std::make_unique<GooString>("Level N, External ICC profile"); break; - case subtypePart6: - part = std::make_unique<GooString>("Complete exchange of printing data suitable for colour-managed workflows using PDF 1.4 (PDF/X-3)"); + case subtypeConfP: + confExp = std::make_unique<GooString>("Level P, Embedded ICC profile"); break; - case subtypePart7: - part = std::make_unique<GooString>("Complete exchange of printing data (PDF/X-4) and partial exchange of printing data with external profile reference (PDF/X-4p) using PDF 1.6"); + case subtypeConfPG: + confExp = std::make_unique<GooString>("Level PG, Embedded ICC profile and external graphical content"); break; - case subtypePart8: - part = std::make_unique<GooString>("Partial exchange of printing data using PDF 1.6 (PDF/X-5)"); + case subtypeConfU: + confExp = std::make_unique<GooString>("Level U, Unicode support"); break; - default: + case subtypeConfNone: + case subtypeConfNull: + default: + confExp.reset(); break; - } - break; - default: - break; + } + + printf(" Title: %s\n", typeExp->c_str()); + printf(" Abbreviation: %s\n", abbr->c_str()); + if (part.get()) + printf(" Subtitle: Part %d: %s\n", subpart, part->c_str()); + else + printf(" Subtitle: Part %d\n", subpart); + printf(" Standard: %s-%d\n", typeExp->toStr().substr(0, 9).c_str(), subpart); + if (confExp.get()) + printf(" Conformance: %s\n", confExp->c_str()); } +} - // Form Conformance explanation from PDFSubtypeConformance - switch (doc->getPDFSubtypeConformance()) - { - case subtypeConfA: - confExp = std::make_unique<GooString>("Level A, Accessible"); - break; - case subtypeConfB: - confExp = std::make_unique<GooString>("Level B, Basic"); - break; - case subtypeConfG: - confExp = std::make_unique<GooString>("Level G, External graphical content"); - break; - case subtypeConfN: - confExp = std::make_unique<GooString>("Level N, External ICC profile"); - break; - case subtypeConfP: - confExp = std::make_unique<GooString>("Level P, Embedded ICC profile"); - break; - case subtypeConfPG: - confExp = std::make_unique<GooString>("Level PG, Embedded ICC profile and external graphical content"); - break; - case subtypeConfU: - confExp = std::make_unique<GooString>("Level U, Unicode support"); - break; - case subtypeConfNone: - case subtypeConfNull: - default: - confExp.reset(); - break; +static void printInfo(PDFDoc *doc, const UnicodeMap *uMap, long long filesize, bool multiPage) +{ + Page *page; + char buf[256]; + double w, h, wISO, hISO, isoThreshold; + int pg, i; + int r; + + // print doc info + Object info = doc->getDocInfo(); + if (info.isDict()) { + printInfoString(info.getDict(), "Title", "Title: ", uMap); + printInfoString(info.getDict(), "Subject", "Subject: ", uMap); + printInfoString(info.getDict(), "Keywords", "Keywords: ", uMap); + printInfoString(info.getDict(), "Author", "Author: ", uMap); + printInfoString(info.getDict(), "Creator", "Creator: ", uMap); + printInfoString(info.getDict(), "Producer", "Producer: ", uMap); + if (isoDates) { + printISODate(info.getDict(), "CreationDate", "CreationDate: "); + printISODate(info.getDict(), "ModDate", "ModDate: "); + } else if (rawDates) { + printInfoString(info.getDict(), "CreationDate", "CreationDate: ", uMap); + printInfoString(info.getDict(), "ModDate", "ModDate: ", uMap); + } else { + printInfoDate(info.getDict(), "CreationDate", "CreationDate: "); + printInfoDate(info.getDict(), "ModDate", "ModDate: "); + } } - printf(" Title: %s\n",typeExp->c_str()); - printf(" Abbreviation: %s\n", abbr->c_str()); - if (part.get()) - printf(" Subtitle: Part %d: %s\n", subpart, part->c_str()); - else - printf(" Subtitle: Part %d\n", subpart); - printf(" Standard: %s-%d\n", typeExp->toStr().substr(0,9).c_str(), subpart); - if (confExp.get()) - printf(" Conformance: %s\n", confExp->c_str()); - } -} + // print tagging info + printf("Tagged: %s\n", (doc->getCatalog()->getMarkInfo() & Catalog::markInfoMarked) ? "yes" : "no"); + printf("UserProperties: %s\n", (doc->getCatalog()->getMarkInfo() & Catalog::markInfoUserProperties) ? "yes" : "no"); + printf("Suspects: %s\n", (doc->getCatalog()->getMarkInfo() & Catalog::markInfoSuspects) ? "yes" : "no"); -static void printInfo(PDFDoc *doc, const UnicodeMap *uMap, long long filesize, bool multiPage) { - Page *page; - char buf[256]; - double w, h, wISO, hISO, isoThreshold; - int pg, i; - int r; - - // print doc info - Object info = doc->getDocInfo(); - if (info.isDict()) { - printInfoString(info.getDict(), "Title", "Title: ", uMap); - printInfoString(info.getDict(), "Subject", "Subject: ", uMap); - printInfoString(info.getDict(), "Keywords", "Keywords: ", uMap); - printInfoString(info.getDict(), "Author", "Author: ", uMap); - printInfoString(info.getDict(), "Creator", "Creator: ", uMap); - printInfoString(info.getDict(), "Producer", "Producer: ", uMap); - if (isoDates) { - printISODate(info.getDict(), "CreationDate", "CreationDate: "); - printISODate(info.getDict(), "ModDate", "ModDate: "); - } else if (rawDates) { - printInfoString(info.getDict(), "CreationDate", "CreationDate: ", - uMap); - printInfoString(info.getDict(), "ModDate", "ModDate: ", - uMap); - } else { - printInfoDate(info.getDict(), "CreationDate", "CreationDate: "); - printInfoDate(info.getDict(), "ModDate", "ModDate: "); - } - } - - // print tagging info - printf("Tagged: %s\n", - (doc->getCatalog()->getMarkInfo() & Catalog::markInfoMarked) ? "yes" : "no"); - printf("UserProperties: %s\n", - (doc->getCatalog()->getMarkInfo() & Catalog::markInfoUserProperties) ? "yes" : "no"); - printf("Suspects: %s\n", - (doc->getCatalog()->getMarkInfo() & Catalog::markInfoSuspects) ? "yes" : "no"); - - // print form info - switch (doc->getCatalog()->getFormType()) - { + // print form info + switch (doc->getCatalog()->getFormType()) { case Catalog::NoForm: - printf("Form: none\n"); - break; + printf("Form: none\n"); + break; case Catalog::AcroForm: - printf("Form: AcroForm\n"); - break; + printf("Form: AcroForm\n"); + break; case Catalog::XfaForm: - printf("Form: XFA\n"); - break; - } - - // print javascript info - { - JSInfo jsInfo(doc, firstPage - 1); - jsInfo.scanJS(lastPage - firstPage + 1); - printf("JavaScript: %s\n", jsInfo.containsJS() ? "yes" : "no"); - } - - // print page count - printf("Pages: %d\n", doc->getNumPages()); - - // print encryption info - printf("Encrypted: "); - if (doc->isEncrypted()) { - unsigned char *fileKey; - CryptAlgorithm encAlgorithm; - int keyLength; - doc->getXRef()->getEncryptionParameters(&fileKey, &encAlgorithm, &keyLength); - - const char *encAlgorithmName = "unknown"; - switch (encAlgorithm) + printf("Form: XFA\n"); + break; + } + + // print javascript info { - case cryptRC4: - encAlgorithmName = "RC4"; - break; - case cryptAES: - encAlgorithmName = "AES"; - break; - case cryptAES256: - encAlgorithmName = "AES-256"; - break; - case cryptNone: - break; - } - - printf("yes (print:%s copy:%s change:%s addNotes:%s algorithm:%s)\n", - doc->okToPrint(true) ? "yes" : "no", - doc->okToCopy(true) ? "yes" : "no", - doc->okToChange(true) ? "yes" : "no", - doc->okToAddNotes(true) ? "yes" : "no", - encAlgorithmName); - } else { - printf("no\n"); - } - - // print page size - for (pg = firstPage; pg <= lastPage; ++pg) { - w = doc->getPageCropWidth(pg); - h = doc->getPageCropHeight(pg); - if (multiPage) { - printf("Page %4d size: %g x %g pts", pg, w, h); - } else { - printf("Page size: %g x %g pts", w, h); + JSInfo jsInfo(doc, firstPage - 1); + jsInfo.scanJS(lastPage - firstPage + 1); + printf("JavaScript: %s\n", jsInfo.containsJS() ? "yes" : "no"); } - if ((fabs(w - 612) < 1 && fabs(h - 792) < 1) || - (fabs(w - 792) < 1 && fabs(h - 612) < 1)) { - printf(" (letter)"); + + // print page count + printf("Pages: %d\n", doc->getNumPages()); + + // print encryption info + printf("Encrypted: "); + if (doc->isEncrypted()) { + unsigned char *fileKey; + CryptAlgorithm encAlgorithm; + int keyLength; + doc->getXRef()->getEncryptionParameters(&fileKey, &encAlgorithm, &keyLength); + + const char *encAlgorithmName = "unknown"; + switch (encAlgorithm) { + case cryptRC4: + encAlgorithmName = "RC4"; + break; + case cryptAES: + encAlgorithmName = "AES"; + break; + case cryptAES256: + encAlgorithmName = "AES-256"; + break; + case cryptNone: + break; + } + + printf("yes (print:%s copy:%s change:%s addNotes:%s algorithm:%s)\n", doc->okToPrint(true) ? "yes" : "no", doc->okToCopy(true) ? "yes" : "no", doc->okToChange(true) ? "yes" : "no", doc->okToAddNotes(true) ? "yes" : "no", + encAlgorithmName); } else { - hISO = sqrt(sqrt(2.0)) * 7200 / 2.54; - wISO = hISO / sqrt(2.0); - isoThreshold = hISO * 0.003; ///< allow for 0.3% error when guessing conformance to ISO 216, A series - for (i = 0; i <= 6; ++i) { - if ((fabs(w - wISO) < isoThreshold && fabs(h - hISO) < isoThreshold) || - (fabs(w - hISO) < isoThreshold && fabs(h - wISO) < isoThreshold)) { - printf(" (A%d)", i); - break; - } - hISO = wISO; - wISO /= sqrt(2.0); - isoThreshold /= sqrt(2.0); - } - } - printf("\n"); - r = doc->getPageRotate(pg); - if (multiPage) { - printf("Page %4d rot: %d\n", pg, r); + printf("no\n"); + } + + // print page size + for (pg = firstPage; pg <= lastPage; ++pg) { + w = doc->getPageCropWidth(pg); + h = doc->getPageCropHeight(pg); + if (multiPage) { + printf("Page %4d size: %g x %g pts", pg, w, h); + } else { + printf("Page size: %g x %g pts", w, h); + } + if ((fabs(w - 612) < 1 && fabs(h - 792) < 1) || (fabs(w - 792) < 1 && fabs(h - 612) < 1)) { + printf(" (letter)"); + } else { + hISO = sqrt(sqrt(2.0)) * 7200 / 2.54; + wISO = hISO / sqrt(2.0); + isoThreshold = hISO * 0.003; ///< allow for 0.3% error when guessing conformance to ISO 216, A series + for (i = 0; i <= 6; ++i) { + if ((fabs(w - wISO) < isoThreshold && fabs(h - hISO) < isoThreshold) || (fabs(w - hISO) < isoThreshold && fabs(h - wISO) < isoThreshold)) { + printf(" (A%d)", i); + break; + } + hISO = wISO; + wISO /= sqrt(2.0); + isoThreshold /= sqrt(2.0); + } + } + printf("\n"); + r = doc->getPageRotate(pg); + if (multiPage) { + printf("Page %4d rot: %d\n", pg, r); + } else { + printf("Page rot: %d\n", r); + } + } + + // print the boxes + if (printBoxes) { + if (multiPage) { + for (pg = firstPage; pg <= lastPage; ++pg) { + page = doc->getPage(pg); + if (!page) { + error(errSyntaxError, -1, "Failed to print boxes for page {0:d}", pg); + continue; + } + sprintf(buf, "Page %4d MediaBox: ", pg); + printBox(buf, page->getMediaBox()); + sprintf(buf, "Page %4d CropBox: ", pg); + printBox(buf, page->getCropBox()); + sprintf(buf, "Page %4d BleedBox: ", pg); + printBox(buf, page->getBleedBox()); + sprintf(buf, "Page %4d TrimBox: ", pg); + printBox(buf, page->getTrimBox()); + sprintf(buf, "Page %4d ArtBox: ", pg); + printBox(buf, page->getArtBox()); + } + } else { + page = doc->getPage(firstPage); + if (!page) { + error(errSyntaxError, -1, "Failed to print boxes for page {0:d}", firstPage); + } else { + printBox("MediaBox: ", page->getMediaBox()); + printBox("CropBox: ", page->getCropBox()); + printBox("BleedBox: ", page->getBleedBox()); + printBox("TrimBox: ", page->getTrimBox()); + printBox("ArtBox: ", page->getArtBox()); + } + } + } + + // print file size + printf("File size: %lld bytes\n", filesize); + + // print linearization info + printf("Optimized: %s\n", doc->isLinearized() ? "yes" : "no"); + + // print PDF version + printf("PDF version: %d.%d\n", doc->getPDFMajorVersion(), doc->getPDFMinorVersion()); + + printPdfSubtype(doc, uMap); +} + +int main(int argc, char *argv[]) +{ + PDFDoc *doc; + GooString *fileName; + GooString *ownerPW, *userPW; + const UnicodeMap *uMap; + FILE *f; + bool ok; + int exitCode; + bool multiPage; + + exitCode = 99; + + // parse args + Win32Console win32console(&argc, &argv); + ok = parseArgs(argDesc, &argc, argv); + if (!ok || (argc != 2 && !printEnc) || printVersion || printHelp) { + fprintf(stderr, "pdfinfo version %s\n", PACKAGE_VERSION); + fprintf(stderr, "%s\n", popplerCopyright); + fprintf(stderr, "%s\n", xpdfCopyright); + if (!printVersion) { + printUsage("pdfinfo", "<PDF-file>", argDesc); + } + if (printVersion || printHelp) + exitCode = 0; + goto err0; + } + + if (printStructureText) + printStructure = true; + + // read config file + globalParams = std::make_unique<GlobalParams>(); + + if (printEnc) { + printEncodings(); + exitCode = 0; + goto err0; + } + + fileName = new GooString(argv[1]); + + if (textEncName[0]) { + globalParams->setTextEncoding(textEncName); + } + + // get mapping to output encoding + if (!(uMap = globalParams->getTextEncoding())) { + error(errCommandLine, -1, "Couldn't get text encoding"); + delete fileName; + goto err1; + } + + // open PDF file + if (ownerPassword[0] != '\001') { + ownerPW = new GooString(ownerPassword); } else { - printf("Page rot: %d\n", r); - } - } - - // print the boxes - if (printBoxes) { - if (multiPage) { - for (pg = firstPage; pg <= lastPage; ++pg) { - page = doc->getPage(pg); - if (!page) { - error(errSyntaxError, -1, "Failed to print boxes for page {0:d}", pg); - continue; - } - sprintf(buf, "Page %4d MediaBox: ", pg); - printBox(buf, page->getMediaBox()); - sprintf(buf, "Page %4d CropBox: ", pg); - printBox(buf, page->getCropBox()); - sprintf(buf, "Page %4d BleedBox: ", pg); - printBox(buf, page->getBleedBox()); - sprintf(buf, "Page %4d TrimBox: ", pg); - printBox(buf, page->getTrimBox()); - sprintf(buf, "Page %4d ArtBox: ", pg); - printBox(buf, page->getArtBox()); - } + ownerPW = nullptr; + } + if (userPassword[0] != '\001') { + userPW = new GooString(userPassword); } else { - page = doc->getPage(firstPage); - if (!page) { - error(errSyntaxError, -1, "Failed to print boxes for page {0:d}", firstPage); - } else { - printBox("MediaBox: ", page->getMediaBox()); - printBox("CropBox: ", page->getCropBox()); - printBox("BleedBox: ", page->getBleedBox()); - printBox("TrimBox: ", page->getTrimBox()); - printBox("ArtBox: ", page->getArtBox()); - } + userPW = nullptr; } - } - // print file size - printf("File size: %lld bytes\n", filesize); + if (fileName->cmp("-") == 0) { + delete fileName; + fileName = new GooString("fd://0"); + } - // print linearization info - printf("Optimized: %s\n", doc->isLinearized() ? "yes" : "no"); + doc = PDFDocFactory().createPDFDoc(*fileName, ownerPW, userPW); - // print PDF version - printf("PDF version: %d.%d\n", doc->getPDFMajorVersion(), doc->getPDFMinorVersion()); + if (userPW) { + delete userPW; + } + if (ownerPW) { + delete ownerPW; + } + if (!doc->isOk()) { + exitCode = 1; + goto err2; + } - printPdfSubtype(doc, uMap); -} + // get page range + if (firstPage < 1) { + firstPage = 1; + } + if (lastPage == 0) { + multiPage = false; + } else { + multiPage = true; + } + if (lastPage < 1 || lastPage > doc->getNumPages()) { + lastPage = doc->getNumPages(); + } + if (lastPage < firstPage) { + error(errCommandLine, -1, "Wrong page range given: the first page ({0:d}) can not be after the last page ({1:d}).", firstPage, lastPage); + goto err2; + } -int main(int argc, char *argv[]) { - PDFDoc *doc; - GooString *fileName; - GooString *ownerPW, *userPW; - const UnicodeMap *uMap; - FILE *f; - bool ok; - int exitCode; - bool multiPage; - - exitCode = 99; - - // parse args - Win32Console win32console(&argc, &argv); - ok = parseArgs(argDesc, &argc, argv); - if (!ok || (argc != 2 && !printEnc) || printVersion || printHelp) { - fprintf(stderr, "pdfinfo version %s\n", PACKAGE_VERSION); - fprintf(stderr, "%s\n", popplerCopyright); - fprintf(stderr, "%s\n", xpdfCopyright); - if (!printVersion) { - printUsage("pdfinfo", "<PDF-file>", argDesc); - } - if (printVersion || printHelp) - exitCode = 0; - goto err0; - } - - if (printStructureText) - printStructure = true; - - // read config file - globalParams = std::make_unique<GlobalParams>(); - - if (printEnc) { - printEncodings(); - exitCode = 0; - goto err0; - } + if (printMetadata) { + // print the metadata + const GooString *metadata = doc->readMetadata(); + if (metadata) { + fputs(metadata->c_str(), stdout); + fputc('\n', stdout); + delete metadata; + } + } else if (printJS) { + // print javascript + JSInfo jsInfo(doc, firstPage - 1); + jsInfo.scanJS(lastPage - firstPage + 1, stdout, uMap); + } else if (printStructure || printStructureText) { + // print structure + const StructTreeRoot *structTree = doc->getCatalog()->getStructTreeRoot(); + if (structTree) { + for (unsigned i = 0; i < structTree->getNumChildren(); i++) { + printStruct(structTree->getChild(i), 0); + } + } + } else if (printDests) { + printDestinations(doc, uMap); + } else { + // print info + long long filesize = 0; + + f = fopen(fileName->c_str(), "rb"); + if (f) { + Gfseek(f, 0, SEEK_END); + filesize = Gftell(f); + fclose(f); + } - fileName = new GooString(argv[1]); + if (multiPage == false) + lastPage = 1; - if (textEncName[0]) { - globalParams->setTextEncoding(textEncName); - } + printInfo(doc, uMap, filesize, multiPage); + } + exitCode = 0; - // get mapping to output encoding - if (!(uMap = globalParams->getTextEncoding())) { - error(errCommandLine, -1, "Couldn't get text encoding"); + // clean up +err2: + delete doc; delete fileName; - goto err1; - } - - // open PDF file - if (ownerPassword[0] != '\001') { - ownerPW = new GooString(ownerPassword); - } else { - ownerPW = nullptr; - } - if (userPassword[0] != '\001') { - userPW = new GooString(userPassword); - } else { - userPW = nullptr; - } - - if (fileName->cmp("-") == 0) { - delete fileName; - fileName = new GooString("fd://0"); - } - - doc = PDFDocFactory().createPDFDoc(*fileName, ownerPW, userPW); - - if (userPW) { - delete userPW; - } - if (ownerPW) { - delete ownerPW; - } - if (!doc->isOk()) { - exitCode = 1; - goto err2; - } - - // get page range - if (firstPage < 1) { - firstPage = 1; - } - if (lastPage == 0) { - multiPage = false; - } else { - multiPage = true; - } - if (lastPage < 1 || lastPage > doc->getNumPages()) { - lastPage = doc->getNumPages(); - } - if (lastPage < firstPage) { - error(errCommandLine, -1, - "Wrong page range given: the first page ({0:d}) can not be after the last page ({1:d}).", - firstPage, lastPage); - goto err2; - } - - if (printMetadata) { - // print the metadata - const GooString *metadata = doc->readMetadata(); - if (metadata) { - fputs(metadata->c_str(), stdout); - fputc('\n', stdout); - delete metadata; - } - } else if (printJS) { - // print javascript - JSInfo jsInfo(doc, firstPage - 1); - jsInfo.scanJS(lastPage - firstPage + 1, stdout, uMap); - } else if (printStructure || printStructureText) { - // print structure - const StructTreeRoot *structTree = doc->getCatalog()->getStructTreeRoot(); - if (structTree) { - for (unsigned i = 0; i < structTree->getNumChildren(); i++) { - printStruct(structTree->getChild(i), 0); - } - } - } else if (printDests) { - printDestinations(doc, uMap); - } else { - // print info - long long filesize = 0; - - f = fopen(fileName->c_str(), "rb"); - if (f) { - Gfseek(f, 0, SEEK_END); - filesize = Gftell(f); - fclose(f); - } - - if (multiPage == false) - lastPage = 1; - - printInfo(doc, uMap, filesize, multiPage); - } - exitCode = 0; - - // clean up - err2: - delete doc; - delete fileName; - err1: - err0: - - return exitCode; +err1: +err0: + + return exitCode; } diff --git a/utils/pdfseparate.cc b/utils/pdfseparate.cc index 240022a6..7fc5d029 100644 --- a/utils/pdfseparate.cc +++ b/utils/pdfseparate.cc @@ -34,148 +34,134 @@ static int lastPage = 0; static bool printVersion = false; static bool printHelp = false; -static const ArgDesc argDesc[] = { - {"-f", argInt, &firstPage, 0, - "first page to extract"}, - {"-l", argInt, &lastPage, 0, - "last page to extract"}, - {"-v", argFlag, &printVersion, 0, - "print copyright and version info"}, - {"-h", argFlag, &printHelp, 0, - "print usage information"}, - {"-help", argFlag, &printHelp, 0, - "print usage information"}, - {"--help", argFlag, &printHelp, 0, - "print usage information"}, - {"-?", argFlag, &printHelp, 0, - "print usage information"}, - {} -}; +static const ArgDesc argDesc[] = { { "-f", argInt, &firstPage, 0, "first page to extract" }, + { "-l", argInt, &lastPage, 0, "last page to extract" }, + { "-v", argFlag, &printVersion, 0, "print copyright and version info" }, + { "-h", argFlag, &printHelp, 0, "print usage information" }, + { "-help", argFlag, &printHelp, 0, "print usage information" }, + { "--help", argFlag, &printHelp, 0, "print usage information" }, + { "-?", argFlag, &printHelp, 0, "print usage information" }, + {} }; -static bool extractPages (const char *srcFileName, const char *destFileName) { - char pathName[4096]; - GooString *gfileName = new GooString (srcFileName); - PDFDoc *doc = new PDFDoc (gfileName, nullptr, nullptr, nullptr); +static bool extractPages(const char *srcFileName, const char *destFileName) +{ + char pathName[4096]; + GooString *gfileName = new GooString(srcFileName); + PDFDoc *doc = new PDFDoc(gfileName, nullptr, nullptr, nullptr); - if (!doc->isOk()) { - error(errSyntaxError, -1, "Could not extract page(s) from damaged file ('{0:s}')", srcFileName); - delete doc; - return false; - } + if (!doc->isOk()) { + error(errSyntaxError, -1, "Could not extract page(s) from damaged file ('{0:s}')", srcFileName); + delete doc; + return false; + } - // destFileName can have multiple %% and one %d - // We use auxDestFileName to replace all the valid % appearances - // by 'A' (random char that is not %), if at the end of replacing - // any of the valid appearances there is still any % around, the - // pattern is wrong - if (firstPage == 0 && lastPage == 0) { - firstPage = 1; - lastPage = doc->getNumPages(); - } - if (lastPage == 0) - lastPage = doc->getNumPages(); - if (firstPage == 0) - firstPage = 1; - if (lastPage < firstPage) { - error(errCommandLine, -1, - "Wrong page range given: the first page ({0:d}) can not be after the last page ({1:d}).", - firstPage, lastPage); - delete doc; - return false; - } - bool foundmatch = false; - char *auxDestFileName = strdup(destFileName); - char *p = strstr(auxDestFileName, "%d"); - if (p != nullptr) { - foundmatch = true; - *p = 'A'; - } else { - char pattern[6]; - for (int i = 2; i < 10; i++) { - sprintf(pattern, "%%0%dd", i); - p = strstr(auxDestFileName, pattern); - if (p != nullptr) { - foundmatch = true; - *p = 'A'; - break; - } + // destFileName can have multiple %% and one %d + // We use auxDestFileName to replace all the valid % appearances + // by 'A' (random char that is not %), if at the end of replacing + // any of the valid appearances there is still any % around, the + // pattern is wrong + if (firstPage == 0 && lastPage == 0) { + firstPage = 1; + lastPage = doc->getNumPages(); + } + if (lastPage == 0) + lastPage = doc->getNumPages(); + if (firstPage == 0) + firstPage = 1; + if (lastPage < firstPage) { + error(errCommandLine, -1, "Wrong page range given: the first page ({0:d}) can not be after the last page ({1:d}).", firstPage, lastPage); + delete doc; + return false; + } + bool foundmatch = false; + char *auxDestFileName = strdup(destFileName); + char *p = strstr(auxDestFileName, "%d"); + if (p != nullptr) { + foundmatch = true; + *p = 'A'; + } else { + char pattern[6]; + for (int i = 2; i < 10; i++) { + sprintf(pattern, "%%0%dd", i); + p = strstr(auxDestFileName, pattern); + if (p != nullptr) { + foundmatch = true; + *p = 'A'; + break; + } + } + } + if (!foundmatch && firstPage != lastPage) { + error(errSyntaxError, -1, "'{0:s}' must contain '%d' (or any variant respecting printf format) if more than one page should be extracted, in order to print the page number", destFileName); + free(auxDestFileName); + delete doc; + return false; } - } - if (!foundmatch && firstPage != lastPage) { - error(errSyntaxError, -1, "'{0:s}' must contain '%d' (or any variant respecting printf format) if more than one page should be extracted, in order to print the page number", destFileName); - free(auxDestFileName); - delete doc; - return false; - } - // at this point auxDestFileName can only contain %% - p = strstr(auxDestFileName, "%%"); - while (p != nullptr) { - *p = 'A'; - *(p + 1) = 'A'; - p = strstr(p, "%%"); - } + // at this point auxDestFileName can only contain %% + p = strstr(auxDestFileName, "%%"); + while (p != nullptr) { + *p = 'A'; + *(p + 1) = 'A'; + p = strstr(p, "%%"); + } - // at this point any other % is wrong - p = strstr(auxDestFileName, "%"); - if (p != nullptr) { - error(errSyntaxError, -1, "'{0:s}' can only contain one '%d' pattern", destFileName); + // at this point any other % is wrong + p = strstr(auxDestFileName, "%"); + if (p != nullptr) { + error(errSyntaxError, -1, "'{0:s}' can only contain one '%d' pattern", destFileName); + free(auxDestFileName); + delete doc; + return false; + } free(auxDestFileName); - delete doc; - return false; - } - free(auxDestFileName); - - for (int pageNo = firstPage; pageNo <= lastPage; pageNo++) { - snprintf (pathName, sizeof (pathName) - 1, destFileName, pageNo); - GooString *gpageName = new GooString (pathName); - PDFDoc *pagedoc = new PDFDoc (new GooString (srcFileName), nullptr, nullptr, nullptr); - int errCode = pagedoc->savePageAs(gpageName, pageNo); - if ( errCode != errNone) { - delete gpageName; - delete doc; - delete pagedoc; - return false; + + for (int pageNo = firstPage; pageNo <= lastPage; pageNo++) { + snprintf(pathName, sizeof(pathName) - 1, destFileName, pageNo); + GooString *gpageName = new GooString(pathName); + PDFDoc *pagedoc = new PDFDoc(new GooString(srcFileName), nullptr, nullptr, nullptr); + int errCode = pagedoc->savePageAs(gpageName, pageNo); + if (errCode != errNone) { + delete gpageName; + delete doc; + delete pagedoc; + return false; + } + delete pagedoc; + delete gpageName; } - delete pagedoc; - delete gpageName; - } - delete doc; - return true; + delete doc; + return true; } -int -main (int argc, char *argv[]) +int main(int argc, char *argv[]) { - bool ok; - int exitCode; + bool ok; + int exitCode; - exitCode = 99; + exitCode = 99; - // parse args - Win32Console win32console(&argc, &argv); - ok = parseArgs (argDesc, &argc, argv); - if (!ok || argc != 3 || printVersion || printHelp) - { - fprintf (stderr, "pdfseparate version %s\n", PACKAGE_VERSION); - fprintf (stderr, "%s\n", popplerCopyright); - fprintf (stderr, "%s\n", xpdfCopyright); - if (!printVersion) - { - printUsage ("pdfseparate", "<PDF-sourcefile> <PDF-pattern-destfile>", - argDesc); - } - if (printVersion || printHelp) - exitCode = 0; - goto err0; + // parse args + Win32Console win32console(&argc, &argv); + ok = parseArgs(argDesc, &argc, argv); + if (!ok || argc != 3 || printVersion || printHelp) { + fprintf(stderr, "pdfseparate version %s\n", PACKAGE_VERSION); + fprintf(stderr, "%s\n", popplerCopyright); + fprintf(stderr, "%s\n", xpdfCopyright); + if (!printVersion) { + printUsage("pdfseparate", "<PDF-sourcefile> <PDF-pattern-destfile>", argDesc); + } + if (printVersion || printHelp) + exitCode = 0; + goto err0; + } + globalParams = std::make_unique<GlobalParams>(); + ok = extractPages(argv[1], argv[2]); + if (ok) { + exitCode = 0; } - globalParams = std::make_unique<GlobalParams>(); - ok = extractPages (argv[1], argv[2]); - if (ok) { - exitCode = 0; - } err0: - return exitCode; + return exitCode; } diff --git a/utils/pdfsig.cc b/utils/pdfsig.cc index e24cc9ba..7b2bed99 100644 --- a/utils/pdfsig.cc +++ b/utils/pdfsig.cc @@ -41,60 +41,60 @@ #include "numberofcharacters.h" #include <libgen.h> -static const char * getReadableSigState(SignatureValidationStatus sig_vs) +static const char *getReadableSigState(SignatureValidationStatus sig_vs) { - switch(sig_vs) { + switch (sig_vs) { case SIGNATURE_VALID: - return "Signature is Valid."; + return "Signature is Valid."; case SIGNATURE_INVALID: - return "Signature is Invalid."; + return "Signature is Invalid."; case SIGNATURE_DIGEST_MISMATCH: - return "Digest Mismatch."; + return "Digest Mismatch."; case SIGNATURE_DECODING_ERROR: - return "Document isn't signed or corrupted data."; + return "Document isn't signed or corrupted data."; case SIGNATURE_NOT_VERIFIED: - return "Signature has not yet been verified."; + return "Signature has not yet been verified."; default: - return "Unknown Validation Failure."; - } + return "Unknown Validation Failure."; + } } -static const char * getReadableCertState(CertificateValidationStatus cert_vs) +static const char *getReadableCertState(CertificateValidationStatus cert_vs) { - switch(cert_vs) { + switch (cert_vs) { case CERTIFICATE_TRUSTED: - return "Certificate is Trusted."; + return "Certificate is Trusted."; case CERTIFICATE_UNTRUSTED_ISSUER: - return "Certificate issuer isn't Trusted."; + return "Certificate issuer isn't Trusted."; case CERTIFICATE_UNKNOWN_ISSUER: - return "Certificate issuer is unknown."; + return "Certificate issuer is unknown."; case CERTIFICATE_REVOKED: - return "Certificate has been Revoked."; + return "Certificate has been Revoked."; case CERTIFICATE_EXPIRED: - return "Certificate has Expired"; + return "Certificate has Expired"; case CERTIFICATE_NOT_VERIFIED: - return "Certificate has not yet been verified."; + return "Certificate has not yet been verified."; default: - return "Unknown issue with Certificate or corrupted data."; - } + return "Unknown issue with Certificate or corrupted data."; + } } static char *getReadableTime(time_t unix_time) { - char * time_str = (char *) gmalloc(64); - strftime(time_str, 64, "%b %d %Y %H:%M:%S", localtime(&unix_time)); - return time_str; + char *time_str = (char *)gmalloc(64); + strftime(time_str, 64, "%b %d %Y %H:%M:%S", localtime(&unix_time)); + return time_str; } static bool dumpSignature(int sig_num, int sigCount, FormFieldSignature *s, const char *filename) @@ -127,148 +127,134 @@ static bool printHelp = false; static bool dontVerifyCert = false; static bool dumpSignatures = false; -static const ArgDesc argDesc[] = { - {"-nssdir", argGooString, &nssDir, 0, - "path to directory of libnss3 database"}, - {"-nocert", argFlag, &dontVerifyCert, 0, - "don't perform certificate validation"}, - {"-dump", argFlag, &dumpSignatures, 0, - "dump all signatures into current directory"}, - - {"-v", argFlag, &printVersion, 0, - "print copyright and version info"}, - {"-h", argFlag, &printHelp, 0, - "print usage information"}, - {"-help", argFlag, &printHelp, 0, - "print usage information"}, - {"-?", argFlag, &printHelp, 0, - "print usage information"}, - {} -}; +static const ArgDesc argDesc[] = { { "-nssdir", argGooString, &nssDir, 0, "path to directory of libnss3 database" }, + { "-nocert", argFlag, &dontVerifyCert, 0, "don't perform certificate validation" }, + { "-dump", argFlag, &dumpSignatures, 0, "dump all signatures into current directory" }, + { "-v", argFlag, &printVersion, 0, "print copyright and version info" }, + { "-h", argFlag, &printHelp, 0, "print usage information" }, + { "-help", argFlag, &printHelp, 0, "print usage information" }, + { "-?", argFlag, &printHelp, 0, "print usage information" }, + {} }; int main(int argc, char *argv[]) { - char *time_str = nullptr; - globalParams = std::make_unique<GlobalParams>(); + char *time_str = nullptr; + globalParams = std::make_unique<GlobalParams>(); - Win32Console win32Console(&argc, &argv); + Win32Console win32Console(&argc, &argv); - const bool ok = parseArgs(argDesc, &argc, argv); + const bool ok = parseArgs(argDesc, &argc, argv); - if (!ok || argc != 2 || printVersion || printHelp) { - fprintf(stderr, "pdfsig version %s\n", PACKAGE_VERSION); - fprintf(stderr, "%s\n", popplerCopyright); - fprintf(stderr, "%s\n", xpdfCopyright); - if (!printVersion) { - printUsage("pdfsig", "<PDF-file>", argDesc); + if (!ok || argc != 2 || printVersion || printHelp) { + fprintf(stderr, "pdfsig version %s\n", PACKAGE_VERSION); + fprintf(stderr, "%s\n", popplerCopyright); + fprintf(stderr, "%s\n", xpdfCopyright); + if (!printVersion) { + printUsage("pdfsig", "<PDF-file>", argDesc); + } + if (printVersion || printHelp) + return 0; + return 99; } - if (printVersion || printHelp) - return 0; - return 99; - } - - std::unique_ptr<GooString> fileName = std::make_unique<GooString>(argv[argc - 1]); - SignatureHandler::setNSSDir(nssDir); + std::unique_ptr<GooString> fileName = std::make_unique<GooString>(argv[argc - 1]); - // open PDF file - std::unique_ptr<PDFDoc> doc(PDFDocFactory().createPDFDoc(*fileName, nullptr, nullptr)); + SignatureHandler::setNSSDir(nssDir); - if (!doc->isOk()) { - return 1; - } + // open PDF file + std::unique_ptr<PDFDoc> doc(PDFDocFactory().createPDFDoc(*fileName, nullptr, nullptr)); - const std::vector<FormFieldSignature*> signatures = doc->getSignatureFields(); - const unsigned int sigCount = signatures.size(); + if (!doc->isOk()) { + return 1; + } - if (sigCount >= 1) { - if (dumpSignatures) { - printf("Dumping Signatures: %u\n", sigCount); - for (unsigned int i = 0; i < sigCount; i++) { - const bool dumpingOk = dumpSignature(i, sigCount, signatures.at(i), fileName->c_str()); - if (!dumpingOk) { - return 3; + const std::vector<FormFieldSignature *> signatures = doc->getSignatureFields(); + const unsigned int sigCount = signatures.size(); + + if (sigCount >= 1) { + if (dumpSignatures) { + printf("Dumping Signatures: %u\n", sigCount); + for (unsigned int i = 0; i < sigCount; i++) { + const bool dumpingOk = dumpSignature(i, sigCount, signatures.at(i), fileName->c_str()); + if (!dumpingOk) { + return 3; + } + } + return 0; + } else { + printf("Digital Signature Info of: %s\n", fileName->c_str()); } - } - return 0; } else { - printf("Digital Signature Info of: %s\n", fileName->c_str()); - } - } else { - printf("File '%s' does not contain any signatures\n", fileName->c_str()); - return 2; - } - - for (unsigned int i = 0; i < sigCount; i++) { - const SignatureInfo *sig_info = signatures.at(i)->validateSignature(!dontVerifyCert, false, -1 /* now */); - printf("Signature #%u:\n", i+1); - printf(" - Signer Certificate Common Name: %s\n", sig_info->getSignerName()); - printf(" - Signer full Distinguished Name: %s\n", sig_info->getSubjectDN()); - printf(" - Signing Time: %s\n", time_str = getReadableTime(sig_info->getSigningTime())); - printf(" - Signing Hash Algorithm: "); - switch (sig_info->getHashAlgorithm()) - { - case HASH_AlgMD2: - printf("MD2\n"); - break; - case HASH_AlgMD5: - printf("MD5\n"); - break; - case HASH_AlgSHA1: - printf("SHA1\n"); - break; - case HASH_AlgSHA256: - printf("SHA-256\n"); - break; - case HASH_AlgSHA384: - printf("SHA-384\n"); - break; - case HASH_AlgSHA512: - printf("SHA-512\n"); - break; - case HASH_AlgSHA224: - printf("SHA-224\n"); - break; - default: - printf("unknown\n"); - } - printf(" - Signature Type: "); - switch (signatures.at(i)->getSignatureType()) - { - case adbe_pkcs7_sha1: - printf("adbe.pkcs7.sha1\n"); - break; - case adbe_pkcs7_detached: - printf("adbe.pkcs7.detached\n"); - break; - case ETSI_CAdES_detached: - printf("ETSI.CAdES.detached\n"); - break; - default: - printf("unknown\n"); + printf("File '%s' does not contain any signatures\n", fileName->c_str()); + return 2; } - std::vector<Goffset> ranges = signatures.at(i)->getSignedRangeBounds(); - if (ranges.size() == 4) - { - printf(" - Signed Ranges: [%lld - %lld], [%lld - %lld]\n", - ranges[0], ranges[1], ranges[2], ranges[3]); - Goffset checked_file_size; - GooString* signature = signatures.at(i)->getCheckedSignature(&checked_file_size); - if (signature && checked_file_size == ranges[3]) { - printf(" - Total document signed\n"); - } else { - printf(" - Not total document signed\n"); - } - delete signature; - } - printf(" - Signature Validation: %s\n", getReadableSigState(sig_info->getSignatureValStatus())); - gfree(time_str); - if (sig_info->getSignatureValStatus() != SIGNATURE_VALID || dontVerifyCert) { - continue; + + for (unsigned int i = 0; i < sigCount; i++) { + const SignatureInfo *sig_info = signatures.at(i)->validateSignature(!dontVerifyCert, false, -1 /* now */); + printf("Signature #%u:\n", i + 1); + printf(" - Signer Certificate Common Name: %s\n", sig_info->getSignerName()); + printf(" - Signer full Distinguished Name: %s\n", sig_info->getSubjectDN()); + printf(" - Signing Time: %s\n", time_str = getReadableTime(sig_info->getSigningTime())); + printf(" - Signing Hash Algorithm: "); + switch (sig_info->getHashAlgorithm()) { + case HASH_AlgMD2: + printf("MD2\n"); + break; + case HASH_AlgMD5: + printf("MD5\n"); + break; + case HASH_AlgSHA1: + printf("SHA1\n"); + break; + case HASH_AlgSHA256: + printf("SHA-256\n"); + break; + case HASH_AlgSHA384: + printf("SHA-384\n"); + break; + case HASH_AlgSHA512: + printf("SHA-512\n"); + break; + case HASH_AlgSHA224: + printf("SHA-224\n"); + break; + default: + printf("unknown\n"); + } + printf(" - Signature Type: "); + switch (signatures.at(i)->getSignatureType()) { + case adbe_pkcs7_sha1: + printf("adbe.pkcs7.sha1\n"); + break; + case adbe_pkcs7_detached: + printf("adbe.pkcs7.detached\n"); + break; + case ETSI_CAdES_detached: + printf("ETSI.CAdES.detached\n"); + break; + default: + printf("unknown\n"); + } + std::vector<Goffset> ranges = signatures.at(i)->getSignedRangeBounds(); + if (ranges.size() == 4) { + printf(" - Signed Ranges: [%lld - %lld], [%lld - %lld]\n", ranges[0], ranges[1], ranges[2], ranges[3]); + Goffset checked_file_size; + GooString *signature = signatures.at(i)->getCheckedSignature(&checked_file_size); + if (signature && checked_file_size == ranges[3]) { + printf(" - Total document signed\n"); + } else { + printf(" - Not total document signed\n"); + } + delete signature; + } + printf(" - Signature Validation: %s\n", getReadableSigState(sig_info->getSignatureValStatus())); + gfree(time_str); + if (sig_info->getSignatureValStatus() != SIGNATURE_VALID || dontVerifyCert) { + continue; + } + printf(" - Certificate Validation: %s\n", getReadableCertState(sig_info->getCertificateValStatus())); } - printf(" - Certificate Validation: %s\n", getReadableCertState(sig_info->getCertificateValStatus())); - } - return 0; + return 0; } diff --git a/utils/pdftocairo-win32.cc b/utils/pdftocairo-win32.cc index 8918daff..9dc6ad95 100644 --- a/utils/pdftocairo-win32.cc +++ b/utils/pdftocairo-win32.cc @@ -14,17 +14,17 @@ #include <cairo.h> #ifdef CAIRO_HAS_WIN32_SURFACE -#include <cairo-win32.h> +# include <cairo-win32.h> -#include "parseargs.h" -#include "pdftocairo-win32.h" -#include "Win32Console.h" +# include "parseargs.h" +# include "pdftocairo-win32.h" +# include "Win32Console.h" -#include <dlgs.h> -#include <commctrl.h> -#include <commdlg.h> -#include <windowsx.h> -#include <winspool.h> +# include <dlgs.h> +# include <commctrl.h> +# include <commdlg.h> +# include <windowsx.h> +# include <winspool.h> static HDC hdc; static HGLOBAL hDevmode = nullptr; @@ -34,204 +34,162 @@ static char *printerName; struct Win32Option { - const char *name; - int value; + const char *name; + int value; }; -static const Win32Option win32PaperSource[] = -{ - {"upper", DMBIN_UPPER}, - {"onlyone", DMBIN_ONLYONE}, - {"lower", DMBIN_LOWER}, - {"middle", DMBIN_MIDDLE}, - {"manual", DMBIN_MANUAL}, - {"envelope", DMBIN_ENVELOPE}, - {"envmanual", DMBIN_ENVMANUAL}, - {"auto", DMBIN_AUTO}, - {"tractor", DMBIN_TRACTOR}, - {"smallfmt", DMBIN_SMALLFMT}, - {"largefmt", DMBIN_LARGEFMT}, - {"largecapacity", DMBIN_LARGECAPACITY}, - {"formsource", DMBIN_FORMSOURCE}, - {nullptr, 0} -}; +static const Win32Option win32PaperSource[] = { { "upper", DMBIN_UPPER }, { "onlyone", DMBIN_ONLYONE }, + { "lower", DMBIN_LOWER }, { "middle", DMBIN_MIDDLE }, + { "manual", DMBIN_MANUAL }, { "envelope", DMBIN_ENVELOPE }, + { "envmanual", DMBIN_ENVMANUAL }, { "auto", DMBIN_AUTO }, + { "tractor", DMBIN_TRACTOR }, { "smallfmt", DMBIN_SMALLFMT }, + { "largefmt", DMBIN_LARGEFMT }, { "largecapacity", DMBIN_LARGECAPACITY }, + { "formsource", DMBIN_FORMSOURCE }, { nullptr, 0 } }; static void parseSource(GooString *source) { - const Win32Option *option = win32PaperSource; - while (option->name) { - if (source->cmp(option->name) == 0) { - devmode->dmDefaultSource = option->value; - devmode->dmFields |= DM_DEFAULTSOURCE; - return; + const Win32Option *option = win32PaperSource; + while (option->name) { + if (source->cmp(option->name) == 0) { + devmode->dmDefaultSource = option->value; + devmode->dmFields |= DM_DEFAULTSOURCE; + return; + } + option++; } - option++; - } - fprintf(stderr, "Warning: Unknown paper source \"%s\"\n", source->c_str()); + fprintf(stderr, "Warning: Unknown paper source \"%s\"\n", source->c_str()); } -static const Win32Option win32DuplexMode[] = -{ - {"off", DMDUP_SIMPLEX}, - {"short", DMDUP_HORIZONTAL}, - {"long", DMDUP_VERTICAL}, - {nullptr, 0} -}; +static const Win32Option win32DuplexMode[] = { { "off", DMDUP_SIMPLEX }, { "short", DMDUP_HORIZONTAL }, { "long", DMDUP_VERTICAL }, { nullptr, 0 } }; static void parseDuplex(GooString *mode) { - const Win32Option *option = win32DuplexMode; - while (option->name) { - if (mode->cmp(option->name) == 0) { - devmode->dmDuplex = option->value; - devmode->dmFields |= DM_DUPLEX; - return; + const Win32Option *option = win32DuplexMode; + while (option->name) { + if (mode->cmp(option->name) == 0) { + devmode->dmDuplex = option->value; + devmode->dmFields |= DM_DUPLEX; + return; + } + option++; } - option++; - } - fprintf(stderr, "Warning: Unknown duplex mode \"%s\"\n", mode->c_str()); + fprintf(stderr, "Warning: Unknown duplex mode \"%s\"\n", mode->c_str()); } static void fillCommonPrinterOptions(bool duplex) { - if (duplex) { - devmode->dmDuplex = DMDUP_HORIZONTAL; - devmode->dmFields |= DM_DUPLEX; - } + if (duplex) { + devmode->dmDuplex = DMDUP_HORIZONTAL; + devmode->dmFields |= DM_DUPLEX; + } } static void fillPagePrinterOptions(double w, double h) { - w *= 254.0 / 72.0; // units are 0.1mm - h *= 254.0 / 72.0; - if (w > h) { - devmode->dmOrientation = DMORIENT_LANDSCAPE; - devmode->dmPaperWidth = h; - devmode->dmPaperLength = w; - } else { - devmode->dmOrientation = DMORIENT_PORTRAIT; - devmode->dmPaperWidth = w; - devmode->dmPaperLength = h; - } - devmode->dmPaperSize = 0; - devmode->dmFields |= DM_ORIENTATION | DM_PAPERWIDTH | DM_PAPERLENGTH; + w *= 254.0 / 72.0; // units are 0.1mm + h *= 254.0 / 72.0; + if (w > h) { + devmode->dmOrientation = DMORIENT_LANDSCAPE; + devmode->dmPaperWidth = h; + devmode->dmPaperLength = w; + } else { + devmode->dmOrientation = DMORIENT_PORTRAIT; + devmode->dmPaperWidth = w; + devmode->dmPaperLength = h; + } + devmode->dmPaperSize = 0; + devmode->dmFields |= DM_ORIENTATION | DM_PAPERWIDTH | DM_PAPERLENGTH; } - static void fillPrinterOptions(bool duplex, GooString *printOpt) { - //printOpt format is: <opt1>=<val1>,<opt2>=<val2>,... - const char *nextOpt = printOpt->c_str(); - while (nextOpt && *nextOpt) - { - const char *comma = strchr(nextOpt, ','); - GooString opt; - if (comma) { - opt.Set(nextOpt, comma - nextOpt); - nextOpt = comma + 1; - } else { - opt.Set(nextOpt); - nextOpt = NULL; + // printOpt format is: <opt1>=<val1>,<opt2>=<val2>,... + const char *nextOpt = printOpt->c_str(); + while (nextOpt && *nextOpt) { + const char *comma = strchr(nextOpt, ','); + GooString opt; + if (comma) { + opt.Set(nextOpt, comma - nextOpt); + nextOpt = comma + 1; + } else { + opt.Set(nextOpt); + nextOpt = NULL; + } + // here opt is "<optN>=<valN> " + const char *equal = strchr(opt.c_str(), '='); + if (!equal) { + fprintf(stderr, "Warning: unknown printer option \"%s\"\n", opt.c_str()); + continue; + } + int iequal = equal - opt.c_str(); + GooString value(&opt, iequal + 1, opt.getLength() - iequal - 1); + opt.del(iequal, opt.getLength() - iequal); + // here opt is "<optN>" and value is "<valN>" + + if (opt.cmp("source") == 0) { + parseSource(&value); + } else if (opt.cmp("duplex") == 0) { + if (duplex) + fprintf(stderr, "Warning: duplex mode is specified both as standalone and printer options\n"); + else + parseDuplex(&value); + } else { + fprintf(stderr, "Warning: unknown printer option \"%s\"\n", opt.c_str()); + } } - //here opt is "<optN>=<valN> " - const char *equal = strchr(opt.c_str(), '='); - if (!equal) { - fprintf(stderr, "Warning: unknown printer option \"%s\"\n", opt.c_str()); - continue; - } - int iequal = equal - opt.c_str(); - GooString value(&opt, iequal + 1, opt.getLength() - iequal - 1); - opt.del(iequal, opt.getLength() - iequal); - //here opt is "<optN>" and value is "<valN>" - - if (opt.cmp("source") == 0) { - parseSource(&value); - } else if (opt.cmp("duplex") == 0) { - if (duplex) - fprintf(stderr, "Warning: duplex mode is specified both as standalone and printer options\n"); - else - parseDuplex( &value); - } else { - fprintf(stderr, "Warning: unknown printer option \"%s\"\n", opt.c_str()); - } - } } static void getLocalPos(HWND wind, HWND dlg, RECT *rect) { - GetClientRect(wind, rect); - MapWindowPoints(wind, dlg, (LPPOINT)rect, 2); + GetClientRect(wind, rect); + MapWindowPoints(wind, dlg, (LPPOINT)rect, 2); } static HWND createGroupBox(HWND parent, HINSTANCE hInstance, HMENU id, const char *label, RECT *rect) { - HWND hwnd = CreateWindowA(WC_BUTTONA, - label, - WS_CHILD | WS_VISIBLE | BS_GROUPBOX, - rect->left, rect->top, - rect->right - rect->left, - rect->bottom - rect->top, - parent, id, - hInstance, NULL); - HFONT hFont = (HFONT)SendMessage(parent, WM_GETFONT, (WPARAM)0, (LPARAM)0); - if (hFont) - SendMessage(hwnd, WM_SETFONT, (WPARAM) hFont, (LPARAM)0); - return hwnd; + HWND hwnd = CreateWindowA(WC_BUTTONA, label, WS_CHILD | WS_VISIBLE | BS_GROUPBOX, rect->left, rect->top, rect->right - rect->left, rect->bottom - rect->top, parent, id, hInstance, NULL); + HFONT hFont = (HFONT)SendMessage(parent, WM_GETFONT, (WPARAM)0, (LPARAM)0); + if (hFont) + SendMessage(hwnd, WM_SETFONT, (WPARAM)hFont, (LPARAM)0); + return hwnd; } static HWND createCheckBox(HWND parent, HINSTANCE hInstance, HMENU id, const char *label, RECT *rect) { - HWND hwnd = CreateWindowA(WC_BUTTONA, - label, - WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX | WS_TABSTOP, - rect->left, rect->top, - rect->right - rect->left, - rect->bottom - rect->top, - parent, id, - hInstance, NULL); - HFONT hFont = (HFONT)SendMessage(parent, WM_GETFONT, (WPARAM)0, (LPARAM)0); - if (hFont) - SendMessage(hwnd, WM_SETFONT, (WPARAM) hFont, (LPARAM)0); - return hwnd; + HWND hwnd = CreateWindowA(WC_BUTTONA, label, WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX | WS_TABSTOP, rect->left, rect->top, rect->right - rect->left, rect->bottom - rect->top, parent, id, hInstance, NULL); + HFONT hFont = (HFONT)SendMessage(parent, WM_GETFONT, (WPARAM)0, (LPARAM)0); + if (hFont) + SendMessage(hwnd, WM_SETFONT, (WPARAM)hFont, (LPARAM)0); + return hwnd; } static HWND createStaticText(HWND parent, HINSTANCE hinstance, HMENU id, const char *text, RECT *rect) { - HWND hwnd = CreateWindowA(WC_STATICA, - text, - WS_CHILD | WS_VISIBLE | SS_LEFT, - rect->left, rect->top, - rect->right - rect->left, - rect->bottom - rect->top, - parent, id, - hinstance, NULL); - HFONT hFont = (HFONT)SendMessage(parent, WM_GETFONT, (WPARAM)0, (LPARAM)0); - if (hFont) - SendMessage(hwnd, WM_SETFONT, (WPARAM) hFont, (LPARAM)0); - return hwnd; + HWND hwnd = CreateWindowA(WC_STATICA, text, WS_CHILD | WS_VISIBLE | SS_LEFT, rect->left, rect->top, rect->right - rect->left, rect->bottom - rect->top, parent, id, hinstance, NULL); + HFONT hFont = (HFONT)SendMessage(parent, WM_GETFONT, (WPARAM)0, (LPARAM)0); + if (hFont) + SendMessage(hwnd, WM_SETFONT, (WPARAM)hFont, (LPARAM)0); + return hwnd; } static HWND createPageScaleComboBox(HWND parent, HINSTANCE hinstance, HMENU id, RECT *rect) { - HWND hwnd = CreateWindowA(WC_COMBOBOX, - "", - WS_CHILD | WS_VISIBLE | WS_GROUP | WS_TABSTOP | - CBS_DROPDOWNLIST, - rect->left, rect->top, - rect->right - rect->left, - rect->bottom - rect->top, - parent, id, - hinstance, NULL); - HFONT hFont = (HFONT)SendMessage(parent, WM_GETFONT, (WPARAM)0, (LPARAM)0); - if (hFont) - SendMessage(hwnd, WM_SETFONT, (WPARAM) hFont, (LPARAM)0); - ComboBox_AddString(hwnd, "None"); - ComboBox_AddString(hwnd, "Shrink to Printable Area"); - ComboBox_AddString(hwnd, "Fit to Printable Area"); - return hwnd; + HWND hwnd = CreateWindowA(WC_COMBOBOX, "", WS_CHILD | WS_VISIBLE | WS_GROUP | WS_TABSTOP | CBS_DROPDOWNLIST, rect->left, rect->top, rect->right - rect->left, rect->bottom - rect->top, parent, id, hinstance, NULL); + HFONT hFont = (HFONT)SendMessage(parent, WM_GETFONT, (WPARAM)0, (LPARAM)0); + if (hFont) + SendMessage(hwnd, WM_SETFONT, (WPARAM)hFont, (LPARAM)0); + ComboBox_AddString(hwnd, "None"); + ComboBox_AddString(hwnd, "Shrink to Printable Area"); + ComboBox_AddString(hwnd, "Fit to Printable Area"); + return hwnd; } -enum PageScale { NONE = 0, SHRINK = 1, FIT = 2 }; +enum PageScale +{ + NONE = 0, + SHRINK = 1, + FIT = 2 +}; // used to set/get option values in printDialogHookProc static PageScale pageScale; @@ -241,325 +199,308 @@ static bool useOrigPageSize; // PrintDlg callback to customize the print dialog with additional controls. static UINT_PTR CALLBACK printDialogHookProc(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam) { - if (uiMsg == WM_INITDIALOG) { - // Get the extant controls. See dlgs.h and prnsetup.dlg for the PrintDlg control ids. - HWND printerGroupWind = GetDlgItem(hdlg, grp4); - HWND printerComboWind = GetDlgItem(hdlg, cmb4); - HWND nameLabelWind = GetDlgItem(hdlg, stc6); - HWND statusLabelWind = GetDlgItem(hdlg, stc8); - HWND printRangeGroupWind = GetDlgItem(hdlg, grp1); - HWND radio1Wind = GetDlgItem(hdlg, rad1); - HWND radio2Wind = GetDlgItem(hdlg, rad3); - HWND copiesGroupWind = GetDlgItem(hdlg, grp2); - HWND okWind = GetDlgItem(hdlg, IDOK); - HWND cancelWind = GetDlgItem(hdlg, IDCANCEL); - if (!printerGroupWind || !printerComboWind || !nameLabelWind || !statusLabelWind || - !printRangeGroupWind || !radio1Wind || !radio2Wind || !copiesGroupWind || - !okWind || !cancelWind) - return 0; - - // Get the size and position of the above controls relative to the - // print dialog window - RECT printerGroupRect; - RECT printerComboRect; - RECT nameLabelRect; - RECT statusLabelRect; - RECT printRangeGroupRect; - RECT radio1Rect, radio2Rect; - RECT copiesGroupRect; - RECT okRect, cancelRect; - getLocalPos(printerGroupWind, hdlg, &printerGroupRect); - getLocalPos(printerComboWind, hdlg, &printerComboRect); - getLocalPos(nameLabelWind, hdlg, &nameLabelRect); - getLocalPos(statusLabelWind, hdlg, &statusLabelRect); - getLocalPos(printRangeGroupWind, hdlg, &printRangeGroupRect); - getLocalPos(radio1Wind, hdlg, &radio1Rect); - getLocalPos(radio2Wind, hdlg, &radio2Rect); - getLocalPos(copiesGroupWind, hdlg, &copiesGroupRect); - getLocalPos(okWind, hdlg, &okRect); - getLocalPos(cancelWind, hdlg, &cancelRect); - - // Calc space required for new group - int interGroupSpace = printRangeGroupRect.top - printerGroupRect.bottom; - int groupHeight = statusLabelRect.top - printerGroupRect.top + - printRangeGroupRect.bottom - radio1Rect.bottom; - - // Increase dialog size - RECT dlgRect; - GetWindowRect(hdlg, &dlgRect); - SetWindowPos(hdlg, nullptr, - dlgRect.left, dlgRect.top, - dlgRect.right - dlgRect.left, - dlgRect.bottom - dlgRect.top + interGroupSpace + groupHeight, - SWP_NOMOVE|SWP_NOREDRAW|SWP_NOZORDER); - - // Add new group and controls - HINSTANCE hinstance = (HINSTANCE)GetWindowLongPtr(hdlg, GWLP_HINSTANCE); - RECT pdfGroupBoxRect; - pdfGroupBoxRect.left = printRangeGroupRect.left; - pdfGroupBoxRect.right = copiesGroupRect.right; - pdfGroupBoxRect.top = printRangeGroupRect.bottom + interGroupSpace; - pdfGroupBoxRect.bottom = pdfGroupBoxRect.top + groupHeight; - createGroupBox(hdlg, hinstance, (HMENU)grp3, "PDF Print Options", &pdfGroupBoxRect); - - RECT textRect; - textRect.left = nameLabelRect.left; - textRect.right = nameLabelRect.left + 1.8*(printerComboRect.left - nameLabelRect.left); - textRect.top = pdfGroupBoxRect.top + nameLabelRect.top - printerGroupRect.top; - textRect.bottom = textRect.top + nameLabelRect.bottom - nameLabelRect.top; - createStaticText(hdlg, hinstance, (HMENU)stc1, "Page Scaling:", &textRect); - - RECT comboBoxRect; - comboBoxRect.left = textRect.right; - comboBoxRect.right = comboBoxRect.left + printerComboRect.right - printerComboRect.left;; - comboBoxRect.top = pdfGroupBoxRect.top + printerComboRect.top - printerGroupRect.top; - comboBoxRect.bottom = textRect.top + 4*(printerComboRect.bottom - printerComboRect.top); - HWND comboBoxWind = createPageScaleComboBox(hdlg, hinstance, (HMENU)cmb1, &comboBoxRect); - - RECT checkBox1Rect; - checkBox1Rect.left = radio1Rect.left; - checkBox1Rect.right = pdfGroupBoxRect.right - 10; - checkBox1Rect.top = pdfGroupBoxRect.top + statusLabelRect.top - printerGroupRect.top; - checkBox1Rect.bottom = checkBox1Rect.top + radio1Rect.bottom - radio1Rect.top; - HWND checkBox1Wind = createCheckBox(hdlg, hinstance, (HMENU)chx3, "Center", &checkBox1Rect); - - RECT checkBox2Rect; - checkBox2Rect.left = radio1Rect.left; - checkBox2Rect.right = pdfGroupBoxRect.right - 10; - checkBox2Rect.top = checkBox1Rect.top + radio2Rect.top - radio1Rect.top; - checkBox2Rect.bottom = checkBox2Rect.top + radio1Rect.bottom - radio1Rect.top; - HWND checkBox2Wind = createCheckBox(hdlg, hinstance, (HMENU)chx4, "Select page size using document page size", &checkBox2Rect); - - // Move OK and Cancel buttons down ensuring they are last in the Z order - // so that the tab order is correct. - SetWindowPos(okWind, - HWND_BOTTOM, - okRect.left, - okRect.top + interGroupSpace + groupHeight, - 0, 0, - SWP_NOSIZE); // keep current size - SetWindowPos(cancelWind, - HWND_BOTTOM, - cancelRect.left, - cancelRect.top + interGroupSpace + groupHeight, - 0, 0, - SWP_NOSIZE); // keep current size - - // Initialize control values - ComboBox_SetCurSel(comboBoxWind, pageScale); - Button_SetCheck(checkBox1Wind, centerPage ? BST_CHECKED : BST_UNCHECKED); - Button_SetCheck(checkBox2Wind, useOrigPageSize ? BST_CHECKED : BST_UNCHECKED); - - } else if (uiMsg == WM_COMMAND) { - // Save settings - UINT id = LOWORD(wParam); - if (id == cmb1) - pageScale = (PageScale)ComboBox_GetCurSel(GetDlgItem(hdlg, cmb1)); - if (id == chx3) - centerPage = IsDlgButtonChecked(hdlg, chx3); - if (id == chx4) - useOrigPageSize = IsDlgButtonChecked(hdlg, chx4); - } - return 0; + if (uiMsg == WM_INITDIALOG) { + // Get the extant controls. See dlgs.h and prnsetup.dlg for the PrintDlg control ids. + HWND printerGroupWind = GetDlgItem(hdlg, grp4); + HWND printerComboWind = GetDlgItem(hdlg, cmb4); + HWND nameLabelWind = GetDlgItem(hdlg, stc6); + HWND statusLabelWind = GetDlgItem(hdlg, stc8); + HWND printRangeGroupWind = GetDlgItem(hdlg, grp1); + HWND radio1Wind = GetDlgItem(hdlg, rad1); + HWND radio2Wind = GetDlgItem(hdlg, rad3); + HWND copiesGroupWind = GetDlgItem(hdlg, grp2); + HWND okWind = GetDlgItem(hdlg, IDOK); + HWND cancelWind = GetDlgItem(hdlg, IDCANCEL); + if (!printerGroupWind || !printerComboWind || !nameLabelWind || !statusLabelWind || !printRangeGroupWind || !radio1Wind || !radio2Wind || !copiesGroupWind || !okWind || !cancelWind) + return 0; + + // Get the size and position of the above controls relative to the + // print dialog window + RECT printerGroupRect; + RECT printerComboRect; + RECT nameLabelRect; + RECT statusLabelRect; + RECT printRangeGroupRect; + RECT radio1Rect, radio2Rect; + RECT copiesGroupRect; + RECT okRect, cancelRect; + getLocalPos(printerGroupWind, hdlg, &printerGroupRect); + getLocalPos(printerComboWind, hdlg, &printerComboRect); + getLocalPos(nameLabelWind, hdlg, &nameLabelRect); + getLocalPos(statusLabelWind, hdlg, &statusLabelRect); + getLocalPos(printRangeGroupWind, hdlg, &printRangeGroupRect); + getLocalPos(radio1Wind, hdlg, &radio1Rect); + getLocalPos(radio2Wind, hdlg, &radio2Rect); + getLocalPos(copiesGroupWind, hdlg, &copiesGroupRect); + getLocalPos(okWind, hdlg, &okRect); + getLocalPos(cancelWind, hdlg, &cancelRect); + + // Calc space required for new group + int interGroupSpace = printRangeGroupRect.top - printerGroupRect.bottom; + int groupHeight = statusLabelRect.top - printerGroupRect.top + printRangeGroupRect.bottom - radio1Rect.bottom; + + // Increase dialog size + RECT dlgRect; + GetWindowRect(hdlg, &dlgRect); + SetWindowPos(hdlg, nullptr, dlgRect.left, dlgRect.top, dlgRect.right - dlgRect.left, dlgRect.bottom - dlgRect.top + interGroupSpace + groupHeight, SWP_NOMOVE | SWP_NOREDRAW | SWP_NOZORDER); + + // Add new group and controls + HINSTANCE hinstance = (HINSTANCE)GetWindowLongPtr(hdlg, GWLP_HINSTANCE); + RECT pdfGroupBoxRect; + pdfGroupBoxRect.left = printRangeGroupRect.left; + pdfGroupBoxRect.right = copiesGroupRect.right; + pdfGroupBoxRect.top = printRangeGroupRect.bottom + interGroupSpace; + pdfGroupBoxRect.bottom = pdfGroupBoxRect.top + groupHeight; + createGroupBox(hdlg, hinstance, (HMENU)grp3, "PDF Print Options", &pdfGroupBoxRect); + + RECT textRect; + textRect.left = nameLabelRect.left; + textRect.right = nameLabelRect.left + 1.8 * (printerComboRect.left - nameLabelRect.left); + textRect.top = pdfGroupBoxRect.top + nameLabelRect.top - printerGroupRect.top; + textRect.bottom = textRect.top + nameLabelRect.bottom - nameLabelRect.top; + createStaticText(hdlg, hinstance, (HMENU)stc1, "Page Scaling:", &textRect); + + RECT comboBoxRect; + comboBoxRect.left = textRect.right; + comboBoxRect.right = comboBoxRect.left + printerComboRect.right - printerComboRect.left; + ; + comboBoxRect.top = pdfGroupBoxRect.top + printerComboRect.top - printerGroupRect.top; + comboBoxRect.bottom = textRect.top + 4 * (printerComboRect.bottom - printerComboRect.top); + HWND comboBoxWind = createPageScaleComboBox(hdlg, hinstance, (HMENU)cmb1, &comboBoxRect); + + RECT checkBox1Rect; + checkBox1Rect.left = radio1Rect.left; + checkBox1Rect.right = pdfGroupBoxRect.right - 10; + checkBox1Rect.top = pdfGroupBoxRect.top + statusLabelRect.top - printerGroupRect.top; + checkBox1Rect.bottom = checkBox1Rect.top + radio1Rect.bottom - radio1Rect.top; + HWND checkBox1Wind = createCheckBox(hdlg, hinstance, (HMENU)chx3, "Center", &checkBox1Rect); + + RECT checkBox2Rect; + checkBox2Rect.left = radio1Rect.left; + checkBox2Rect.right = pdfGroupBoxRect.right - 10; + checkBox2Rect.top = checkBox1Rect.top + radio2Rect.top - radio1Rect.top; + checkBox2Rect.bottom = checkBox2Rect.top + radio1Rect.bottom - radio1Rect.top; + HWND checkBox2Wind = createCheckBox(hdlg, hinstance, (HMENU)chx4, "Select page size using document page size", &checkBox2Rect); + + // Move OK and Cancel buttons down ensuring they are last in the Z order + // so that the tab order is correct. + SetWindowPos(okWind, HWND_BOTTOM, okRect.left, okRect.top + interGroupSpace + groupHeight, 0, 0, + SWP_NOSIZE); // keep current size + SetWindowPos(cancelWind, HWND_BOTTOM, cancelRect.left, cancelRect.top + interGroupSpace + groupHeight, 0, 0, + SWP_NOSIZE); // keep current size + + // Initialize control values + ComboBox_SetCurSel(comboBoxWind, pageScale); + Button_SetCheck(checkBox1Wind, centerPage ? BST_CHECKED : BST_UNCHECKED); + Button_SetCheck(checkBox2Wind, useOrigPageSize ? BST_CHECKED : BST_UNCHECKED); + + } else if (uiMsg == WM_COMMAND) { + // Save settings + UINT id = LOWORD(wParam); + if (id == cmb1) + pageScale = (PageScale)ComboBox_GetCurSel(GetDlgItem(hdlg, cmb1)); + if (id == chx3) + centerPage = IsDlgButtonChecked(hdlg, chx3); + if (id == chx4) + useOrigPageSize = IsDlgButtonChecked(hdlg, chx4); + } + return 0; } -void win32SetupPrinter(GooString *printer, GooString *printOpt, - bool duplex, bool setupdlg) +void win32SetupPrinter(GooString *printer, GooString *printOpt, bool duplex, bool setupdlg) { - if (printer->c_str()[0] == 0) { - DWORD size = 0; - GetDefaultPrinterA(nullptr, &size); - printerName = (char*)gmalloc(size); - GetDefaultPrinterA(printerName, &size); - } else { - printerName = copyString(printer->c_str(), printer->getLength()); - } - - //Query the size of the DEVMODE struct - LONG szProp = DocumentPropertiesA(nullptr, nullptr, printerName, nullptr, nullptr, 0); - if (szProp < 0) { - fprintf(stderr, "Error: Printer \"%s\" not found\n", printerName); - exit(99); - } - devmode = (DEVMODEA*)gmalloc(szProp); - memset(devmode, 0, szProp); - devmode->dmSize = sizeof(DEVMODEA); - devmode->dmSpecVersion = DM_SPECVERSION; - //Load the current default configuration for the printer into devmode - if (DocumentPropertiesA(nullptr, nullptr, printerName, devmode, devmode, DM_OUT_BUFFER) < 0) { - fprintf(stderr, "Error: Printer \"%s\" not found\n", printerName); - exit(99); - } - - // Update devmode with selected print options - fillCommonPrinterOptions(duplex); - fillPrinterOptions(duplex, printOpt); - - // Call DocumentProperties again so the driver can update its private data - // with the modified print options. This will also display the printer - // properties dialog if setupdlg is true. - int ret; - DWORD mode = DM_IN_BUFFER | DM_OUT_BUFFER; - if (setupdlg) - mode |= DM_IN_PROMPT; - ret = DocumentPropertiesA(nullptr, nullptr, printerName, devmode, devmode, mode); - if (ret < 0) { - fprintf(stderr, "Error: Printer \"%s\" not found\n", printerName); - exit(99); - } - if (setupdlg && ret == IDCANCEL) - exit(0); - - hdc = CreateDCA(nullptr, printerName, nullptr, devmode); - if (!hdc) { - fprintf(stderr, "Error: Printer \"%s\" not found\n", printerName); - exit(99); - } + if (printer->c_str()[0] == 0) { + DWORD size = 0; + GetDefaultPrinterA(nullptr, &size); + printerName = (char *)gmalloc(size); + GetDefaultPrinterA(printerName, &size); + } else { + printerName = copyString(printer->c_str(), printer->getLength()); + } + + // Query the size of the DEVMODE struct + LONG szProp = DocumentPropertiesA(nullptr, nullptr, printerName, nullptr, nullptr, 0); + if (szProp < 0) { + fprintf(stderr, "Error: Printer \"%s\" not found\n", printerName); + exit(99); + } + devmode = (DEVMODEA *)gmalloc(szProp); + memset(devmode, 0, szProp); + devmode->dmSize = sizeof(DEVMODEA); + devmode->dmSpecVersion = DM_SPECVERSION; + // Load the current default configuration for the printer into devmode + if (DocumentPropertiesA(nullptr, nullptr, printerName, devmode, devmode, DM_OUT_BUFFER) < 0) { + fprintf(stderr, "Error: Printer \"%s\" not found\n", printerName); + exit(99); + } + + // Update devmode with selected print options + fillCommonPrinterOptions(duplex); + fillPrinterOptions(duplex, printOpt); + + // Call DocumentProperties again so the driver can update its private data + // with the modified print options. This will also display the printer + // properties dialog if setupdlg is true. + int ret; + DWORD mode = DM_IN_BUFFER | DM_OUT_BUFFER; + if (setupdlg) + mode |= DM_IN_PROMPT; + ret = DocumentPropertiesA(nullptr, nullptr, printerName, devmode, devmode, mode); + if (ret < 0) { + fprintf(stderr, "Error: Printer \"%s\" not found\n", printerName); + exit(99); + } + if (setupdlg && ret == IDCANCEL) + exit(0); + + hdc = CreateDCA(nullptr, printerName, nullptr, devmode); + if (!hdc) { + fprintf(stderr, "Error: Printer \"%s\" not found\n", printerName); + exit(99); + } } -void win32ShowPrintDialog(bool *expand, bool *noShrink, bool *noCenter, - bool *usePDFPageSize, bool *allPages, - int *firstPage, int *lastPage, int maxPages) +void win32ShowPrintDialog(bool *expand, bool *noShrink, bool *noCenter, bool *usePDFPageSize, bool *allPages, int *firstPage, int *lastPage, int maxPages) { - PRINTDLG pd; - memset(&pd, 0, sizeof(PRINTDLG)); - pd.lStructSize = sizeof(PRINTDLG); - pd.Flags = PD_NOSELECTION | PD_ENABLEPRINTHOOK | PD_USEDEVMODECOPIESANDCOLLATE | PD_RETURNDC; - if (*allPages) { - pd.nFromPage = 1; - pd.nToPage = maxPages; - } else { - pd.Flags |= PD_PAGENUMS; - pd.nFromPage = *firstPage; - pd.nToPage = *lastPage; - } - pd.nCopies = 1; - pd.nMinPage = 1; - pd.nMaxPage = maxPages; - pd.lpfnPrintHook = printDialogHookProc; - if (!*expand && *noShrink) - pageScale = NONE; - else if (!*expand && !*noShrink) - pageScale = SHRINK; - else - pageScale = FIT; - centerPage = !*noCenter; - useOrigPageSize = *usePDFPageSize; - - if (PrintDlgA(&pd)) { - // Ok - hDevnames = pd.hDevNames; - DEVNAMES *devnames = (DEVNAMES*)GlobalLock(hDevnames); - printerName = (char*)devnames + devnames->wDeviceOffset; - hDevmode = pd.hDevMode; - devmode = (DEVMODEA*)GlobalLock(hDevmode); - hdc = pd.hDC; - if (pd.Flags & PD_PAGENUMS) { - *allPages = false; - *firstPage = pd.nFromPage; - *lastPage = pd.nToPage; + PRINTDLG pd; + memset(&pd, 0, sizeof(PRINTDLG)); + pd.lStructSize = sizeof(PRINTDLG); + pd.Flags = PD_NOSELECTION | PD_ENABLEPRINTHOOK | PD_USEDEVMODECOPIESANDCOLLATE | PD_RETURNDC; + if (*allPages) { + pd.nFromPage = 1; + pd.nToPage = maxPages; } else { - *allPages = true; + pd.Flags |= PD_PAGENUMS; + pd.nFromPage = *firstPage; + pd.nToPage = *lastPage; } - if (pageScale == NONE) { - *expand = false; - *noShrink = true; - } else if (pageScale == SHRINK) { - *expand = false; - *noShrink = false; + pd.nCopies = 1; + pd.nMinPage = 1; + pd.nMaxPage = maxPages; + pd.lpfnPrintHook = printDialogHookProc; + if (!*expand && *noShrink) + pageScale = NONE; + else if (!*expand && !*noShrink) + pageScale = SHRINK; + else + pageScale = FIT; + centerPage = !*noCenter; + useOrigPageSize = *usePDFPageSize; + + if (PrintDlgA(&pd)) { + // Ok + hDevnames = pd.hDevNames; + DEVNAMES *devnames = (DEVNAMES *)GlobalLock(hDevnames); + printerName = (char *)devnames + devnames->wDeviceOffset; + hDevmode = pd.hDevMode; + devmode = (DEVMODEA *)GlobalLock(hDevmode); + hdc = pd.hDC; + if (pd.Flags & PD_PAGENUMS) { + *allPages = false; + *firstPage = pd.nFromPage; + *lastPage = pd.nToPage; + } else { + *allPages = true; + } + if (pageScale == NONE) { + *expand = false; + *noShrink = true; + } else if (pageScale == SHRINK) { + *expand = false; + *noShrink = false; + } else { + *expand = true; + *noShrink = false; + } + *noCenter = !centerPage; + *usePDFPageSize = useOrigPageSize; } else { - *expand = true; - *noShrink = false; + // Cancel + exit(0); } - *noCenter = !centerPage; - *usePDFPageSize = useOrigPageSize; - } else { - // Cancel - exit(0); - } } cairo_surface_t *win32BeginDocument(GooString *inputFileName, GooString *outputFileName) { - DOCINFOA docinfo; - memset(&docinfo, 0, sizeof(docinfo)); - docinfo.cbSize = sizeof(docinfo); - if (inputFileName->cmp("fd://0") == 0) - docinfo.lpszDocName = "pdftocairo <stdin>"; - else - docinfo.lpszDocName = inputFileName->c_str(); - if (outputFileName) - docinfo.lpszOutput = outputFileName->c_str(); - if (StartDocA(hdc, &docinfo) <=0) { - fprintf(stderr, "Error: StartDoc failed\n"); - exit(99); - } - - return cairo_win32_printing_surface_create(hdc); + DOCINFOA docinfo; + memset(&docinfo, 0, sizeof(docinfo)); + docinfo.cbSize = sizeof(docinfo); + if (inputFileName->cmp("fd://0") == 0) + docinfo.lpszDocName = "pdftocairo <stdin>"; + else + docinfo.lpszDocName = inputFileName->c_str(); + if (outputFileName) + docinfo.lpszOutput = outputFileName->c_str(); + if (StartDocA(hdc, &docinfo) <= 0) { + fprintf(stderr, "Error: StartDoc failed\n"); + exit(99); + } + + return cairo_win32_printing_surface_create(hdc); } void win32BeginPage(double *w, double *h, bool changePageSize, bool useFullPage) { - if (changePageSize) - fillPagePrinterOptions(*w, *h); - if (DocumentPropertiesA(nullptr, nullptr, printerName, devmode, devmode, DM_IN_BUFFER | DM_OUT_BUFFER) < 0) { - fprintf(stderr, "Error: Printer \"%s\" not found\n", printerName); - exit(99); - } - ResetDCA(hdc, devmode); - - // Get actual paper size or if useFullPage is false the printable area. - // Transform the hdc scale to points to be consistent with other cairo backends - int x_dpi = GetDeviceCaps (hdc, LOGPIXELSX); - int y_dpi = GetDeviceCaps (hdc, LOGPIXELSY); - int x_off = GetDeviceCaps (hdc, PHYSICALOFFSETX); - int y_off = GetDeviceCaps (hdc, PHYSICALOFFSETY); - if (useFullPage) { - *w = GetDeviceCaps (hdc, PHYSICALWIDTH)*72.0/x_dpi; - *h = GetDeviceCaps (hdc, PHYSICALHEIGHT)*72.0/y_dpi; - } else { - *w = GetDeviceCaps (hdc, HORZRES)*72.0/x_dpi; - *h = GetDeviceCaps (hdc, VERTRES)*72.0/y_dpi; - } - XFORM xform; - xform.eM11 = x_dpi/72.0; - xform.eM12 = 0; - xform.eM21 = 0; - xform.eM22 = y_dpi/72.0; - if (useFullPage) { - xform.eDx = -x_off; - xform.eDy = -y_off; - } else { - xform.eDx = 0; - xform.eDy = 0; - } - SetGraphicsMode (hdc, GM_ADVANCED); - SetWorldTransform (hdc, &xform); - - StartPage(hdc); + if (changePageSize) + fillPagePrinterOptions(*w, *h); + if (DocumentPropertiesA(nullptr, nullptr, printerName, devmode, devmode, DM_IN_BUFFER | DM_OUT_BUFFER) < 0) { + fprintf(stderr, "Error: Printer \"%s\" not found\n", printerName); + exit(99); + } + ResetDCA(hdc, devmode); + + // Get actual paper size or if useFullPage is false the printable area. + // Transform the hdc scale to points to be consistent with other cairo backends + int x_dpi = GetDeviceCaps(hdc, LOGPIXELSX); + int y_dpi = GetDeviceCaps(hdc, LOGPIXELSY); + int x_off = GetDeviceCaps(hdc, PHYSICALOFFSETX); + int y_off = GetDeviceCaps(hdc, PHYSICALOFFSETY); + if (useFullPage) { + *w = GetDeviceCaps(hdc, PHYSICALWIDTH) * 72.0 / x_dpi; + *h = GetDeviceCaps(hdc, PHYSICALHEIGHT) * 72.0 / y_dpi; + } else { + *w = GetDeviceCaps(hdc, HORZRES) * 72.0 / x_dpi; + *h = GetDeviceCaps(hdc, VERTRES) * 72.0 / y_dpi; + } + XFORM xform; + xform.eM11 = x_dpi / 72.0; + xform.eM12 = 0; + xform.eM21 = 0; + xform.eM22 = y_dpi / 72.0; + if (useFullPage) { + xform.eDx = -x_off; + xform.eDy = -y_off; + } else { + xform.eDx = 0; + xform.eDy = 0; + } + SetGraphicsMode(hdc, GM_ADVANCED); + SetWorldTransform(hdc, &xform); + + StartPage(hdc); } void win32EndPage(GooString *imageFileName) { - EndPage(hdc); + EndPage(hdc); } void win32EndDocument() { - EndDoc(hdc); - DeleteDC(hdc); - if (hDevmode) { - GlobalUnlock(hDevmode); - GlobalFree(hDevmode); - } else { - gfree(devmode); - } - if (hDevnames) { - GlobalUnlock(hDevnames); - GlobalFree(hDevnames); - } else { - gfree(printerName); - } + EndDoc(hdc); + DeleteDC(hdc); + if (hDevmode) { + GlobalUnlock(hDevmode); + GlobalFree(hDevmode); + } else { + gfree(devmode); + } + if (hDevnames) { + GlobalUnlock(hDevnames); + GlobalFree(hDevnames); + } else { + gfree(printerName); + } } #endif // CAIRO_HAS_WIN32_SURFACE diff --git a/utils/pdftocairo-win32.h b/utils/pdftocairo-win32.h index 1c859bcf..b99bd406 100644 --- a/utils/pdftocairo-win32.h +++ b/utils/pdftocairo-win32.h @@ -17,13 +17,10 @@ #ifdef CAIRO_HAS_WIN32_SURFACE -#include <cairo-win32.h> +# include <cairo-win32.h> -void win32SetupPrinter(GooString *printer, GooString *printOpt, - bool duplex, bool setupdlg); -void win32ShowPrintDialog(bool *expand, bool *noShrink, bool *noCenter, - bool *usePDFPageSize, bool *allPages, - int *firstPage, int *lastPage, int maxPages); +void win32SetupPrinter(GooString *printer, GooString *printOpt, bool duplex, bool setupdlg); +void win32ShowPrintDialog(bool *expand, bool *noShrink, bool *noCenter, bool *usePDFPageSize, bool *allPages, int *firstPage, int *lastPage, int maxPages); cairo_surface_t *win32BeginDocument(GooString *inputFileName, GooString *outputFileName); void win32BeginPage(double *w, double *h, bool changePageSize, bool useFullPage); void win32EndPage(GooString *imageFileName); diff --git a/utils/pdftocairo.cc b/utils/pdftocairo.cc index a6d9384f..d3302c7b 100644 --- a/utils/pdftocairo.cc +++ b/utils/pdftocairo.cc @@ -63,22 +63,21 @@ #include "Win32Console.h" #include "numberofcharacters.h" #ifdef USE_CMS -#include <lcms2.h> +# include <lcms2.h> #endif #include <cairo.h> #ifdef CAIRO_HAS_PS_SURFACE -#include <cairo-ps.h> +# include <cairo-ps.h> #endif #ifdef CAIRO_HAS_PDF_SURFACE -#include <cairo-pdf.h> +# include <cairo-pdf.h> #endif #ifdef CAIRO_HAS_SVG_SURFACE -#include <cairo-svg.h> +# include <cairo-svg.h> #endif #include "pdftocairo-win32.h" - static bool png = false; static bool jpeg = false; static bool ps = false; @@ -144,145 +143,89 @@ static bool setupdlg = false; static const ArgDesc argDesc[] = { #ifdef ENABLE_LIBPNG - {"-png", argFlag, &png, 0, - "generate a PNG file"}, + { "-png", argFlag, &png, 0, "generate a PNG file" }, #endif #ifdef ENABLE_LIBJPEG - {"-jpeg", argFlag, &jpeg, 0, - "generate a JPEG file"}, - {"-jpegopt", argGooString, &jpegOpt, 0, - "jpeg options, with format <opt1>=<val1>[,<optN>=<valN>]*"}, + { "-jpeg", argFlag, &jpeg, 0, "generate a JPEG file" }, + { "-jpegopt", argGooString, &jpegOpt, 0, "jpeg options, with format <opt1>=<val1>[,<optN>=<valN>]*" }, #endif #ifdef ENABLE_LIBTIFF - {"-tiff", argFlag, &tiff, 0, - "generate a TIFF file"}, - {"-tiffcompression", argString, tiffCompressionStr, sizeof(tiffCompressionStr), - "set TIFF compression: none, packbits, jpeg, lzw, deflate"}, + { "-tiff", argFlag, &tiff, 0, "generate a TIFF file" }, + { "-tiffcompression", argString, tiffCompressionStr, sizeof(tiffCompressionStr), "set TIFF compression: none, packbits, jpeg, lzw, deflate" }, #endif #ifdef CAIRO_HAS_PS_SURFACE - {"-ps", argFlag, &ps, 0, - "generate PostScript file"}, - {"-eps", argFlag, &eps, 0, - "generate Encapsulated PostScript (EPS)"}, + { "-ps", argFlag, &ps, 0, "generate PostScript file" }, + { "-eps", argFlag, &eps, 0, "generate Encapsulated PostScript (EPS)" }, #endif #ifdef CAIRO_HAS_PDF_SURFACE - {"-pdf", argFlag, &pdf, 0, - "generate a PDF file"}, + { "-pdf", argFlag, &pdf, 0, "generate a PDF file" }, #endif #ifdef CAIRO_HAS_SVG_SURFACE - {"-svg", argFlag, &svg, 0, - "generate a Scalable Vector Graphics (SVG) file"}, + { "-svg", argFlag, &svg, 0, "generate a Scalable Vector Graphics (SVG) file" }, #endif #ifdef CAIRO_HAS_WIN32_SURFACE - {"-print", argFlag, &printToWin32, 0, - "print to a Windows printer"}, - {"-printdlg", argFlag, &printdlg, 0, - "show Windows print dialog and print"}, - {"-printer", argGooString, &printer, 0, - "printer name or use default if this option is not specified"}, - {"-printopt", argGooString, &printOpt, 0, - "printer options, with format <opt1>=<val1>[,<optN>=<valN>]*"}, - {"-setupdlg", argFlag, &setupdlg, 0, - "show printer setup dialog before printing"}, + { "-print", argFlag, &printToWin32, 0, "print to a Windows printer" }, + { "-printdlg", argFlag, &printdlg, 0, "show Windows print dialog and print" }, + { "-printer", argGooString, &printer, 0, "printer name or use default if this option is not specified" }, + { "-printopt", argGooString, &printOpt, 0, "printer options, with format <opt1>=<val1>[,<optN>=<valN>]*" }, + { "-setupdlg", argFlag, &setupdlg, 0, "show printer setup dialog before printing" }, #endif - {"-f", argInt, &firstPage, 0, - "first page to print"}, - {"-l", argInt, &lastPage, 0, - "last page to print"}, - {"-o", argFlag, &printOnlyOdd, 0, - "print only odd pages"}, - {"-e", argFlag, &printOnlyEven, 0, - "print only even pages"}, - {"-singlefile", argFlag, &singleFile, 0, - "write only the first page and do not add digits"}, - - {"-r", argFP, &resolution, 0, - "resolution, in PPI (default is 150)"}, - {"-rx", argFP, &x_resolution, 0, - "X resolution, in PPI (default is 150)"}, - {"-ry", argFP, &y_resolution, 0, - "Y resolution, in PPI (default is 150)"}, - {"-scale-to", argInt, &scaleTo, 0, - "scales each page to fit within scale-to*scale-to pixel box"}, - {"-scale-to-x", argInt, &x_scaleTo, 0, - "scales each page horizontally to fit in scale-to-x pixels"}, - {"-scale-to-y", argInt, &y_scaleTo, 0, - "scales each page vertically to fit in scale-to-y pixels"}, - - {"-x", argInt, &crop_x, 0, - "x-coordinate of the crop area top left corner"}, - {"-y", argInt, &crop_y, 0, - "y-coordinate of the crop area top left corner"}, - {"-W", argInt, &crop_w, 0, - "width of crop area in pixels (default is 0)"}, - {"-H", argInt, &crop_h, 0, - "height of crop area in pixels (default is 0)"}, - {"-sz", argInt, &sz, 0, - "size of crop square in pixels (sets W and H)"}, - {"-cropbox",argFlag, &useCropBox, 0, - "use the crop box rather than media box"}, - - {"-mono", argFlag, &mono, 0, - "generate a monochrome image file (PNG, JPEG)"}, - {"-gray", argFlag, &gray, 0, - "generate a grayscale image file (PNG, JPEG)"}, - {"-transp", argFlag, &transp, 0, - "use a transparent background instead of white (PNG)"}, - {"-antialias", argGooString, &antialias, 0, - "set cairo antialias option"}, + { "-f", argInt, &firstPage, 0, "first page to print" }, + { "-l", argInt, &lastPage, 0, "last page to print" }, + { "-o", argFlag, &printOnlyOdd, 0, "print only odd pages" }, + { "-e", argFlag, &printOnlyEven, 0, "print only even pages" }, + { "-singlefile", argFlag, &singleFile, 0, "write only the first page and do not add digits" }, + + { "-r", argFP, &resolution, 0, "resolution, in PPI (default is 150)" }, + { "-rx", argFP, &x_resolution, 0, "X resolution, in PPI (default is 150)" }, + { "-ry", argFP, &y_resolution, 0, "Y resolution, in PPI (default is 150)" }, + { "-scale-to", argInt, &scaleTo, 0, "scales each page to fit within scale-to*scale-to pixel box" }, + { "-scale-to-x", argInt, &x_scaleTo, 0, "scales each page horizontally to fit in scale-to-x pixels" }, + { "-scale-to-y", argInt, &y_scaleTo, 0, "scales each page vertically to fit in scale-to-y pixels" }, + + { "-x", argInt, &crop_x, 0, "x-coordinate of the crop area top left corner" }, + { "-y", argInt, &crop_y, 0, "y-coordinate of the crop area top left corner" }, + { "-W", argInt, &crop_w, 0, "width of crop area in pixels (default is 0)" }, + { "-H", argInt, &crop_h, 0, "height of crop area in pixels (default is 0)" }, + { "-sz", argInt, &sz, 0, "size of crop square in pixels (sets W and H)" }, + { "-cropbox", argFlag, &useCropBox, 0, "use the crop box rather than media box" }, + + { "-mono", argFlag, &mono, 0, "generate a monochrome image file (PNG, JPEG)" }, + { "-gray", argFlag, &gray, 0, "generate a grayscale image file (PNG, JPEG)" }, + { "-transp", argFlag, &transp, 0, "use a transparent background instead of white (PNG)" }, + { "-antialias", argGooString, &antialias, 0, "set cairo antialias option" }, #ifdef USE_CMS - {"-icc", argGooString, &icc, 0, - "ICC color profile to use"}, + { "-icc", argGooString, &icc, 0, "ICC color profile to use" }, #endif - {"-level2", argFlag, &level2, 0, - "generate Level 2 PostScript (PS, EPS)"}, - {"-level3", argFlag, &level3, 0, - "generate Level 3 PostScript (PS, EPS)"}, - {"-origpagesizes",argFlag, &origPageSizes,0, - "conserve original page sizes (PS, PDF, SVG)"}, - {"-paper", argString, paperSize, sizeof(paperSize), - "paper size (letter, legal, A4, A3, match)"}, - {"-paperw", argInt, &paperWidth, 0, - "paper width, in points"}, - {"-paperh", argInt, &paperHeight, 0, - "paper height, in points"}, - {"-nocrop", argFlag, &noCrop, 0, - "don't crop pages to CropBox"}, - {"-expand", argFlag, &expand, 0, - "expand pages smaller than the paper size"}, - {"-noshrink", argFlag, &noShrink, 0, - "don't shrink pages larger than the paper size"}, - {"-nocenter", argFlag, &noCenter, 0, - "don't center pages smaller than the paper size"}, - {"-duplex", argFlag, &duplex, 0, - "enable duplex printing"}, - - {"-opw", argString, ownerPassword, sizeof(ownerPassword), - "owner password (for encrypted files)"}, - {"-upw", argString, userPassword, sizeof(userPassword), - "user password (for encrypted files)"}, - - {"-q", argFlag, &quiet, 0, - "don't print any messages or errors"}, - {"-v", argFlag, &printVersion, 0, - "print copyright and version info"}, - {"-h", argFlag, &printHelp, 0, - "print usage information"}, - {"-help", argFlag, &printHelp, 0, - "print usage information"}, - {"--help", argFlag, &printHelp, 0, - "print usage information"}, - {"-?", argFlag, &printHelp, 0, - "print usage information"}, - {} + { "-level2", argFlag, &level2, 0, "generate Level 2 PostScript (PS, EPS)" }, + { "-level3", argFlag, &level3, 0, "generate Level 3 PostScript (PS, EPS)" }, + { "-origpagesizes", argFlag, &origPageSizes, 0, "conserve original page sizes (PS, PDF, SVG)" }, + { "-paper", argString, paperSize, sizeof(paperSize), "paper size (letter, legal, A4, A3, match)" }, + { "-paperw", argInt, &paperWidth, 0, "paper width, in points" }, + { "-paperh", argInt, &paperHeight, 0, "paper height, in points" }, + { "-nocrop", argFlag, &noCrop, 0, "don't crop pages to CropBox" }, + { "-expand", argFlag, &expand, 0, "expand pages smaller than the paper size" }, + { "-noshrink", argFlag, &noShrink, 0, "don't shrink pages larger than the paper size" }, + { "-nocenter", argFlag, &noCenter, 0, "don't center pages smaller than the paper size" }, + { "-duplex", argFlag, &duplex, 0, "enable duplex printing" }, + + { "-opw", argString, ownerPassword, sizeof(ownerPassword), "owner password (for encrypted files)" }, + { "-upw", argString, userPassword, sizeof(userPassword), "user password (for encrypted files)" }, + + { "-q", argFlag, &quiet, 0, "don't print any messages or errors" }, + { "-v", argFlag, &printVersion, 0, "print copyright and version info" }, + { "-h", argFlag, &printHelp, 0, "print usage information" }, + { "-help", argFlag, &printHelp, 0, "print usage information" }, + { "--help", argFlag, &printHelp, 0, "print usage information" }, + { "-?", argFlag, &printHelp, 0, "print usage information" }, + {} }; - -static cairo_surface_t *surface; -static bool printing; -static FILE *output_file; +static cairo_surface_t *surface; +static bool printing; +static FILE *output_file; static bool usePDFPageSize; static cairo_antialias_t antialiasEnum = CAIRO_ANTIALIAS_DEFAULT; @@ -294,1004 +237,970 @@ static GfxLCMSProfilePtr profile; struct AntialiasOption { - const char *name; - cairo_antialias_t value; + const char *name; + cairo_antialias_t value; }; -static const AntialiasOption antialiasOptions[] = -{ - { "default", CAIRO_ANTIALIAS_DEFAULT }, - { "none", CAIRO_ANTIALIAS_NONE }, - { "gray", CAIRO_ANTIALIAS_GRAY }, - { "subpixel", CAIRO_ANTIALIAS_SUBPIXEL }, - { "fast", CAIRO_ANTIALIAS_FAST }, - { "good", CAIRO_ANTIALIAS_GOOD }, - { "best", CAIRO_ANTIALIAS_BEST }, - { nullptr, CAIRO_ANTIALIAS_DEFAULT }, +static const AntialiasOption antialiasOptions[] = { + { "default", CAIRO_ANTIALIAS_DEFAULT }, { "none", CAIRO_ANTIALIAS_NONE }, { "gray", CAIRO_ANTIALIAS_GRAY }, { "subpixel", CAIRO_ANTIALIAS_SUBPIXEL }, + { "fast", CAIRO_ANTIALIAS_FAST }, { "good", CAIRO_ANTIALIAS_GOOD }, { "best", CAIRO_ANTIALIAS_BEST }, { nullptr, CAIRO_ANTIALIAS_DEFAULT }, }; static bool parseAntialiasOption() { - const AntialiasOption *option = antialiasOptions; - while (option->name) { - if (antialias.cmp(option->name) == 0) { - antialiasEnum = option->value; - return true; + const AntialiasOption *option = antialiasOptions; + while (option->name) { + if (antialias.cmp(option->name) == 0) { + antialiasEnum = option->value; + return true; + } + option++; + } + + fprintf(stderr, "Error: Invalid antialias option \"%s\"\n", antialias.c_str()); + fprintf(stderr, "Valid options are:\n"); + option = antialiasOptions; + while (option->name) { + fprintf(stderr, " %s\n", option->name); + option++; } - option++; - } - - fprintf(stderr, "Error: Invalid antialias option \"%s\"\n", antialias.c_str()); - fprintf(stderr, "Valid options are:\n"); - option = antialiasOptions; - while (option->name) { - fprintf(stderr, " %s\n", option->name); - option++; - } - return false; + return false; } static bool parseJpegOptions() { - //jpegOpt format is: <opt1>=<val1>,<opt2>=<val2>,... - const char *nextOpt = jpegOpt.c_str(); - while (nextOpt && *nextOpt) - { - const char *comma = strchr(nextOpt, ','); - GooString opt; - if (comma) { - opt.Set(nextOpt, comma - nextOpt); - nextOpt = comma + 1; - } else { - opt.Set(nextOpt); - nextOpt = nullptr; - } - //here opt is "<optN>=<valN> " - const char *equal = strchr(opt.c_str(), '='); - if (!equal) { - fprintf(stderr, "Unknown jpeg option \"%s\"\n", opt.c_str()); - return false; - } - int iequal = equal - opt.c_str(); - GooString value(&opt, iequal + 1, opt.getLength() - iequal - 1); - opt.del(iequal, opt.getLength() - iequal); - //here opt is "<optN>" and value is "<valN>" - - if (opt.cmp("quality") == 0) { - if (!isInt(value.c_str())) { - fprintf(stderr, "Invalid jpeg quality\n"); - return false; - } - jpegQuality = atoi(value.c_str()); - if (jpegQuality < 0 || jpegQuality > 100) { - fprintf(stderr, "jpeg quality must be between 0 and 100\n"); - return false; - } - } else if (opt.cmp("progressive") == 0) { - jpegProgressive = false; - if (value.cmp("y") == 0) { - jpegProgressive = true; - } else if (value.cmp("n") != 0) { - fprintf(stderr, "jpeg progressive option must be \"y\" or \"n\"\n"); - return false; - } - } else if (opt.cmp("optimize") == 0 || opt.cmp("optimise") == 0) { - jpegOptimize = false; - if (value.cmp("y") == 0) { - jpegOptimize = true; - } else if (value.cmp("n") != 0) { - fprintf(stderr, "jpeg optimize option must be \"y\" or \"n\"\n"); - return false; - } - } else { - fprintf(stderr, "Unknown jpeg option \"%s\"\n", opt.c_str()); - return false; + // jpegOpt format is: <opt1>=<val1>,<opt2>=<val2>,... + const char *nextOpt = jpegOpt.c_str(); + while (nextOpt && *nextOpt) { + const char *comma = strchr(nextOpt, ','); + GooString opt; + if (comma) { + opt.Set(nextOpt, comma - nextOpt); + nextOpt = comma + 1; + } else { + opt.Set(nextOpt); + nextOpt = nullptr; + } + // here opt is "<optN>=<valN> " + const char *equal = strchr(opt.c_str(), '='); + if (!equal) { + fprintf(stderr, "Unknown jpeg option \"%s\"\n", opt.c_str()); + return false; + } + int iequal = equal - opt.c_str(); + GooString value(&opt, iequal + 1, opt.getLength() - iequal - 1); + opt.del(iequal, opt.getLength() - iequal); + // here opt is "<optN>" and value is "<valN>" + + if (opt.cmp("quality") == 0) { + if (!isInt(value.c_str())) { + fprintf(stderr, "Invalid jpeg quality\n"); + return false; + } + jpegQuality = atoi(value.c_str()); + if (jpegQuality < 0 || jpegQuality > 100) { + fprintf(stderr, "jpeg quality must be between 0 and 100\n"); + return false; + } + } else if (opt.cmp("progressive") == 0) { + jpegProgressive = false; + if (value.cmp("y") == 0) { + jpegProgressive = true; + } else if (value.cmp("n") != 0) { + fprintf(stderr, "jpeg progressive option must be \"y\" or \"n\"\n"); + return false; + } + } else if (opt.cmp("optimize") == 0 || opt.cmp("optimise") == 0) { + jpegOptimize = false; + if (value.cmp("y") == 0) { + jpegOptimize = true; + } else if (value.cmp("n") != 0) { + fprintf(stderr, "jpeg optimize option must be \"y\" or \"n\"\n"); + return false; + } + } else { + fprintf(stderr, "Unknown jpeg option \"%s\"\n", opt.c_str()); + return false; + } } - } - return true; + return true; } static void writePageImage(GooString *filename) { - ImgWriter *writer = nullptr; - FILE *file; - int height, width, stride; - unsigned char *data; + ImgWriter *writer = nullptr; + FILE *file; + int height, width, stride; + unsigned char *data; - if (png) { + if (png) { #ifdef ENABLE_LIBPNG - if (transp) - writer = new PNGWriter(PNGWriter::RGBA); - else if (gray) - writer = new PNGWriter(PNGWriter::GRAY); - else if (mono) - writer = new PNGWriter(PNGWriter::MONOCHROME); - else - writer = new PNGWriter(PNGWriter::RGB); - -#ifdef USE_CMS - if (icc_data) { - cmsUInt8Number profileID[17]; - profileID[16] = '\0'; - - cmsGetHeaderProfileID(profile.get(),profileID); - static_cast<PNGWriter*>(writer)->setICCProfile(reinterpret_cast<char *>(profileID), icc_data, icc_data_size); - } else { - static_cast<PNGWriter*>(writer)->setSRGBProfile(); - } -#endif + if (transp) + writer = new PNGWriter(PNGWriter::RGBA); + else if (gray) + writer = new PNGWriter(PNGWriter::GRAY); + else if (mono) + writer = new PNGWriter(PNGWriter::MONOCHROME); + else + writer = new PNGWriter(PNGWriter::RGB); + +# ifdef USE_CMS + if (icc_data) { + cmsUInt8Number profileID[17]; + profileID[16] = '\0'; + + cmsGetHeaderProfileID(profile.get(), profileID); + static_cast<PNGWriter *>(writer)->setICCProfile(reinterpret_cast<char *>(profileID), icc_data, icc_data_size); + } else { + static_cast<PNGWriter *>(writer)->setSRGBProfile(); + } +# endif #endif - } else if (jpeg) { + } else if (jpeg) { #ifdef ENABLE_LIBJPEG - if (gray) - writer = new JpegWriter(JpegWriter::GRAY); - else - writer = new JpegWriter(JpegWriter::RGB); - - static_cast<JpegWriter*>(writer)->setOptimize(jpegOptimize); - static_cast<JpegWriter*>(writer)->setProgressive(jpegProgressive); - if (jpegQuality >= 0) - static_cast<JpegWriter*>(writer)->setQuality(jpegQuality); + if (gray) + writer = new JpegWriter(JpegWriter::GRAY); + else + writer = new JpegWriter(JpegWriter::RGB); + + static_cast<JpegWriter *>(writer)->setOptimize(jpegOptimize); + static_cast<JpegWriter *>(writer)->setProgressive(jpegProgressive); + if (jpegQuality >= 0) + static_cast<JpegWriter *>(writer)->setQuality(jpegQuality); #endif - } else if (tiff) { + } else if (tiff) { #ifdef ENABLE_LIBTIFF - if (transp) - writer = new TiffWriter(TiffWriter::RGBA_PREMULTIPLIED); - else if (gray) - writer = new TiffWriter(TiffWriter::GRAY); - else if (mono) - writer = new TiffWriter(TiffWriter::MONOCHROME); - else - writer = new TiffWriter(TiffWriter::RGB); - static_cast<TiffWriter*>(writer)->setCompressionString(tiffCompressionStr); + if (transp) + writer = new TiffWriter(TiffWriter::RGBA_PREMULTIPLIED); + else if (gray) + writer = new TiffWriter(TiffWriter::GRAY); + else if (mono) + writer = new TiffWriter(TiffWriter::MONOCHROME); + else + writer = new TiffWriter(TiffWriter::RGB); + static_cast<TiffWriter *>(writer)->setCompressionString(tiffCompressionStr); #endif - } - if (!writer) - return; - - if (filename->cmp("fd://0") == 0) - file = stdout; - else - file = fopen(filename->c_str(), "wb"); - - if (!file) { - fprintf(stderr, "Error opening output file %s\n", filename->c_str()); - exit(2); - } - - height = cairo_image_surface_get_height(surface); - width = cairo_image_surface_get_width(surface); - stride = cairo_image_surface_get_stride(surface); - cairo_surface_flush(surface); - data = cairo_image_surface_get_data(surface); - - if (!writer->init(file, width, height, x_resolution, y_resolution)) { - fprintf(stderr, "Error writing %s\n", filename->c_str()); - exit(2); - } - unsigned char *row = (unsigned char *) gmallocn(width, 4); - - for (int y = 0; y < height; y++ ) { - uint32_t *pixel = reinterpret_cast<uint32_t *>((data + y*stride)); - unsigned char *rowp = row; - int bit = 7; - for (int x = 0; x < width; x++, pixel++) { - if (transp) { - if (tiff) { - // RGBA premultipled format - *rowp++ = (*pixel & 0xff0000) >> 16; - *rowp++ = (*pixel & 0x00ff00) >> 8; - *rowp++ = (*pixel & 0x0000ff) >> 0; - *rowp++ = (*pixel & 0xff000000) >> 24; - } else { - // unpremultiply into RGBA format - uint8_t a; - a = (*pixel & 0xff000000) >> 24; - if (a == 0) { - *rowp++ = 0; - *rowp++ = 0; - *rowp++ = 0; - } else { - *rowp++ = (((*pixel & 0xff0000) >> 16) * 255 + a / 2) / a; - *rowp++ = (((*pixel & 0x00ff00) >> 8) * 255 + a / 2) / a; - *rowp++ = (((*pixel & 0x0000ff) >> 0) * 255 + a / 2) / a; - } - *rowp++ = a; - } - } else if (gray || mono) { - // convert to gray - // The PDF Reference specifies the DeviceRGB to DeviceGray conversion as - // gray = 0.3*red + 0.59*green + 0.11*blue - const int r = (*pixel & 0x00ff0000) >> 16; - const int g = (*pixel & 0x0000ff00) >> 8; - const int b = (*pixel & 0x000000ff) >> 0; - // an arbitrary integer approximation of .3*r + .59*g + .11*b - const int grayValue = (r*19661+g*38666+b*7209 + 32829)>>16; - if (mono) { - if (bit == 7) - *rowp = 0; - if (grayValue > 127) - *rowp |= (1 << bit); - bit--; - if (bit < 0) { - bit = 7; - rowp++; - } - } else { - *rowp++ = grayValue; + } + if (!writer) + return; + + if (filename->cmp("fd://0") == 0) + file = stdout; + else + file = fopen(filename->c_str(), "wb"); + + if (!file) { + fprintf(stderr, "Error opening output file %s\n", filename->c_str()); + exit(2); + } + + height = cairo_image_surface_get_height(surface); + width = cairo_image_surface_get_width(surface); + stride = cairo_image_surface_get_stride(surface); + cairo_surface_flush(surface); + data = cairo_image_surface_get_data(surface); + + if (!writer->init(file, width, height, x_resolution, y_resolution)) { + fprintf(stderr, "Error writing %s\n", filename->c_str()); + exit(2); + } + unsigned char *row = (unsigned char *)gmallocn(width, 4); + + for (int y = 0; y < height; y++) { + uint32_t *pixel = reinterpret_cast<uint32_t *>((data + y * stride)); + unsigned char *rowp = row; + int bit = 7; + for (int x = 0; x < width; x++, pixel++) { + if (transp) { + if (tiff) { + // RGBA premultipled format + *rowp++ = (*pixel & 0xff0000) >> 16; + *rowp++ = (*pixel & 0x00ff00) >> 8; + *rowp++ = (*pixel & 0x0000ff) >> 0; + *rowp++ = (*pixel & 0xff000000) >> 24; + } else { + // unpremultiply into RGBA format + uint8_t a; + a = (*pixel & 0xff000000) >> 24; + if (a == 0) { + *rowp++ = 0; + *rowp++ = 0; + *rowp++ = 0; + } else { + *rowp++ = (((*pixel & 0xff0000) >> 16) * 255 + a / 2) / a; + *rowp++ = (((*pixel & 0x00ff00) >> 8) * 255 + a / 2) / a; + *rowp++ = (((*pixel & 0x0000ff) >> 0) * 255 + a / 2) / a; + } + *rowp++ = a; + } + } else if (gray || mono) { + // convert to gray + // The PDF Reference specifies the DeviceRGB to DeviceGray conversion as + // gray = 0.3*red + 0.59*green + 0.11*blue + const int r = (*pixel & 0x00ff0000) >> 16; + const int g = (*pixel & 0x0000ff00) >> 8; + const int b = (*pixel & 0x000000ff) >> 0; + // an arbitrary integer approximation of .3*r + .59*g + .11*b + const int grayValue = (r * 19661 + g * 38666 + b * 7209 + 32829) >> 16; + if (mono) { + if (bit == 7) + *rowp = 0; + if (grayValue > 127) + *rowp |= (1 << bit); + bit--; + if (bit < 0) { + bit = 7; + rowp++; + } + } else { + *rowp++ = grayValue; + } + } else { + // copy into RGB format + *rowp++ = (*pixel & 0x00ff0000) >> 16; + *rowp++ = (*pixel & 0x0000ff00) >> 8; + *rowp++ = (*pixel & 0x000000ff) >> 0; + } } - } else { - // copy into RGB format - *rowp++ = (*pixel & 0x00ff0000) >> 16; - *rowp++ = (*pixel & 0x0000ff00) >> 8; - *rowp++ = (*pixel & 0x000000ff) >> 0; - } + writer->writeRow(&row); } - writer->writeRow(&row); - } - gfree(row); - writer->close(); - delete writer; - if (file == stdout) fflush(file); - else fclose(file); + gfree(row); + writer->close(); + delete writer; + if (file == stdout) + fflush(file); + else + fclose(file); } static void getCropSize(double page_w, double page_h, double *width, double *height) { - int w = crop_w; - int h = crop_h; + int w = crop_w; + int h = crop_h; - if (w == 0) - w = (int)ceil(page_w); + if (w == 0) + w = (int)ceil(page_w); - if (h == 0) - h = (int)ceil(page_h); + if (h == 0) + h = (int)ceil(page_h); - *width = (crop_x + w > page_w ? (int)ceil(page_w - crop_x) : w); - *height = (crop_y + h > page_h ? (int)ceil(page_h - crop_y) : h); + *width = (crop_x + w > page_w ? (int)ceil(page_w - crop_x) : w); + *height = (crop_y + h > page_h ? (int)ceil(page_h - crop_y) : h); } static void getOutputSize(double page_w, double page_h, double *width, double *height) { - if (printing) { - if (usePDFPageSize) { - *width = page_w; - *height = page_h; + if (printing) { + if (usePDFPageSize) { + *width = page_w; + *height = page_h; + } else { + if (page_w > page_h) { + *width = paperHeight; + *height = paperWidth; + } else { + *width = paperWidth; + *height = paperHeight; + } + } } else { - if (page_w > page_h) { - *width = paperHeight; - *height = paperWidth; - } else { - *width = paperWidth; - *height = paperHeight; - } + getCropSize(page_w * (x_resolution / 72.0), page_h * (y_resolution / 72.0), width, height); } - } else { - getCropSize(page_w * (x_resolution / 72.0), - page_h * (y_resolution / 72.0), - width, height); - } } -static void getFitToPageTransform(double page_w, double page_h, - double paper_w, double paper_h, - cairo_matrix_t *m) +static void getFitToPageTransform(double page_w, double page_h, double paper_w, double paper_h, cairo_matrix_t *m) { - double x_scale, y_scale, scale; - - x_scale = paper_w / page_w; - y_scale = paper_h / page_h; - if (x_scale < y_scale) - scale = x_scale; - else - scale = y_scale; - - if (scale > 1.0 && !expand) - scale = 1.0; - if (scale < 1.0 && noShrink) - scale = 1.0; - - cairo_matrix_init_identity (m); - if (!noCenter) { - // centre page - cairo_matrix_translate (m, (paper_w - page_w*scale)/2, (paper_h - page_h*scale)/2); - } else if (!svg) { - // move to PostScript origin - cairo_matrix_translate (m, 0, (paper_h - page_h*scale)); - } - cairo_matrix_scale (m, scale, scale); + double x_scale, y_scale, scale; + + x_scale = paper_w / page_w; + y_scale = paper_h / page_h; + if (x_scale < y_scale) + scale = x_scale; + else + scale = y_scale; + + if (scale > 1.0 && !expand) + scale = 1.0; + if (scale < 1.0 && noShrink) + scale = 1.0; + + cairo_matrix_init_identity(m); + if (!noCenter) { + // centre page + cairo_matrix_translate(m, (paper_w - page_w * scale) / 2, (paper_h - page_h * scale) / 2); + } else if (!svg) { + // move to PostScript origin + cairo_matrix_translate(m, 0, (paper_h - page_h * scale)); + } + cairo_matrix_scale(m, scale, scale); } static cairo_status_t writeStream(void *closure, const unsigned char *data, unsigned int length) { - FILE *file = (FILE *)closure; + FILE *file = (FILE *)closure; - if (fwrite(data, length, 1, file) == 1) - return CAIRO_STATUS_SUCCESS; - else - return CAIRO_STATUS_WRITE_ERROR; + if (fwrite(data, length, 1, file) == 1) + return CAIRO_STATUS_SUCCESS; + else + return CAIRO_STATUS_WRITE_ERROR; } static void beginDocument(GooString *inputFileName, GooString *outputFileName, double w, double h) { - if (printing) { - if (printToWin32) { - output_file = nullptr; - } else { - if (outputFileName->cmp("fd://0") == 0) - output_file = stdout; - else - { - output_file = fopen(outputFileName->c_str(), "wb"); - if (!output_file) { - fprintf(stderr, "Error opening output file %s\n", outputFileName->c_str()); - exit(2); + if (printing) { + if (printToWin32) { + output_file = nullptr; + } else { + if (outputFileName->cmp("fd://0") == 0) + output_file = stdout; + else { + output_file = fopen(outputFileName->c_str(), "wb"); + if (!output_file) { + fprintf(stderr, "Error opening output file %s\n", outputFileName->c_str()); + exit(2); + } + } } - } - } - if (ps || eps) { + if (ps || eps) { #ifdef CAIRO_HAS_PS_SURFACE - surface = cairo_ps_surface_create_for_stream(writeStream, output_file, w, h); - if (level2) - cairo_ps_surface_restrict_to_level (surface, CAIRO_PS_LEVEL_2); - if (eps) - cairo_ps_surface_set_eps (surface, 1); - if (duplex) { - cairo_ps_surface_dsc_comment(surface, "%%Requirements: duplex"); - cairo_ps_surface_dsc_begin_setup(surface); - cairo_ps_surface_dsc_comment(surface, "%%IncludeFeature: *Duplex DuplexNoTumble"); - } - cairo_ps_surface_dsc_begin_page_setup (surface); + surface = cairo_ps_surface_create_for_stream(writeStream, output_file, w, h); + if (level2) + cairo_ps_surface_restrict_to_level(surface, CAIRO_PS_LEVEL_2); + if (eps) + cairo_ps_surface_set_eps(surface, 1); + if (duplex) { + cairo_ps_surface_dsc_comment(surface, "%%Requirements: duplex"); + cairo_ps_surface_dsc_begin_setup(surface); + cairo_ps_surface_dsc_comment(surface, "%%IncludeFeature: *Duplex DuplexNoTumble"); + } + cairo_ps_surface_dsc_begin_page_setup(surface); #endif - } else if (pdf) { + } else if (pdf) { #ifdef CAIRO_HAS_PDF_SURFACE - surface = cairo_pdf_surface_create_for_stream(writeStream, output_file, w, h); + surface = cairo_pdf_surface_create_for_stream(writeStream, output_file, w, h); #endif - } else if (svg) { + } else if (svg) { #ifdef CAIRO_HAS_SVG_SURFACE - surface = cairo_svg_surface_create_for_stream(writeStream, output_file, w, h); - cairo_svg_surface_restrict_to_version (surface, CAIRO_SVG_VERSION_1_2); + surface = cairo_svg_surface_create_for_stream(writeStream, output_file, w, h); + cairo_svg_surface_restrict_to_version(surface, CAIRO_SVG_VERSION_1_2); #endif - } + } #ifdef CAIRO_HAS_WIN32_SURFACE - if (printToWin32) - surface = win32BeginDocument(inputFileName, outputFileName); + if (printToWin32) + surface = win32BeginDocument(inputFileName, outputFileName); #endif - } + } } static void beginPage(double *w, double *h) { - if (printing) { - if (ps || eps) { + if (printing) { + if (ps || eps) { #ifdef CAIRO_HAS_PS_SURFACE - if (*w > *h) { - cairo_ps_surface_dsc_comment (surface, "%%PageOrientation: Landscape"); - cairo_ps_surface_set_size (surface, *h, *w); - } else { - cairo_ps_surface_dsc_comment (surface, "%%PageOrientation: Portrait"); - cairo_ps_surface_set_size (surface, *w, *h); - } + if (*w > *h) { + cairo_ps_surface_dsc_comment(surface, "%%PageOrientation: Landscape"); + cairo_ps_surface_set_size(surface, *h, *w); + } else { + cairo_ps_surface_dsc_comment(surface, "%%PageOrientation: Portrait"); + cairo_ps_surface_set_size(surface, *w, *h); + } #endif - } + } #ifdef CAIRO_HAS_PDF_SURFACE - if (pdf) - cairo_pdf_surface_set_size (surface, *w, *h); + if (pdf) + cairo_pdf_surface_set_size(surface, *w, *h); #endif #ifdef CAIRO_HAS_WIN32_SURFACE - if (printToWin32) { - bool changePageSize = true; - if (setupdlg && !origPageSizes) - changePageSize = false; - win32BeginPage(w, h, changePageSize, noShrink); // w,h will be changed to actual size used - } + if (printToWin32) { + bool changePageSize = true; + if (setupdlg && !origPageSizes) + changePageSize = false; + win32BeginPage(w, h, changePageSize, noShrink); // w,h will be changed to actual size used + } #endif - cairo_surface_set_fallback_resolution (surface, x_resolution, y_resolution); + cairo_surface_set_fallback_resolution(surface, x_resolution, y_resolution); - } else { - surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, ceil(*w), ceil(*h)); - } + } else { + surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, ceil(*w), ceil(*h)); + } } -static void renderPage(PDFDoc *doc, CairoOutputDev *cairoOut, int pg, - double page_w, double page_h, - double output_w, double output_h) +static void renderPage(PDFDoc *doc, CairoOutputDev *cairoOut, int pg, double page_w, double page_h, double output_w, double output_h) { - cairo_t *cr; - cairo_status_t status; - cairo_matrix_t m; - - cr = cairo_create(surface); - - cairoOut->setCairo(cr); - cairoOut->setPrinting(printing); - cairoOut->setAntialias(antialiasEnum); - - cairo_save(cr); - if (ps && output_w > output_h) { - // rotate 90 deg for landscape - cairo_translate (cr, 0, output_w); - cairo_matrix_init (&m, 0, -1, 1, 0, 0, 0); - cairo_transform (cr, &m); - } - cairo_translate (cr, -crop_x, -crop_y); - if (printing) { - double cropped_w, cropped_h; - getCropSize(page_w, page_h, &cropped_w, &cropped_h); - getFitToPageTransform(cropped_w, cropped_h, output_w, output_h, &m); - cairo_transform (cr, &m); - cairo_rectangle(cr, crop_x, crop_y, cropped_w, cropped_h); - cairo_clip(cr); - } else { - cairo_scale (cr, x_resolution/72.0, y_resolution/72.0); - } - doc->displayPageSlice(cairoOut, - pg, - 72.0, 72.0, - 0, /* rotate */ - !useCropBox, /* useMediaBox */ - false, /* Crop */ - printing, - -1, -1, -1, -1); - cairo_restore(cr); - cairoOut->setCairo(nullptr); - - // Blend onto white page - if (!printing && !transp) { + cairo_t *cr; + cairo_status_t status; + cairo_matrix_t m; + + cr = cairo_create(surface); + + cairoOut->setCairo(cr); + cairoOut->setPrinting(printing); + cairoOut->setAntialias(antialiasEnum); + cairo_save(cr); - cairo_set_operator(cr, CAIRO_OPERATOR_DEST_OVER); - cairo_set_source_rgb(cr, 1, 1, 1); - cairo_paint(cr); + if (ps && output_w > output_h) { + // rotate 90 deg for landscape + cairo_translate(cr, 0, output_w); + cairo_matrix_init(&m, 0, -1, 1, 0, 0, 0); + cairo_transform(cr, &m); + } + cairo_translate(cr, -crop_x, -crop_y); + if (printing) { + double cropped_w, cropped_h; + getCropSize(page_w, page_h, &cropped_w, &cropped_h); + getFitToPageTransform(cropped_w, cropped_h, output_w, output_h, &m); + cairo_transform(cr, &m); + cairo_rectangle(cr, crop_x, crop_y, cropped_w, cropped_h); + cairo_clip(cr); + } else { + cairo_scale(cr, x_resolution / 72.0, y_resolution / 72.0); + } + doc->displayPageSlice(cairoOut, pg, 72.0, 72.0, 0, /* rotate */ + !useCropBox, /* useMediaBox */ + false, /* Crop */ + printing, -1, -1, -1, -1); cairo_restore(cr); - } + cairoOut->setCairo(nullptr); + + // Blend onto white page + if (!printing && !transp) { + cairo_save(cr); + cairo_set_operator(cr, CAIRO_OPERATOR_DEST_OVER); + cairo_set_source_rgb(cr, 1, 1, 1); + cairo_paint(cr); + cairo_restore(cr); + } - status = cairo_status(cr); - if (status) - fprintf(stderr, "cairo error: %s\n", cairo_status_to_string(status)); - cairo_destroy (cr); + status = cairo_status(cr); + if (status) + fprintf(stderr, "cairo error: %s\n", cairo_status_to_string(status)); + cairo_destroy(cr); } static void endPage(GooString *imageFileName) { - cairo_status_t status; + cairo_status_t status; - if (printing) { - cairo_surface_show_page(surface); + if (printing) { + cairo_surface_show_page(surface); #ifdef CAIRO_HAS_WIN32_SURFACE - if (printToWin32) - win32EndPage(imageFileName); + if (printToWin32) + win32EndPage(imageFileName); #endif - } else { - writePageImage(imageFileName); - cairo_surface_finish(surface); - status = cairo_surface_status(surface); - if (status) - fprintf(stderr, "cairo error: %s\n", cairo_status_to_string(status)); - cairo_surface_destroy(surface); - } - + } else { + writePageImage(imageFileName); + cairo_surface_finish(surface); + status = cairo_surface_status(surface); + if (status) + fprintf(stderr, "cairo error: %s\n", cairo_status_to_string(status)); + cairo_surface_destroy(surface); + } } static void endDocument() { - cairo_status_t status; - - if (printing) { - cairo_surface_finish(surface); - status = cairo_surface_status(surface); - if (status) - fprintf(stderr, "cairo error: %s\n", cairo_status_to_string(status)); - cairo_surface_destroy(surface); + cairo_status_t status; + + if (printing) { + cairo_surface_finish(surface); + status = cairo_surface_status(surface); + if (status) + fprintf(stderr, "cairo error: %s\n", cairo_status_to_string(status)); + cairo_surface_destroy(surface); #ifdef CAIRO_HAS_WIN32_SURFACE - if (printToWin32) - win32EndDocument(); + if (printToWin32) + win32EndDocument(); #endif - if (output_file) - fclose(output_file); - } + if (output_file) + fclose(output_file); + } } -static bool setPSPaperSize(char *size, int &psPaperWidth, int &psPaperHeight) { - if (!strcmp(size, "match")) { - psPaperWidth = psPaperHeight = -1; - } else if (!strcmp(size, "letter")) { - psPaperWidth = 612; - psPaperHeight = 792; - } else if (!strcmp(size, "legal")) { - psPaperWidth = 612; - psPaperHeight = 1008; - } else if (!strcmp(size, "A4")) { - psPaperWidth = 595; - psPaperHeight = 842; - } else if (!strcmp(size, "A3")) { - psPaperWidth = 842; - psPaperHeight = 1190; - } else { - return false; - } - return true; +static bool setPSPaperSize(char *size, int &psPaperWidth, int &psPaperHeight) +{ + if (!strcmp(size, "match")) { + psPaperWidth = psPaperHeight = -1; + } else if (!strcmp(size, "letter")) { + psPaperWidth = 612; + psPaperHeight = 792; + } else if (!strcmp(size, "legal")) { + psPaperWidth = 612; + psPaperHeight = 1008; + } else if (!strcmp(size, "A4")) { + psPaperWidth = 595; + psPaperHeight = 842; + } else if (!strcmp(size, "A3")) { + psPaperWidth = 842; + psPaperHeight = 1190; + } else { + return false; + } + return true; } static GooString *getImageFileName(GooString *outputFileName, int numDigits, int page) { - char buf[10]; - GooString *imageName = new GooString(outputFileName); - if (!singleFile) { - snprintf(buf, sizeof(buf), "-%0*d", numDigits, page); - imageName->append(buf); - } - if (outputFileName->cmp("fd://0") != 0) { - if (png) - imageName->append(".png"); - else if (jpeg) - imageName->append(".jpg"); - else if (tiff) - imageName->append(".tif"); - } - - return imageName; + char buf[10]; + GooString *imageName = new GooString(outputFileName); + if (!singleFile) { + snprintf(buf, sizeof(buf), "-%0*d", numDigits, page); + imageName->append(buf); + } + if (outputFileName->cmp("fd://0") != 0) { + if (png) + imageName->append(".png"); + else if (jpeg) + imageName->append(".jpg"); + else if (tiff) + imageName->append(".tif"); + } + + return imageName; } // If (printing || singleFile) the output file name includes the // extension. Otherwise it is the file name base. static GooString *getOutputFileName(GooString *fileName, GooString *outputName) { - GooString *name; - - if (outputName) { - if (outputName->cmp("-") == 0) { - if (printToWin32 || (!printing && !singleFile)) { - fprintf(stderr, "Error: stdout may only be used with the ps, eps, pdf, svg output options or if -singlefile is used.\n"); - exit(99); - } - return new GooString("fd://0"); + GooString *name; + + if (outputName) { + if (outputName->cmp("-") == 0) { + if (printToWin32 || (!printing && !singleFile)) { + fprintf(stderr, "Error: stdout may only be used with the ps, eps, pdf, svg output options or if -singlefile is used.\n"); + exit(99); + } + return new GooString("fd://0"); + } + return new GooString(outputName); } - return new GooString(outputName); - } - - if (printToWin32) - return nullptr; // No output file means print to printer - - if (fileName->cmp("fd://0") == 0) { - fprintf(stderr, "Error: an output filename or '-' must be supplied when the PDF file is stdin.\n"); - exit(99); - } - - // be careful not to overwrite the input file when the output format is PDF - if (pdf && fileName->cmpN("http://", 7) != 0 && fileName->cmpN("https://", 8) != 0) { - fprintf(stderr, "Error: an output filename or '-' must be supplied when the output format is PDF and input PDF file is a local file.\n"); - exit(99); - } - - // strip everything up to last '/' - const char *s = fileName->c_str(); - const char *p = strrchr(s, '/'); - if (p) { - p++; - if (*p == 0) { - fprintf(stderr, "Error: invalid output filename.\n"); - exit(99); + + if (printToWin32) + return nullptr; // No output file means print to printer + + if (fileName->cmp("fd://0") == 0) { + fprintf(stderr, "Error: an output filename or '-' must be supplied when the PDF file is stdin.\n"); + exit(99); + } + + // be careful not to overwrite the input file when the output format is PDF + if (pdf && fileName->cmpN("http://", 7) != 0 && fileName->cmpN("https://", 8) != 0) { + fprintf(stderr, "Error: an output filename or '-' must be supplied when the output format is PDF and input PDF file is a local file.\n"); + exit(99); } - name = new GooString(p); - } else { - name = new GooString(s); - } - - // remove .pdf extension - p = strrchr(name->c_str(), '.'); - if (p && strcasecmp(p, ".pdf") == 0) { - GooString *name2 = new GooString(name->c_str(), name->getLength() - 4); - delete name; - name = name2; - } - - // append new extension - if (ps) - name->append(".ps"); - else if (eps) - name->append(".eps"); - else if (pdf) - name->append(".pdf"); - else if (svg) - name->append(".svg"); - - return name; + + // strip everything up to last '/' + const char *s = fileName->c_str(); + const char *p = strrchr(s, '/'); + if (p) { + p++; + if (*p == 0) { + fprintf(stderr, "Error: invalid output filename.\n"); + exit(99); + } + name = new GooString(p); + } else { + name = new GooString(s); + } + + // remove .pdf extension + p = strrchr(name->c_str(), '.'); + if (p && strcasecmp(p, ".pdf") == 0) { + GooString *name2 = new GooString(name->c_str(), name->getLength() - 4); + delete name; + name = name2; + } + + // append new extension + if (ps) + name->append(".ps"); + else if (eps) + name->append(".eps"); + else if (pdf) + name->append(".pdf"); + else if (svg) + name->append(".svg"); + + return name; } static void checkInvalidPrintOption(bool option, const char *option_name) { - if (option) { - fprintf(stderr, "Error: %s may only be used with the -png, -jpeg, or -tiff output options.\n", option_name); - exit(99); - } + if (option) { + fprintf(stderr, "Error: %s may only be used with the -png, -jpeg, or -tiff output options.\n", option_name); + exit(99); + } } static void checkInvalidImageOption(bool option, const char *option_name) { - if (option) { - fprintf(stderr, "Error: %s may only be used with the -ps, -eps, -pdf, or -svg output options.\n", option_name); - exit(99); - } + if (option) { + fprintf(stderr, "Error: %s may only be used with the -ps, -eps, -pdf, or -svg output options.\n", option_name); + exit(99); + } } -int main(int argc, char *argv[]) { - PDFDoc *doc; - GooString *fileName = nullptr; - GooString *outputName = nullptr; - GooString *outputFileName = nullptr; - GooString *imageFileName = nullptr; - GooString *ownerPW, *userPW; - CairoOutputDev *cairoOut; - int pg, pg_num_len; - double pg_w, pg_h, tmp, output_w, output_h; - int num_outputs; - - // parse args - Win32Console win32Console(&argc, &argv); - if (!parseArgs(argDesc, &argc, argv)) { - printUsage("pdftocairo", nullptr, argDesc); - exit(99); - } - - if ( resolution != 0.0 && - (x_resolution == 150.0 || - y_resolution == 150.0)) { - x_resolution = resolution; - y_resolution = resolution; - } - if (argc < 2 || argc > 3 || printVersion || printHelp) { - fprintf(stderr, "pdftocairo version %s\n", PACKAGE_VERSION); - fprintf(stderr, "%s\n", popplerCopyright); - fprintf(stderr, "%s\n", xpdfCopyright); - if (!printVersion) { - printUsage("pdftocairo", "<PDF-file> [<output-file>]", argDesc); +int main(int argc, char *argv[]) +{ + PDFDoc *doc; + GooString *fileName = nullptr; + GooString *outputName = nullptr; + GooString *outputFileName = nullptr; + GooString *imageFileName = nullptr; + GooString *ownerPW, *userPW; + CairoOutputDev *cairoOut; + int pg, pg_num_len; + double pg_w, pg_h, tmp, output_w, output_h; + int num_outputs; + + // parse args + Win32Console win32Console(&argc, &argv); + if (!parseArgs(argDesc, &argc, argv)) { + printUsage("pdftocairo", nullptr, argDesc); + exit(99); + } + + if (resolution != 0.0 && (x_resolution == 150.0 || y_resolution == 150.0)) { + x_resolution = resolution; + y_resolution = resolution; + } + if (argc < 2 || argc > 3 || printVersion || printHelp) { + fprintf(stderr, "pdftocairo version %s\n", PACKAGE_VERSION); + fprintf(stderr, "%s\n", popplerCopyright); + fprintf(stderr, "%s\n", xpdfCopyright); + if (!printVersion) { + printUsage("pdftocairo", "<PDF-file> [<output-file>]", argDesc); + } + if (printVersion || printHelp) + exit(0); + else + exit(99); } - if (printVersion || printHelp) - exit(0); + + num_outputs = (png ? 1 : 0) + (jpeg ? 1 : 0) + (tiff ? 1 : 0) + (ps ? 1 : 0) + (eps ? 1 : 0) + (pdf ? 1 : 0) + (printToWin32 ? 1 : 0) + (printdlg ? 1 : 0) + (svg ? 1 : 0); + if (num_outputs == 0) { + fprintf(stderr, "Error: one of the output format options (-png, -jpeg, -ps, -eps, -pdf, -print, -printdlg, -svg) must be used.\n"); + exit(99); + } + if (num_outputs > 1) { + fprintf(stderr, "Error: use only one of the output format options (-png, -jpeg, -ps, -eps, -pdf, -printdlg, -print, -svg).\n"); + exit(99); + } + if (png || jpeg || tiff) + printing = false; else - exit(99); - } - - num_outputs = (png ? 1 : 0) + - (jpeg ? 1 : 0) + - (tiff ? 1 : 0) + - (ps ? 1 : 0) + - (eps ? 1 : 0) + - (pdf ? 1 : 0) + - (printToWin32 ? 1 : 0) + - (printdlg ? 1 : 0) + - (svg ? 1 : 0); - if (num_outputs == 0) { - fprintf(stderr, "Error: one of the output format options (-png, -jpeg, -ps, -eps, -pdf, -print, -printdlg, -svg) must be used.\n"); - exit(99); - } - if (num_outputs > 1) { - fprintf(stderr, "Error: use only one of the output format options (-png, -jpeg, -ps, -eps, -pdf, -printdlg, -print, -svg).\n"); - exit(99); - } - if (png || jpeg || tiff) - printing = false; - else - printing = true; - - if (printing) { - checkInvalidPrintOption(mono, "-mono"); - checkInvalidPrintOption(gray, "-gray"); - checkInvalidPrintOption(transp, "-transp"); - checkInvalidPrintOption(icc.c_str()[0], "-icc"); - checkInvalidPrintOption(singleFile, "-singlefile"); - checkInvalidPrintOption(useCropBox, "-cropbox"); - checkInvalidPrintOption(scaleTo != 0, "-scale-to"); - checkInvalidPrintOption(x_scaleTo != 0, "-scale-to-x"); - checkInvalidPrintOption(y_scaleTo != 0, "-scale-to-y"); - } else { - checkInvalidImageOption(level2, "-level2"); - checkInvalidImageOption(level3, "-level3"); - checkInvalidImageOption(origPageSizes, "-origpagesizes"); - checkInvalidImageOption(paperSize[0], "-paper"); - checkInvalidImageOption(paperWidth > 0, "-paperw"); - checkInvalidImageOption(paperHeight > 0, "-paperh"); - checkInvalidImageOption(noCrop, "-nocrop"); - checkInvalidImageOption(expand, "-expand"); - checkInvalidImageOption(noShrink, "-noshrink"); - checkInvalidImageOption(noCenter, "-nocenter"); - checkInvalidImageOption(duplex, "-duplex"); - } - - if (printing) - useCropBox = !noCrop; - - if (icc.c_str()[0] && !png) { - fprintf(stderr, "Error: -icc may only be used with png output.\n"); - exit(99); - } - - if (antialias.getLength() > 0) { - if (!parseAntialiasOption()) - exit(99); - } - - if (transp && !(png || tiff)) { - fprintf(stderr, "Error: -transp may only be used with png or tiff output.\n"); - exit(99); - } - - if (mono && gray) { - fprintf(stderr, "Error: -mono and -gray may not be used together.\n"); - exit(99); - } - - if (mono && !(png || tiff)) { - fprintf(stderr, "Error: -mono may only be used with png or tiff output.\n"); - exit(99); - } - - if (jpegOpt.getLength() > 0) { - if (!jpeg) { - fprintf(stderr, "Error: -jpegopt may only be used with jpeg output.\n"); - exit(99); + printing = true; + + if (printing) { + checkInvalidPrintOption(mono, "-mono"); + checkInvalidPrintOption(gray, "-gray"); + checkInvalidPrintOption(transp, "-transp"); + checkInvalidPrintOption(icc.c_str()[0], "-icc"); + checkInvalidPrintOption(singleFile, "-singlefile"); + checkInvalidPrintOption(useCropBox, "-cropbox"); + checkInvalidPrintOption(scaleTo != 0, "-scale-to"); + checkInvalidPrintOption(x_scaleTo != 0, "-scale-to-x"); + checkInvalidPrintOption(y_scaleTo != 0, "-scale-to-y"); + } else { + checkInvalidImageOption(level2, "-level2"); + checkInvalidImageOption(level3, "-level3"); + checkInvalidImageOption(origPageSizes, "-origpagesizes"); + checkInvalidImageOption(paperSize[0], "-paper"); + checkInvalidImageOption(paperWidth > 0, "-paperw"); + checkInvalidImageOption(paperHeight > 0, "-paperh"); + checkInvalidImageOption(noCrop, "-nocrop"); + checkInvalidImageOption(expand, "-expand"); + checkInvalidImageOption(noShrink, "-noshrink"); + checkInvalidImageOption(noCenter, "-nocenter"); + checkInvalidImageOption(duplex, "-duplex"); } - if (!parseJpegOptions()) - exit(99); - } - - if (strlen(tiffCompressionStr) > 0 && !tiff) { - fprintf(stderr, "Error: -tiffcompression may only be used with tiff output.\n"); - exit(99); - } - - if (level2 && level3) { - fprintf(stderr, "Error: use only one of the 'level' options.\n"); - exit(99); - } - if (!level2 && !level3) - level3 = true; - - if (eps && (origPageSizes || paperSize[0] || paperWidth > 0 || paperHeight > 0)) { - fprintf(stderr, "Error: page size options may not be used with eps output.\n"); - exit(99); - } - - if ((paperWidth > 0 && paperHeight <= 0) || (paperWidth <= 0 && paperHeight > 0)) { - fprintf(stderr, "Error: both -paperw and -paperh must be specified.\n"); - exit(99); - } - - if (paperSize[0]) { - if (origPageSizes) { - fprintf(stderr, "Error: -origpagesizes and -paper may not be used together.\n"); - exit(99); + + if (printing) + useCropBox = !noCrop; + + if (icc.c_str()[0] && !png) { + fprintf(stderr, "Error: -icc may only be used with png output.\n"); + exit(99); } - if (!setPSPaperSize(paperSize, paperWidth, paperHeight)) { - fprintf(stderr, "Invalid paper size\n"); - exit(99); + + if (antialias.getLength() > 0) { + if (!parseAntialiasOption()) + exit(99); } - } - if (origPageSizes || paperWidth < 0 || paperHeight < 0) - usePDFPageSize = true; - else - usePDFPageSize = false; - - if (printdlg) - printToWin32 = true; - - globalParams = std::make_unique<GlobalParams>(); - if (quiet) { - globalParams->setErrQuiet(quiet); - } - - // open PDF file - if (ownerPassword[0]) { - ownerPW = new GooString(ownerPassword); - } else { - ownerPW = nullptr; - } - if (userPassword[0]) { - userPW = new GooString(userPassword); - } else { - userPW = nullptr; - } - - fileName = new GooString(argv[1]); - if (fileName->cmp("-") == 0) { - delete fileName; - fileName = new GooString("fd://0"); - } - if (argc == 3) - outputName = new GooString(argv[2]); - else - outputName = nullptr; - - outputFileName = getOutputFileName(fileName, outputName); -#ifdef USE_CMS - icc_data = nullptr; - if (icc.c_str()[0]) { - FILE *file = fopen(icc.c_str(), "rb"); - if (!file) { - fprintf(stderr, "Error: unable to open icc profile %s\n", icc.c_str()); - exit(4); + if (transp && !(png || tiff)) { + fprintf(stderr, "Error: -transp may only be used with png or tiff output.\n"); + exit(99); + } + + if (mono && gray) { + fprintf(stderr, "Error: -mono and -gray may not be used together.\n"); + exit(99); + } + + if (mono && !(png || tiff)) { + fprintf(stderr, "Error: -mono may only be used with png or tiff output.\n"); + exit(99); + } + + if (jpegOpt.getLength() > 0) { + if (!jpeg) { + fprintf(stderr, "Error: -jpegopt may only be used with jpeg output.\n"); + exit(99); + } + if (!parseJpegOptions()) + exit(99); + } + + if (strlen(tiffCompressionStr) > 0 && !tiff) { + fprintf(stderr, "Error: -tiffcompression may only be used with tiff output.\n"); + exit(99); + } + + if (level2 && level3) { + fprintf(stderr, "Error: use only one of the 'level' options.\n"); + exit(99); + } + if (!level2 && !level3) + level3 = true; + + if (eps && (origPageSizes || paperSize[0] || paperWidth > 0 || paperHeight > 0)) { + fprintf(stderr, "Error: page size options may not be used with eps output.\n"); + exit(99); + } + + if ((paperWidth > 0 && paperHeight <= 0) || (paperWidth <= 0 && paperHeight > 0)) { + fprintf(stderr, "Error: both -paperw and -paperh must be specified.\n"); + exit(99); + } + + if (paperSize[0]) { + if (origPageSizes) { + fprintf(stderr, "Error: -origpagesizes and -paper may not be used together.\n"); + exit(99); + } + if (!setPSPaperSize(paperSize, paperWidth, paperHeight)) { + fprintf(stderr, "Invalid paper size\n"); + exit(99); + } + } + if (origPageSizes || paperWidth < 0 || paperHeight < 0) + usePDFPageSize = true; + else + usePDFPageSize = false; + + if (printdlg) + printToWin32 = true; + + globalParams = std::make_unique<GlobalParams>(); + if (quiet) { + globalParams->setErrQuiet(quiet); + } + + // open PDF file + if (ownerPassword[0]) { + ownerPW = new GooString(ownerPassword); + } else { + ownerPW = nullptr; + } + if (userPassword[0]) { + userPW = new GooString(userPassword); + } else { + userPW = nullptr; } - fseek (file, 0, SEEK_END); - icc_data_size = ftell(file); - fseek (file, 0, SEEK_SET); - icc_data = (unsigned char*)gmalloc(icc_data_size); - if (fread(icc_data, icc_data_size, 1, file) != 1) { - fprintf(stderr, "Error: unable to read icc profile %s\n", icc.c_str()); - exit(4); + + fileName = new GooString(argv[1]); + if (fileName->cmp("-") == 0) { + delete fileName; + fileName = new GooString("fd://0"); } - fclose(file); - profile = make_GfxLCMSProfilePtr(cmsOpenProfileFromMem(icc_data, icc_data_size)); - if (!profile) { - fprintf(stderr, "Error: lcms error opening profile\n"); - exit(4); + if (argc == 3) + outputName = new GooString(argv[2]); + else + outputName = nullptr; + + outputFileName = getOutputFileName(fileName, outputName); + +#ifdef USE_CMS + icc_data = nullptr; + if (icc.c_str()[0]) { + FILE *file = fopen(icc.c_str(), "rb"); + if (!file) { + fprintf(stderr, "Error: unable to open icc profile %s\n", icc.c_str()); + exit(4); + } + fseek(file, 0, SEEK_END); + icc_data_size = ftell(file); + fseek(file, 0, SEEK_SET); + icc_data = (unsigned char *)gmalloc(icc_data_size); + if (fread(icc_data, icc_data_size, 1, file) != 1) { + fprintf(stderr, "Error: unable to read icc profile %s\n", icc.c_str()); + exit(4); + } + fclose(file); + profile = make_GfxLCMSProfilePtr(cmsOpenProfileFromMem(icc_data, icc_data_size)); + if (!profile) { + fprintf(stderr, "Error: lcms error opening profile\n"); + exit(4); + } + } else { + profile = make_GfxLCMSProfilePtr(cmsCreate_sRGBProfile()); } - } else { - profile = make_GfxLCMSProfilePtr(cmsCreate_sRGBProfile()); - } #endif - doc = PDFDocFactory().createPDFDoc(*fileName, ownerPW, userPW); - if (!doc->isOk()) { - fprintf(stderr, "Error opening PDF file.\n"); - exit(1); - } + doc = PDFDocFactory().createPDFDoc(*fileName, ownerPW, userPW); + if (!doc->isOk()) { + fprintf(stderr, "Error opening PDF file.\n"); + exit(1); + } #ifdef ENFORCE_PERMISSIONS - // check for print permission - if (printing && !doc->okToPrint()) { - fprintf(stderr, "Printing this document is not allowed.\n"); - exit(3); - } + // check for print permission + if (printing && !doc->okToPrint()) { + fprintf(stderr, "Printing this document is not allowed.\n"); + exit(3); + } #endif - // get page range - if (firstPage < 1) - firstPage = 1; - if (singleFile && lastPage < 1) - lastPage = firstPage; - if (lastPage < 1 || lastPage > doc->getNumPages()) - lastPage = doc->getNumPages(); - - if (lastPage < firstPage) { - fprintf(stderr, - "Wrong page range given: the first page (%d) can not be after the last page (%d).\n", - firstPage, lastPage); - exit(99); - } - if (eps && firstPage != lastPage) { - fprintf(stderr, "EPS files can only contain one page.\n"); - exit(99); - } - - // If our page range selection and document size indicate we're only - // outputting a single page, ensure that even/odd page selection doesn't - // filter out that single page. - if (firstPage == lastPage && - ((printOnlyEven && firstPage % 2 == 1) || - (printOnlyOdd && firstPage % 2 == 0))) { - fprintf(stderr, "Invalid even/odd page selection, no pages match criteria.\n"); - exit(99); - } - - if (singleFile && firstPage < lastPage) { - if (!quiet) { - fprintf(stderr, - "Warning: Single file will write only the first of the %d pages.\n", - lastPage + 1 - firstPage); + // get page range + if (firstPage < 1) + firstPage = 1; + if (singleFile && lastPage < 1) + lastPage = firstPage; + if (lastPage < 1 || lastPage > doc->getNumPages()) + lastPage = doc->getNumPages(); + + if (lastPage < firstPage) { + fprintf(stderr, "Wrong page range given: the first page (%d) can not be after the last page (%d).\n", firstPage, lastPage); + exit(99); + } + if (eps && firstPage != lastPage) { + fprintf(stderr, "EPS files can only contain one page.\n"); + exit(99); + } + + // If our page range selection and document size indicate we're only + // outputting a single page, ensure that even/odd page selection doesn't + // filter out that single page. + if (firstPage == lastPage && ((printOnlyEven && firstPage % 2 == 1) || (printOnlyOdd && firstPage % 2 == 0))) { + fprintf(stderr, "Invalid even/odd page selection, no pages match criteria.\n"); + exit(99); + } + + if (singleFile && firstPage < lastPage) { + if (!quiet) { + fprintf(stderr, "Warning: Single file will write only the first of the %d pages.\n", lastPage + 1 - firstPage); + } + lastPage = firstPage; } - lastPage = firstPage; - } #ifdef CAIRO_HAS_WIN32_SURFACE if (printdlg) { - bool allPages = false; - if (firstPage == 1 && lastPage == doc->getNumPages()) - allPages = true; - win32ShowPrintDialog(&expand, &noShrink, &noCenter, - &usePDFPageSize, &allPages, - &firstPage, &lastPage, doc->getNumPages()); - if (allPages) { - firstPage = 1; - lastPage = doc->getNumPages(); - } + bool allPages = false; + if (firstPage == 1 && lastPage == doc->getNumPages()) + allPages = true; + win32ShowPrintDialog(&expand, &noShrink, &noCenter, &usePDFPageSize, &allPages, &firstPage, &lastPage, doc->getNumPages()); + if (allPages) { + firstPage = 1; + lastPage = doc->getNumPages(); + } } else if (printToWin32) { - win32SetupPrinter(&printer, &printOpt, - duplex, setupdlg); + win32SetupPrinter(&printer, &printOpt, duplex, setupdlg); } #endif - - cairoOut = new CairoOutputDev(); + cairoOut = new CairoOutputDev(); #ifdef USE_CMS - cairoOut->setDisplayProfile(profile); + cairoOut->setDisplayProfile(profile); #endif - cairoOut->startDoc(doc); - if (sz != 0) - crop_w = crop_h = sz; - pg_num_len = numberOfCharacters(doc->getNumPages()); - for (pg = firstPage; pg <= lastPage; ++pg) { - if (printOnlyEven && pg % 2 == 1) continue; - if (printOnlyOdd && pg % 2 == 0) continue; - if (useCropBox) { - pg_w = doc->getPageCropWidth(pg); - pg_h = doc->getPageCropHeight(pg); - } else { - pg_w = doc->getPageMediaWidth(pg); - pg_h = doc->getPageMediaHeight(pg); - } + cairoOut->startDoc(doc); + if (sz != 0) + crop_w = crop_h = sz; + pg_num_len = numberOfCharacters(doc->getNumPages()); + for (pg = firstPage; pg <= lastPage; ++pg) { + if (printOnlyEven && pg % 2 == 1) + continue; + if (printOnlyOdd && pg % 2 == 0) + continue; + if (useCropBox) { + pg_w = doc->getPageCropWidth(pg); + pg_h = doc->getPageCropHeight(pg); + } else { + pg_w = doc->getPageMediaWidth(pg); + pg_h = doc->getPageMediaHeight(pg); + } - if (printing && pg == firstPage) { - if (paperWidth < 0 || paperHeight < 0) { - paperWidth = (int)ceil(pg_w); - paperHeight = (int)ceil(pg_h); - } - } + if (printing && pg == firstPage) { + if (paperWidth < 0 || paperHeight < 0) { + paperWidth = (int)ceil(pg_w); + paperHeight = (int)ceil(pg_h); + } + } - if ((doc->getPageRotate(pg) == 90) || (doc->getPageRotate(pg) == 270)) { - tmp = pg_w; - pg_w = pg_h; - pg_h = tmp; - } - if (scaleTo != 0) { - resolution = (72.0 * scaleTo) / (pg_w > pg_h ? pg_w : pg_h); - x_resolution = y_resolution = resolution; - } else { - if (x_scaleTo > 0) { - x_resolution = (72.0 * x_scaleTo) / pg_w; - if (y_scaleTo == -1) - y_resolution = x_resolution; - } - if (y_scaleTo > 0) { - y_resolution = (72.0 * y_scaleTo) / pg_h; - if (x_scaleTo == -1) - x_resolution = y_resolution; - } - } - if (imageFileName) { - delete imageFileName; - imageFileName = nullptr; + if ((doc->getPageRotate(pg) == 90) || (doc->getPageRotate(pg) == 270)) { + tmp = pg_w; + pg_w = pg_h; + pg_h = tmp; + } + if (scaleTo != 0) { + resolution = (72.0 * scaleTo) / (pg_w > pg_h ? pg_w : pg_h); + x_resolution = y_resolution = resolution; + } else { + if (x_scaleTo > 0) { + x_resolution = (72.0 * x_scaleTo) / pg_w; + if (y_scaleTo == -1) + y_resolution = x_resolution; + } + if (y_scaleTo > 0) { + y_resolution = (72.0 * y_scaleTo) / pg_h; + if (x_scaleTo == -1) + x_resolution = y_resolution; + } + } + if (imageFileName) { + delete imageFileName; + imageFileName = nullptr; + } + if (!printing) + imageFileName = getImageFileName(outputFileName, pg_num_len, pg); + getOutputSize(pg_w, pg_h, &output_w, &output_h); + + if (pg == firstPage) + beginDocument(fileName, outputFileName, output_w, output_h); + beginPage(&output_w, &output_h); + renderPage(doc, cairoOut, pg, pg_w, pg_h, output_w, output_h); + endPage(imageFileName); } - if (!printing) - imageFileName = getImageFileName(outputFileName, pg_num_len, pg); - getOutputSize(pg_w, pg_h, &output_w, &output_h); - - if (pg == firstPage) - beginDocument(fileName, outputFileName, output_w, output_h); - beginPage(&output_w, &output_h); - renderPage(doc, cairoOut, pg, pg_w, pg_h, output_w, output_h); - endPage(imageFileName); - } - endDocument(); - - // clean up - delete cairoOut; - delete doc; - if (fileName) - delete fileName; - if (outputName) - delete outputName; - if (outputFileName) - delete outputFileName; - if (imageFileName) - delete imageFileName; - if (ownerPW) - delete ownerPW; - if (userPW) - delete userPW; + endDocument(); + + // clean up + delete cairoOut; + delete doc; + if (fileName) + delete fileName; + if (outputName) + delete outputName; + if (outputFileName) + delete outputFileName; + if (imageFileName) + delete imageFileName; + if (ownerPW) + delete ownerPW; + if (userPW) + delete userPW; #ifdef USE_CMS - if (icc_data) - gfree(icc_data); + if (icc_data) + gfree(icc_data); #endif - return 0; + return 0; } diff --git a/utils/pdftohtml.cc b/utils/pdftohtml.cc index cdb7964e..bbf4bf4f 100644 --- a/utils/pdftohtml.cc +++ b/utils/pdftohtml.cc @@ -42,7 +42,7 @@ #include <cstddef> #include <cstring> #ifdef HAVE_DIRENT_H -#include <dirent.h> +# include <dirent.h> #endif #include <ctime> #include "parseargs.h" @@ -62,8 +62,8 @@ #include "PDFDocFactory.h" #include "HtmlOutputDev.h" #ifdef HAVE_SPLASH -#include "SplashOutputDev.h" -#include "splash/SplashBitmap.h" +# include "SplashOutputDev.h" +# include "splash/SplashBitmap.h" #endif #include "GlobalParams.h" #include "PDFDocEncoding.h" @@ -79,19 +79,19 @@ static bool rawOrder = true; bool printCommands = true; static bool printHelp = false; bool printHtml = false; -bool complexMode=false; -bool singleHtml=false; // singleHtml +bool complexMode = false; +bool singleHtml = false; // singleHtml bool dataUrls = false; -bool ignore=false; -static char extension[5]="png"; -static double scale=1.5; -bool noframes=false; -bool stout=false; -bool xml=false; +bool ignore = false; +static char extension[5] = "png"; +static double scale = 1.5; +bool noframes = false; +bool stout = false; +bool xml = false; bool noRoundedCoordinates = false; -static bool errQuiet=false; -static bool noDrm=false; -double wordBreakThreshold=10; // 10%, below converted into a coefficient - 0.1 +static bool errQuiet = false; +static bool noDrm = false; +double wordBreakThreshold = 10; // 10%, below converted into a coefficient - 0.1 bool showHidden = false; bool noMerge = false; @@ -100,450 +100,397 @@ static char ownerPassword[33] = ""; static char userPassword[33] = ""; static bool printVersion = false; -static GooString* getInfoString(Dict *infoDict, const char *key); -static GooString* getInfoDate(Dict *infoDict, const char *key); +static GooString *getInfoString(Dict *infoDict, const char *key); +static GooString *getInfoDate(Dict *infoDict, const char *key); static char textEncName[128] = ""; -static const ArgDesc argDesc[] = { - {"-f", argInt, &firstPage, 0, - "first page to convert"}, - {"-l", argInt, &lastPage, 0, - "last page to convert"}, - /*{"-raw", argFlag, &rawOrder, 0, - "keep strings in content stream order"},*/ - {"-q", argFlag, &errQuiet, 0, - "don't print any messages or errors"}, - {"-h", argFlag, &printHelp, 0, - "print usage information"}, - {"-?", argFlag, &printHelp, 0, - "print usage information"}, - {"-help", argFlag, &printHelp, 0, - "print usage information"}, - {"--help", argFlag, &printHelp, 0, - "print usage information"}, - {"-p", argFlag, &printHtml, 0, - "exchange .pdf links by .html"}, - {"-c", argFlag, &complexMode, 0, - "generate complex document"}, - {"-s", argFlag, &singleHtml, 0, - "generate single document that includes all pages"}, +static const ArgDesc argDesc[] = { { "-f", argInt, &firstPage, 0, "first page to convert" }, + { "-l", argInt, &lastPage, 0, "last page to convert" }, + /*{"-raw", argFlag, &rawOrder, 0, + "keep strings in content stream order"},*/ + { "-q", argFlag, &errQuiet, 0, "don't print any messages or errors" }, + { "-h", argFlag, &printHelp, 0, "print usage information" }, + { "-?", argFlag, &printHelp, 0, "print usage information" }, + { "-help", argFlag, &printHelp, 0, "print usage information" }, + { "--help", argFlag, &printHelp, 0, "print usage information" }, + { "-p", argFlag, &printHtml, 0, "exchange .pdf links by .html" }, + { "-c", argFlag, &complexMode, 0, "generate complex document" }, + { "-s", argFlag, &singleHtml, 0, "generate single document that includes all pages" }, #ifdef HAVE_IN_MEMORY_FILE - {"-dataurls", argFlag, &dataUrls, 0, - "use data URLs instead of external images in HTML"}, + { "-dataurls", argFlag, &dataUrls, 0, "use data URLs instead of external images in HTML" }, #endif - {"-i", argFlag, &ignore, 0, - "ignore images"}, - {"-noframes", argFlag, &noframes, 0, - "generate no frames"}, - {"-stdout" ,argFlag, &stout, 0, - "use standard output"}, - {"-zoom", argFP, &scale, 0, - "zoom the pdf document (default 1.5)"}, - {"-xml", argFlag, &xml, 0, - "output for XML post-processing"}, - {"-noroundcoord", argFlag, &noRoundedCoordinates, 0, - "do not round coordinates (with XML output only)"}, - {"-hidden", argFlag, &showHidden, 0, - "output hidden text"}, - {"-nomerge", argFlag, &noMerge, 0, - "do not merge paragraphs"}, - {"-enc", argString, textEncName, sizeof(textEncName), - "output text encoding name"}, - {"-fmt", argString, extension, sizeof(extension), - "image file format for Splash output (png or jpg)"}, - {"-v", argFlag, &printVersion, 0, - "print copyright and version info"}, - {"-opw", argString, ownerPassword, sizeof(ownerPassword), - "owner password (for encrypted files)"}, - {"-upw", argString, userPassword, sizeof(userPassword), - "user password (for encrypted files)"}, - {"-nodrm", argFlag, &noDrm, 0, - "override document DRM settings"}, - {"-wbt", argFP, &wordBreakThreshold, 0, - "word break threshold (default 10 percent)"}, - {"-fontfullname", argFlag, &fontFullName, 0, - "outputs font full name"}, - {} -}; + { "-i", argFlag, &ignore, 0, "ignore images" }, + { "-noframes", argFlag, &noframes, 0, "generate no frames" }, + { "-stdout", argFlag, &stout, 0, "use standard output" }, + { "-zoom", argFP, &scale, 0, "zoom the pdf document (default 1.5)" }, + { "-xml", argFlag, &xml, 0, "output for XML post-processing" }, + { "-noroundcoord", argFlag, &noRoundedCoordinates, 0, "do not round coordinates (with XML output only)" }, + { "-hidden", argFlag, &showHidden, 0, "output hidden text" }, + { "-nomerge", argFlag, &noMerge, 0, "do not merge paragraphs" }, + { "-enc", argString, textEncName, sizeof(textEncName), "output text encoding name" }, + { "-fmt", argString, extension, sizeof(extension), "image file format for Splash output (png or jpg)" }, + { "-v", argFlag, &printVersion, 0, "print copyright and version info" }, + { "-opw", argString, ownerPassword, sizeof(ownerPassword), "owner password (for encrypted files)" }, + { "-upw", argString, userPassword, sizeof(userPassword), "user password (for encrypted files)" }, + { "-nodrm", argFlag, &noDrm, 0, "override document DRM settings" }, + { "-wbt", argFP, &wordBreakThreshold, 0, "word break threshold (default 10 percent)" }, + { "-fontfullname", argFlag, &fontFullName, 0, "outputs font full name" }, + {} }; #ifdef HAVE_SPLASH -class SplashOutputDevNoText : public SplashOutputDev { +class SplashOutputDevNoText : public SplashOutputDev +{ public: - SplashOutputDevNoText(SplashColorMode colorModeA, int bitmapRowPadA, - bool reverseVideoA, SplashColorPtr paperColorA, - bool bitmapTopDownA = true) : SplashOutputDev(colorModeA, - bitmapRowPadA, reverseVideoA, paperColorA, bitmapTopDownA) { } - ~SplashOutputDevNoText() override { } - - void drawChar(GfxState *state, double x, double y, - double dx, double dy, - double originX, double originY, - CharCode code, int nBytes, const Unicode *u, int uLen) override { } - bool beginType3Char(GfxState *state, double x, double y, - double dx, double dy, - CharCode code, const Unicode *u, int uLen) override { return false; } - void endType3Char(GfxState *state) override { } - void beginTextObject(GfxState *state) override { } - void endTextObject(GfxState *state) override { } - bool interpretType3Chars() override { return false; } + SplashOutputDevNoText(SplashColorMode colorModeA, int bitmapRowPadA, bool reverseVideoA, SplashColorPtr paperColorA, bool bitmapTopDownA = true) + : SplashOutputDev(colorModeA, bitmapRowPadA, reverseVideoA, paperColorA, bitmapTopDownA) { } + ~SplashOutputDevNoText() override { } + + void drawChar(GfxState *state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, const Unicode *u, int uLen) override { } + bool beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode code, const Unicode *u, int uLen) override { return false; } + void endType3Char(GfxState *state) override { } + void beginTextObject(GfxState *state) override { } + void endTextObject(GfxState *state) override { } + bool interpretType3Chars() override { return false; } }; #endif -int main(int argc, char *argv[]) { - PDFDoc *doc = nullptr; - GooString *fileName = nullptr; - GooString *docTitle = nullptr; - GooString *author = nullptr, *keywords = nullptr, *subject = nullptr, *date = nullptr; - GooString *htmlFileName = nullptr; - HtmlOutputDev *htmlOut = nullptr; +int main(int argc, char *argv[]) +{ + PDFDoc *doc = nullptr; + GooString *fileName = nullptr; + GooString *docTitle = nullptr; + GooString *author = nullptr, *keywords = nullptr, *subject = nullptr, *date = nullptr; + GooString *htmlFileName = nullptr; + HtmlOutputDev *htmlOut = nullptr; #ifdef HAVE_SPLASH - SplashOutputDev *splashOut = nullptr; + SplashOutputDev *splashOut = nullptr; #endif - bool doOutline; - bool ok; - GooString *ownerPW, *userPW; - Object info; - int exit_status = EXIT_FAILURE; - - Win32Console win32Console(&argc, &argv); - // parse args - ok = parseArgs(argDesc, &argc, argv); - if (!ok || argc < 2 || argc > 3 || printHelp || printVersion) { - fprintf(stderr, "pdftohtml version %s\n", PACKAGE_VERSION); - fprintf(stderr, "%s\n", popplerCopyright); - fprintf(stderr, "%s\n", "Copyright 1999-2003 Gueorgui Ovtcharov and Rainer Dorsch"); - fprintf(stderr, "%s\n\n", xpdfCopyright); - if (!printVersion) { - printUsage("pdftohtml", "<PDF-file> [<html-file> <xml-file>]", argDesc); - } - exit(printHelp || printVersion ? 0 : 1); - } - - // init error file - //errorInit(); - - // read config file - globalParams = std::make_unique<GlobalParams>(); - - if (errQuiet) { - globalParams->setErrQuiet(errQuiet); - printCommands = false; // I'm not 100% what is the difference between them - } - - if (textEncName[0]) { - globalParams->setTextEncoding(textEncName); - if( !globalParams->getTextEncoding() ) { - goto error; + bool doOutline; + bool ok; + GooString *ownerPW, *userPW; + Object info; + int exit_status = EXIT_FAILURE; + + Win32Console win32Console(&argc, &argv); + // parse args + ok = parseArgs(argDesc, &argc, argv); + if (!ok || argc < 2 || argc > 3 || printHelp || printVersion) { + fprintf(stderr, "pdftohtml version %s\n", PACKAGE_VERSION); + fprintf(stderr, "%s\n", popplerCopyright); + fprintf(stderr, "%s\n", "Copyright 1999-2003 Gueorgui Ovtcharov and Rainer Dorsch"); + fprintf(stderr, "%s\n\n", xpdfCopyright); + if (!printVersion) { + printUsage("pdftohtml", "<PDF-file> [<html-file> <xml-file>]", argDesc); + } + exit(printHelp || printVersion ? 0 : 1); } - } - - // convert from user-friendly percents into a coefficient - wordBreakThreshold /= 100.0; - - // open PDF file - if (ownerPassword[0]) { - ownerPW = new GooString(ownerPassword); - } else { - ownerPW = nullptr; - } - if (userPassword[0]) { - userPW = new GooString(userPassword); - } else { - userPW = nullptr; - } - - fileName = new GooString(argv[1]); - - if (fileName->cmp("-") == 0) { - delete fileName; - fileName = new GooString("fd://0"); - } - - doc = PDFDocFactory().createPDFDoc(*fileName, ownerPW, userPW); - - if (userPW) { - delete userPW; - } - if (ownerPW) { - delete ownerPW; - } - if (!doc->isOk()) { - goto error; - } - - // check for copy permission - if (!doc->okToCopy()) { - if (!noDrm) { - error(errNotAllowed, -1, "Copying of text from this document is not allowed."); - goto error; + + // init error file + // errorInit(); + + // read config file + globalParams = std::make_unique<GlobalParams>(); + + if (errQuiet) { + globalParams->setErrQuiet(errQuiet); + printCommands = false; // I'm not 100% what is the difference between them } - fprintf(stderr, "Document has copy-protection bit set.\n"); - } - - // construct text file name - if (argc == 3) { - GooString* tmp = new GooString(argv[2]); - if (!xml) { - if (tmp->getLength() >= 5) { - const char *p = tmp->c_str() + tmp->getLength() - 5; - if (!strcmp(p, ".html") || !strcmp(p, ".HTML")) { - htmlFileName = new GooString(tmp->c_str(), tmp->getLength() - 5); + + if (textEncName[0]) { + globalParams->setTextEncoding(textEncName); + if (!globalParams->getTextEncoding()) { + goto error; } - } + } + + // convert from user-friendly percents into a coefficient + wordBreakThreshold /= 100.0; + + // open PDF file + if (ownerPassword[0]) { + ownerPW = new GooString(ownerPassword); + } else { + ownerPW = nullptr; + } + if (userPassword[0]) { + userPW = new GooString(userPassword); } else { - if (tmp->getLength() >= 4) { - const char *p = tmp->c_str() + tmp->getLength() - 4; - if (!strcmp(p, ".xml") || !strcmp(p, ".XML")) { - htmlFileName = new GooString(tmp->c_str(), tmp->getLength() - 4); + userPW = nullptr; + } + + fileName = new GooString(argv[1]); + + if (fileName->cmp("-") == 0) { + delete fileName; + fileName = new GooString("fd://0"); + } + + doc = PDFDocFactory().createPDFDoc(*fileName, ownerPW, userPW); + + if (userPW) { + delete userPW; + } + if (ownerPW) { + delete ownerPW; + } + if (!doc->isOk()) { + goto error; + } + + // check for copy permission + if (!doc->okToCopy()) { + if (!noDrm) { + error(errNotAllowed, -1, "Copying of text from this document is not allowed."); + goto error; } - } + fprintf(stderr, "Document has copy-protection bit set.\n"); + } + + // construct text file name + if (argc == 3) { + GooString *tmp = new GooString(argv[2]); + if (!xml) { + if (tmp->getLength() >= 5) { + const char *p = tmp->c_str() + tmp->getLength() - 5; + if (!strcmp(p, ".html") || !strcmp(p, ".HTML")) { + htmlFileName = new GooString(tmp->c_str(), tmp->getLength() - 5); + } + } + } else { + if (tmp->getLength() >= 4) { + const char *p = tmp->c_str() + tmp->getLength() - 4; + if (!strcmp(p, ".xml") || !strcmp(p, ".XML")) { + htmlFileName = new GooString(tmp->c_str(), tmp->getLength() - 4); + } + } + } + if (!htmlFileName) { + htmlFileName = new GooString(tmp); + } + delete tmp; + } else if (fileName->cmp("fd://0") == 0) { + error(errCommandLine, -1, "You have to provide an output filename when reading from stdin."); + goto error; + } else { + const char *p = fileName->c_str() + fileName->getLength() - 4; + if (!strcmp(p, ".pdf") || !strcmp(p, ".PDF")) + htmlFileName = new GooString(fileName->c_str(), fileName->getLength() - 4); + else + htmlFileName = fileName->copy(); + // htmlFileName->append(".html"); + } + + if (scale > 3.0) + scale = 3.0; + if (scale < 0.5) + scale = 0.5; + + if (complexMode) { + // noframes=false; + stout = false; } - if (!htmlFileName) { - htmlFileName =new GooString(tmp); + + if (stout) { + noframes = true; + complexMode = false; + } + + if (xml) { + complexMode = true; + singleHtml = false; + noframes = true; + noMerge = true; + } + + // get page range + if (firstPage < 1) + firstPage = 1; + if (lastPage < 1 || lastPage > doc->getNumPages()) + lastPage = doc->getNumPages(); + if (lastPage < firstPage) { + error(errCommandLine, -1, "Wrong page range given: the first page ({0:d}) can not be after the last page ({1:d}).", firstPage, lastPage); + goto error; + } + + info = doc->getDocInfo(); + if (info.isDict()) { + docTitle = getInfoString(info.getDict(), "Title"); + author = getInfoString(info.getDict(), "Author"); + keywords = getInfoString(info.getDict(), "Keywords"); + subject = getInfoString(info.getDict(), "Subject"); + date = getInfoDate(info.getDict(), "ModDate"); + if (!date) + date = getInfoDate(info.getDict(), "CreationDate"); } - delete tmp; - } else if (fileName->cmp("fd://0") == 0) { - error(errCommandLine, -1, "You have to provide an output filename when reading from stdin."); - goto error; - } else { - const char *p = fileName->c_str() + fileName->getLength() - 4; - if (!strcmp(p, ".pdf") || !strcmp(p, ".PDF")) - htmlFileName = new GooString(fileName->c_str(), - fileName->getLength() - 4); + if (!docTitle) + docTitle = new GooString(htmlFileName); + + if (!singleHtml) + rawOrder = complexMode; // todo: figure out what exactly rawOrder do :) else - htmlFileName = fileName->copy(); - // htmlFileName->append(".html"); - } - - if (scale>3.0) scale=3.0; - if (scale<0.5) scale=0.5; - - if (complexMode) { - //noframes=false; - stout=false; - } - - if (stout) { - noframes=true; - complexMode=false; - } - - if (xml) - { - complexMode = true; - singleHtml = false; - noframes = true; - noMerge = true; - } - - // get page range - if (firstPage < 1) - firstPage = 1; - if (lastPage < 1 || lastPage > doc->getNumPages()) - lastPage = doc->getNumPages(); - if (lastPage < firstPage) { - error(errCommandLine, -1, - "Wrong page range given: the first page ({0:d}) can not be after the last page ({1:d}).", - firstPage, lastPage); - goto error; - } - - info = doc->getDocInfo(); - if (info.isDict()) { - docTitle = getInfoString(info.getDict(), "Title"); - author = getInfoString(info.getDict(), "Author"); - keywords = getInfoString(info.getDict(), "Keywords"); - subject = getInfoString(info.getDict(), "Subject"); - date = getInfoDate(info.getDict(), "ModDate"); - if( !date ) - date = getInfoDate(info.getDict(), "CreationDate"); - } - if( !docTitle ) docTitle = new GooString(htmlFileName); - - if (!singleHtml) - rawOrder = complexMode; // todo: figure out what exactly rawOrder do :) - else - rawOrder = singleHtml; - - doOutline = doc->getOutline()->getItems() != nullptr; - // write text file - htmlOut = new HtmlOutputDev(doc->getCatalog(), htmlFileName->c_str(), - docTitle->c_str(), - author ? author->c_str() : nullptr, - keywords ? keywords->c_str() : nullptr, - subject ? subject->c_str() : nullptr, - date ? date->c_str() : nullptr, - rawOrder, - firstPage, - doOutline); - delete docTitle; - if( author ) - { - delete author; - } - if( keywords ) - { - delete keywords; - } - if( subject ) - { - delete subject; - } - if( date ) - { - delete date; - } - - if ((complexMode || singleHtml) && !xml && !ignore) { -#ifdef HAVE_SPLASH - GooString *imgFileName = nullptr; - // White paper color - SplashColor color; - color[0] = color[1] = color[2] = 255; - // If the user specified "jpg" use JPEG, otherwise PNG - SplashImageFileFormat format = strcmp(extension, "jpg") ? - splashFormatPng : splashFormatJpeg; - - splashOut = new SplashOutputDevNoText(splashModeRGB8, 4, false, color); - splashOut->startDoc(doc); - - for (int pg = firstPage; pg <= lastPage; ++pg) { - InMemoryFile imf; - doc->displayPage(splashOut, pg, - 72 * scale, 72 * scale, - 0, true, false, false); - SplashBitmap *bitmap = splashOut->getBitmap(); - - imgFileName = GooString::format("{0:s}{1:03d}.{2:s}", - htmlFileName->c_str(), pg, extension); - auto f1 = dataUrls ? imf.open("wb") : fopen(imgFileName->c_str(), "wb"); - if (!f1) { - fprintf(stderr, "Could not open %s\n", imgFileName->c_str()); - delete imgFileName; - continue; - } - bitmap->writeImgFile(format, f1, 72 * scale, 72 * scale); - fclose(f1); - if (dataUrls) { - htmlOut->addBackgroundImage( - std::string((format == splashFormatJpeg) ? "data:image/jpeg;base64," : "data:image/png;base64,") + - gbase64Encode(imf.getBuffer()) - ); - } else { - htmlOut->addBackgroundImage(gbasename(imgFileName->c_str())); - } - delete imgFileName; + rawOrder = singleHtml; + + doOutline = doc->getOutline()->getItems() != nullptr; + // write text file + htmlOut = new HtmlOutputDev(doc->getCatalog(), htmlFileName->c_str(), docTitle->c_str(), author ? author->c_str() : nullptr, keywords ? keywords->c_str() : nullptr, subject ? subject->c_str() : nullptr, date ? date->c_str() : nullptr, + rawOrder, firstPage, doOutline); + delete docTitle; + if (author) { + delete author; + } + if (keywords) { + delete keywords; + } + if (subject) { + delete subject; + } + if (date) { + delete date; } - delete splashOut; + if ((complexMode || singleHtml) && !xml && !ignore) { +#ifdef HAVE_SPLASH + GooString *imgFileName = nullptr; + // White paper color + SplashColor color; + color[0] = color[1] = color[2] = 255; + // If the user specified "jpg" use JPEG, otherwise PNG + SplashImageFileFormat format = strcmp(extension, "jpg") ? splashFormatPng : splashFormatJpeg; + + splashOut = new SplashOutputDevNoText(splashModeRGB8, 4, false, color); + splashOut->startDoc(doc); + + for (int pg = firstPage; pg <= lastPage; ++pg) { + InMemoryFile imf; + doc->displayPage(splashOut, pg, 72 * scale, 72 * scale, 0, true, false, false); + SplashBitmap *bitmap = splashOut->getBitmap(); + + imgFileName = GooString::format("{0:s}{1:03d}.{2:s}", htmlFileName->c_str(), pg, extension); + auto f1 = dataUrls ? imf.open("wb") : fopen(imgFileName->c_str(), "wb"); + if (!f1) { + fprintf(stderr, "Could not open %s\n", imgFileName->c_str()); + delete imgFileName; + continue; + } + bitmap->writeImgFile(format, f1, 72 * scale, 72 * scale); + fclose(f1); + if (dataUrls) { + htmlOut->addBackgroundImage(std::string((format == splashFormatJpeg) ? "data:image/jpeg;base64," : "data:image/png;base64,") + gbase64Encode(imf.getBuffer())); + } else { + htmlOut->addBackgroundImage(gbasename(imgFileName->c_str())); + } + delete imgFileName; + } + + delete splashOut; #else - fprintf(stderr, "Your pdftohtml was built without splash backend support. It is needed for the option you want to use.\n"); - delete htmlOut; - delete htmlFileName; - delete fileName; - delete doc; - return -1; + fprintf(stderr, "Your pdftohtml was built without splash backend support. It is needed for the option you want to use.\n"); + delete htmlOut; + delete htmlFileName; + delete fileName; + delete doc; + return -1; #endif - } + } - if (htmlOut->isOk()) - { - doc->displayPages(htmlOut, firstPage, lastPage, 72 * scale, 72 * scale, 0, - true, false, false); - htmlOut->dumpDocOutline(doc); - } + if (htmlOut->isOk()) { + doc->displayPages(htmlOut, firstPage, lastPage, 72 * scale, 72 * scale, 0, true, false, false); + htmlOut->dumpDocOutline(doc); + } - delete htmlOut; + delete htmlOut; - exit_status = EXIT_SUCCESS; + exit_status = EXIT_SUCCESS; - // clean up - error: - if(doc) delete doc; - delete fileName; + // clean up +error: + if (doc) + delete doc; + delete fileName; - if(htmlFileName) delete htmlFileName; + if (htmlFileName) + delete htmlFileName; - return exit_status; + return exit_status; } -static GooString* getInfoString(Dict *infoDict, const char *key) { - Object obj; - // Raw value as read from PDF (may be in pdfDocEncoding or UCS2) - const GooString *rawString; - // Value converted to unicode - Unicode *unicodeString; - int unicodeLength; - // Value HTML escaped and converted to desired encoding - GooString *encodedString = nullptr; - // Is rawString UCS2 (as opposed to pdfDocEncoding) - bool isUnicode; - - obj = infoDict->lookup(key); - if (obj.isString()) { - rawString = obj.getString(); - - // Convert rawString to unicode - if (rawString->hasUnicodeMarker()) { - isUnicode = true; - unicodeLength = (obj.getString()->getLength() - 2) / 2; - } else { - isUnicode = false; - unicodeLength = obj.getString()->getLength(); - } - unicodeString = new Unicode[unicodeLength]; - - for (int i=0; i<unicodeLength; i++) { - if (isUnicode) { - unicodeString[i] = ((rawString->getChar((i+1)*2) & 0xff) << 8) | - (rawString->getChar(((i+1)*2)+1) & 0xff); - } else { - unicodeString[i] = pdfDocEncoding[rawString->getChar(i) & 0xff]; - } - } +static GooString *getInfoString(Dict *infoDict, const char *key) +{ + Object obj; + // Raw value as read from PDF (may be in pdfDocEncoding or UCS2) + const GooString *rawString; + // Value converted to unicode + Unicode *unicodeString; + int unicodeLength; + // Value HTML escaped and converted to desired encoding + GooString *encodedString = nullptr; + // Is rawString UCS2 (as opposed to pdfDocEncoding) + bool isUnicode; + + obj = infoDict->lookup(key); + if (obj.isString()) { + rawString = obj.getString(); + + // Convert rawString to unicode + if (rawString->hasUnicodeMarker()) { + isUnicode = true; + unicodeLength = (obj.getString()->getLength() - 2) / 2; + } else { + isUnicode = false; + unicodeLength = obj.getString()->getLength(); + } + unicodeString = new Unicode[unicodeLength]; + + for (int i = 0; i < unicodeLength; i++) { + if (isUnicode) { + unicodeString[i] = ((rawString->getChar((i + 1) * 2) & 0xff) << 8) | (rawString->getChar(((i + 1) * 2) + 1) & 0xff); + } else { + unicodeString[i] = pdfDocEncoding[rawString->getChar(i) & 0xff]; + } + } - // HTML escape and encode unicode - encodedString = HtmlFont::HtmlFilter(unicodeString, unicodeLength); - delete[] unicodeString; - } + // HTML escape and encode unicode + encodedString = HtmlFont::HtmlFilter(unicodeString, unicodeLength); + delete[] unicodeString; + } - return encodedString; + return encodedString; } -static GooString* getInfoDate(Dict *infoDict, const char *key) { - Object obj; - const char *s; - int year, mon, day, hour, min, sec, tz_hour, tz_minute; - char tz; - struct tm tmStruct; - GooString *result = nullptr; - char buf[256]; - - obj = infoDict->lookup(key); - if (obj.isString()) { - s = obj.getString()->c_str(); - // TODO do something with the timezone info - if ( parseDateString( s, &year, &mon, &day, &hour, &min, &sec, &tz, &tz_hour, &tz_minute ) ) { - tmStruct.tm_year = year - 1900; - tmStruct.tm_mon = mon - 1; - tmStruct.tm_mday = day; - tmStruct.tm_hour = hour; - tmStruct.tm_min = min; - tmStruct.tm_sec = sec; - tmStruct.tm_wday = -1; - tmStruct.tm_yday = -1; - tmStruct.tm_isdst = -1; - mktime(&tmStruct); // compute the tm_wday and tm_yday fields - if (strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S+00:00", &tmStruct)) { - result = new GooString(buf); - } else { - result = new GooString(s); - } - } else { - result = new GooString(s); +static GooString *getInfoDate(Dict *infoDict, const char *key) +{ + Object obj; + const char *s; + int year, mon, day, hour, min, sec, tz_hour, tz_minute; + char tz; + struct tm tmStruct; + GooString *result = nullptr; + char buf[256]; + + obj = infoDict->lookup(key); + if (obj.isString()) { + s = obj.getString()->c_str(); + // TODO do something with the timezone info + if (parseDateString(s, &year, &mon, &day, &hour, &min, &sec, &tz, &tz_hour, &tz_minute)) { + tmStruct.tm_year = year - 1900; + tmStruct.tm_mon = mon - 1; + tmStruct.tm_mday = day; + tmStruct.tm_hour = hour; + tmStruct.tm_min = min; + tmStruct.tm_sec = sec; + tmStruct.tm_wday = -1; + tmStruct.tm_yday = -1; + tmStruct.tm_isdst = -1; + mktime(&tmStruct); // compute the tm_wday and tm_yday fields + if (strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S+00:00", &tmStruct)) { + result = new GooString(buf); + } else { + result = new GooString(s); + } + } else { + result = new GooString(s); + } } - } - return result; + return result; } - diff --git a/utils/pdftoppm.cc b/utils/pdftoppm.cc index 2652f799..ebb1c6d4 100644 --- a/utils/pdftoppm.cc +++ b/utils/pdftoppm.cc @@ -42,8 +42,8 @@ #include "config.h" #include <poppler-config.h> #ifdef _WIN32 -#include <fcntl.h> // for O_BINARY -#include <io.h> // for setmode +# include <fcntl.h> // for O_BINARY +# include <io.h> // for setmode #endif #include <cstdio> #include <cmath> @@ -67,13 +67,13 @@ // #define UTILS_USE_PTHREADS 1 #ifdef UTILS_USE_PTHREADS -#include <cerrno> -#include <pthread.h> -#include <deque> +# include <cerrno> +# include <pthread.h> +# include <deque> #endif // UTILS_USE_PTHREADS #ifdef USE_CMS -#include <lcms2.h> +# include <lcms2.h> #endif static int firstPage = 1; @@ -130,114 +130,68 @@ static bool quiet = false; static bool printVersion = false; static bool printHelp = false; -static const ArgDesc argDesc[] = { - {"-f", argInt, &firstPage, 0, - "first page to print"}, - {"-l", argInt, &lastPage, 0, - "last page to print"}, - {"-o", argFlag, &printOnlyOdd, 0, - "print only odd pages"}, - {"-e", argFlag, &printOnlyEven, 0, - "print only even pages"}, - {"-singlefile", argFlag, &singleFile, 0, - "write only the first page and do not add digits"}, - {"-scale-dimension-before-rotation", argFlag, &scaleDimensionBeforeRotation, 0, - "for rotated pdf, resize dimensions before the rotation"}, - - {"-r", argFP, &resolution, 0, - "resolution, in DPI (default is 150)"}, - {"-rx", argFP, &x_resolution, 0, - "X resolution, in DPI (default is 150)"}, - {"-ry", argFP, &y_resolution, 0, - "Y resolution, in DPI (default is 150)"}, - {"-scale-to", argInt, &scaleTo, 0, - "scales each page to fit within scale-to*scale-to pixel box"}, - {"-scale-to-x", argInt, &x_scaleTo, 0, - "scales each page horizontally to fit in scale-to-x pixels"}, - {"-scale-to-y", argInt, &y_scaleTo, 0, - "scales each page vertically to fit in scale-to-y pixels"}, - - {"-x", argInt, ¶m_x, 0, - "x-coordinate of the crop area top left corner"}, - {"-y", argInt, ¶m_y, 0, - "y-coordinate of the crop area top left corner"}, - {"-W", argInt, ¶m_w, 0, - "width of crop area in pixels (default is 0)"}, - {"-H", argInt, ¶m_h, 0, - "height of crop area in pixels (default is 0)"}, - {"-sz", argInt, &sz, 0, - "size of crop square in pixels (sets W and H)"}, - {"-cropbox",argFlag, &useCropBox, 0, - "use the crop box rather than media box"}, - {"-hide-annotations", argFlag, &hideAnnotations, 0, - "do not show annotations"}, - - {"-mono", argFlag, &mono, 0, - "generate a monochrome PBM file"}, - {"-gray", argFlag, &gray, 0, - "generate a grayscale PGM file"}, +static const ArgDesc argDesc[] = { { "-f", argInt, &firstPage, 0, "first page to print" }, + { "-l", argInt, &lastPage, 0, "last page to print" }, + { "-o", argFlag, &printOnlyOdd, 0, "print only odd pages" }, + { "-e", argFlag, &printOnlyEven, 0, "print only even pages" }, + { "-singlefile", argFlag, &singleFile, 0, "write only the first page and do not add digits" }, + { "-scale-dimension-before-rotation", argFlag, &scaleDimensionBeforeRotation, 0, "for rotated pdf, resize dimensions before the rotation" }, + + { "-r", argFP, &resolution, 0, "resolution, in DPI (default is 150)" }, + { "-rx", argFP, &x_resolution, 0, "X resolution, in DPI (default is 150)" }, + { "-ry", argFP, &y_resolution, 0, "Y resolution, in DPI (default is 150)" }, + { "-scale-to", argInt, &scaleTo, 0, "scales each page to fit within scale-to*scale-to pixel box" }, + { "-scale-to-x", argInt, &x_scaleTo, 0, "scales each page horizontally to fit in scale-to-x pixels" }, + { "-scale-to-y", argInt, &y_scaleTo, 0, "scales each page vertically to fit in scale-to-y pixels" }, + + { "-x", argInt, ¶m_x, 0, "x-coordinate of the crop area top left corner" }, + { "-y", argInt, ¶m_y, 0, "y-coordinate of the crop area top left corner" }, + { "-W", argInt, ¶m_w, 0, "width of crop area in pixels (default is 0)" }, + { "-H", argInt, ¶m_h, 0, "height of crop area in pixels (default is 0)" }, + { "-sz", argInt, &sz, 0, "size of crop square in pixels (sets W and H)" }, + { "-cropbox", argFlag, &useCropBox, 0, "use the crop box rather than media box" }, + { "-hide-annotations", argFlag, &hideAnnotations, 0, "do not show annotations" }, + + { "-mono", argFlag, &mono, 0, "generate a monochrome PBM file" }, + { "-gray", argFlag, &gray, 0, "generate a grayscale PGM file" }, #ifdef USE_CMS - {"-displayprofile", argGooString, &displayprofilename, 0, - "ICC color profile to use as the display profile"}, + { "-displayprofile", argGooString, &displayprofilename, 0, "ICC color profile to use as the display profile" }, #endif - {"-sep", argString, sep, sizeof(sep), - "single character separator between name and page number, default - "}, - {"-forcenum", argFlag, &forceNum, 0, - "force page number even if there is only one page "}, + { "-sep", argString, sep, sizeof(sep), "single character separator between name and page number, default - " }, + { "-forcenum", argFlag, &forceNum, 0, "force page number even if there is only one page " }, #ifdef ENABLE_LIBPNG - {"-png", argFlag, &png, 0, - "generate a PNG file"}, + { "-png", argFlag, &png, 0, "generate a PNG file" }, #endif #ifdef ENABLE_LIBJPEG - {"-jpeg", argFlag, &jpeg, 0, - "generate a JPEG file"}, - {"-jpegcmyk",argFlag, &jpegcmyk, 0, - "generate a CMYK JPEG file"}, - {"-jpegopt", argGooString, &jpegOpt, 0, - "jpeg options, with format <opt1>=<val1>[,<optN>=<valN>]*"}, + { "-jpeg", argFlag, &jpeg, 0, "generate a JPEG file" }, + { "-jpegcmyk", argFlag, &jpegcmyk, 0, "generate a CMYK JPEG file" }, + { "-jpegopt", argGooString, &jpegOpt, 0, "jpeg options, with format <opt1>=<val1>[,<optN>=<valN>]*" }, #endif - {"-overprint",argFlag, &overprint, 0, - "enable overprint"}, + { "-overprint", argFlag, &overprint, 0, "enable overprint" }, #ifdef ENABLE_LIBTIFF - {"-tiff", argFlag, &tiff, 0, - "generate a TIFF file"}, - {"-tiffcompression", argString, TiffCompressionStr, sizeof(TiffCompressionStr), - "set TIFF compression: none, packbits, jpeg, lzw, deflate"}, + { "-tiff", argFlag, &tiff, 0, "generate a TIFF file" }, + { "-tiffcompression", argString, TiffCompressionStr, sizeof(TiffCompressionStr), "set TIFF compression: none, packbits, jpeg, lzw, deflate" }, #endif - {"-freetype", argString, enableFreeTypeStr, sizeof(enableFreeTypeStr), - "enable FreeType font rasterizer: yes, no"}, - {"-thinlinemode", argString, thinLineModeStr, sizeof(thinLineModeStr), - "set thin line mode: none, solid, shape. Default: none"}, - - {"-aa", argString, antialiasStr, sizeof(antialiasStr), - "enable font anti-aliasing: yes, no"}, - {"-aaVector", argString, vectorAntialiasStr, sizeof(vectorAntialiasStr), - "enable vector anti-aliasing: yes, no"}, - - {"-opw", argString, ownerPassword, sizeof(ownerPassword), - "owner password (for encrypted files)"}, - {"-upw", argString, userPassword, sizeof(userPassword), - "user password (for encrypted files)"}, - + { "-freetype", argString, enableFreeTypeStr, sizeof(enableFreeTypeStr), "enable FreeType font rasterizer: yes, no" }, + { "-thinlinemode", argString, thinLineModeStr, sizeof(thinLineModeStr), "set thin line mode: none, solid, shape. Default: none" }, + + { "-aa", argString, antialiasStr, sizeof(antialiasStr), "enable font anti-aliasing: yes, no" }, + { "-aaVector", argString, vectorAntialiasStr, sizeof(vectorAntialiasStr), "enable vector anti-aliasing: yes, no" }, + + { "-opw", argString, ownerPassword, sizeof(ownerPassword), "owner password (for encrypted files)" }, + { "-upw", argString, userPassword, sizeof(userPassword), "user password (for encrypted files)" }, + #ifdef UTILS_USE_PTHREADS - {"-j", argInt, &numberOfJobs, 0, - "number of jobs to run concurrently"}, + { "-j", argInt, &numberOfJobs, 0, "number of jobs to run concurrently" }, #endif // UTILS_USE_PTHREADS - {"-q", argFlag, &quiet, 0, - "don't print any messages or errors"}, - {"-v", argFlag, &printVersion, 0, - "print copyright and version info"}, - {"-h", argFlag, &printHelp, 0, - "print usage information"}, - {"-help", argFlag, &printHelp, 0, - "print usage information"}, - {"--help", argFlag, &printHelp, 0, - "print usage information"}, - {"-?", argFlag, &printHelp, 0, - "print usage information"}, - {} -}; + { "-q", argFlag, &quiet, 0, "don't print any messages or errors" }, + { "-v", argFlag, &printVersion, 0, "print copyright and version info" }, + { "-h", argFlag, &printHelp, 0, "print usage information" }, + { "-help", argFlag, &printHelp, 0, "print usage information" }, + { "--help", argFlag, &printHelp, 0, "print usage information" }, + { "-?", argFlag, &printHelp, 0, "print usage information" }, + {} }; static bool needToRotate(int angle) { @@ -246,493 +200,467 @@ static bool needToRotate(int angle) static bool parseJpegOptions() { - //jpegOpt format is: <opt1>=<val1>,<opt2>=<val2>,... - const char *nextOpt = jpegOpt.c_str(); - while (nextOpt && *nextOpt) - { - const char *comma = strchr(nextOpt, ','); - GooString opt; - if (comma) { - opt.Set(nextOpt, comma - nextOpt); - nextOpt = comma + 1; - } else { - opt.Set(nextOpt); - nextOpt = nullptr; + // jpegOpt format is: <opt1>=<val1>,<opt2>=<val2>,... + const char *nextOpt = jpegOpt.c_str(); + while (nextOpt && *nextOpt) { + const char *comma = strchr(nextOpt, ','); + GooString opt; + if (comma) { + opt.Set(nextOpt, comma - nextOpt); + nextOpt = comma + 1; + } else { + opt.Set(nextOpt); + nextOpt = nullptr; + } + // here opt is "<optN>=<valN> " + const char *equal = strchr(opt.c_str(), '='); + if (!equal) { + fprintf(stderr, "Unknown jpeg option \"%s\"\n", opt.c_str()); + return false; + } + int iequal = equal - opt.c_str(); + GooString value(&opt, iequal + 1, opt.getLength() - iequal - 1); + opt.del(iequal, opt.getLength() - iequal); + // here opt is "<optN>" and value is "<valN>" + + if (opt.cmp("quality") == 0) { + if (!isInt(value.c_str())) { + fprintf(stderr, "Invalid jpeg quality\n"); + return false; + } + jpegQuality = atoi(value.c_str()); + if (jpegQuality < 0 || jpegQuality > 100) { + fprintf(stderr, "jpeg quality must be between 0 and 100\n"); + return false; + } + } else if (opt.cmp("progressive") == 0) { + jpegProgressive = false; + if (value.cmp("y") == 0) { + jpegProgressive = true; + } else if (value.cmp("n") != 0) { + fprintf(stderr, "jpeg progressive option must be \"y\" or \"n\"\n"); + return false; + } + } else if (opt.cmp("optimize") == 0 || opt.cmp("optimise") == 0) { + jpegOptimize = false; + if (value.cmp("y") == 0) { + jpegOptimize = true; + } else if (value.cmp("n") != 0) { + fprintf(stderr, "jpeg optimize option must be \"y\" or \"n\"\n"); + return false; + } + } else { + fprintf(stderr, "Unknown jpeg option \"%s\"\n", opt.c_str()); + return false; + } } - //here opt is "<optN>=<valN> " - const char *equal = strchr(opt.c_str(), '='); - if (!equal) { - fprintf(stderr, "Unknown jpeg option \"%s\"\n", opt.c_str()); - return false; - } - int iequal = equal - opt.c_str(); - GooString value(&opt, iequal + 1, opt.getLength() - iequal - 1); - opt.del(iequal, opt.getLength() - iequal); - //here opt is "<optN>" and value is "<valN>" - - if (opt.cmp("quality") == 0) { - if (!isInt(value.c_str())) { - fprintf(stderr, "Invalid jpeg quality\n"); - return false; - } - jpegQuality = atoi(value.c_str()); - if (jpegQuality < 0 || jpegQuality > 100) { - fprintf(stderr, "jpeg quality must be between 0 and 100\n"); - return false; - } - } else if (opt.cmp("progressive") == 0) { - jpegProgressive = false; - if (value.cmp("y") == 0) { - jpegProgressive = true; - } else if (value.cmp("n") != 0) { - fprintf(stderr, "jpeg progressive option must be \"y\" or \"n\"\n"); - return false; - } - } else if (opt.cmp("optimize") == 0 || opt.cmp("optimise") == 0) { - jpegOptimize = false; - if (value.cmp("y") == 0) { - jpegOptimize = true; - } else if (value.cmp("n") != 0) { - fprintf(stderr, "jpeg optimize option must be \"y\" or \"n\"\n"); - return false; - } - } else { - fprintf(stderr, "Unknown jpeg option \"%s\"\n", opt.c_str()); - return false; - } - } - return true; + return true; } -static auto annotDisplayDecideCbk = [](Annot *annot, void *user_data) { - return !hideAnnotations; -}; +static auto annotDisplayDecideCbk = [](Annot *annot, void *user_data) { return !hideAnnotations; }; -static void savePageSlice(PDFDoc *doc, - SplashOutputDev *splashOut, - int pg, int x, int y, int w, int h, - double pg_w, double pg_h, - char *ppmFile) { - if (w == 0) w = (int)ceil(pg_w); - if (h == 0) h = (int)ceil(pg_h); - w = (x+w > pg_w ? (int)ceil(pg_w-x) : w); - h = (y+h > pg_h ? (int)ceil(pg_h-y) : h); - doc->displayPageSlice(splashOut, - pg, x_resolution, y_resolution, - 0, - !useCropBox, false, false, - x, y, w, h, - nullptr, nullptr, annotDisplayDecideCbk, nullptr - ); - - SplashBitmap *bitmap = splashOut->getBitmap(); - - SplashBitmap::WriteImgParams params; - params.jpegQuality = jpegQuality; - params.jpegProgressive = jpegProgressive; - params.jpegOptimize = jpegOptimize; - params.tiffCompression.Set(TiffCompressionStr); - - if (ppmFile != nullptr) { - if (png) { - bitmap->writeImgFile(splashFormatPng, ppmFile, x_resolution, y_resolution); - } else if (jpeg) { - bitmap->writeImgFile(splashFormatJpeg, ppmFile, x_resolution, y_resolution, ¶ms); - } else if (jpegcmyk) { - bitmap->writeImgFile(splashFormatJpegCMYK, ppmFile, x_resolution, y_resolution, ¶ms); - } else if (tiff) { - bitmap->writeImgFile(splashFormatTiff, ppmFile, x_resolution, y_resolution, ¶ms); +static void savePageSlice(PDFDoc *doc, SplashOutputDev *splashOut, int pg, int x, int y, int w, int h, double pg_w, double pg_h, char *ppmFile) +{ + if (w == 0) + w = (int)ceil(pg_w); + if (h == 0) + h = (int)ceil(pg_h); + w = (x + w > pg_w ? (int)ceil(pg_w - x) : w); + h = (y + h > pg_h ? (int)ceil(pg_h - y) : h); + doc->displayPageSlice(splashOut, pg, x_resolution, y_resolution, 0, !useCropBox, false, false, x, y, w, h, nullptr, nullptr, annotDisplayDecideCbk, nullptr); + + SplashBitmap *bitmap = splashOut->getBitmap(); + + SplashBitmap::WriteImgParams params; + params.jpegQuality = jpegQuality; + params.jpegProgressive = jpegProgressive; + params.jpegOptimize = jpegOptimize; + params.tiffCompression.Set(TiffCompressionStr); + + if (ppmFile != nullptr) { + if (png) { + bitmap->writeImgFile(splashFormatPng, ppmFile, x_resolution, y_resolution); + } else if (jpeg) { + bitmap->writeImgFile(splashFormatJpeg, ppmFile, x_resolution, y_resolution, ¶ms); + } else if (jpegcmyk) { + bitmap->writeImgFile(splashFormatJpegCMYK, ppmFile, x_resolution, y_resolution, ¶ms); + } else if (tiff) { + bitmap->writeImgFile(splashFormatTiff, ppmFile, x_resolution, y_resolution, ¶ms); + } else { + bitmap->writePNMFile(ppmFile); + } } else { - bitmap->writePNMFile(ppmFile); - } - } else { #ifdef _WIN32 - setmode(fileno(stdout), O_BINARY); + setmode(fileno(stdout), O_BINARY); #endif - if (png) { - bitmap->writeImgFile(splashFormatPng, stdout, x_resolution, y_resolution); - } else if (jpeg) { - bitmap->writeImgFile(splashFormatJpeg, stdout, x_resolution, y_resolution, ¶ms); - } else if (tiff) { - bitmap->writeImgFile(splashFormatTiff, stdout, x_resolution, y_resolution, ¶ms); - } else { - bitmap->writePNMFile(stdout); + if (png) { + bitmap->writeImgFile(splashFormatPng, stdout, x_resolution, y_resolution); + } else if (jpeg) { + bitmap->writeImgFile(splashFormatJpeg, stdout, x_resolution, y_resolution, ¶ms); + } else if (tiff) { + bitmap->writeImgFile(splashFormatTiff, stdout, x_resolution, y_resolution, ¶ms); + } else { + bitmap->writePNMFile(stdout); + } } - } } #ifdef UTILS_USE_PTHREADS -struct PageJob { - PDFDoc *doc; - int pg; - - double pg_w, pg_h; - SplashColor* paperColor; - - char *ppmFile; +struct PageJob +{ + PDFDoc *doc; + int pg; + + double pg_w, pg_h; + SplashColor *paperColor; + + char *ppmFile; }; static std::deque<PageJob> pageJobQueue; static pthread_mutex_t pageJobMutex = PTHREAD_MUTEX_INITIALIZER; -static void processPageJobs() { - while(true) { - // pop the next job or exit if queue is empty - pthread_mutex_lock(&pageJobMutex); - - if(pageJobQueue.empty()) { - pthread_mutex_unlock(&pageJobMutex); - return; +static void processPageJobs() +{ + while (true) { + // pop the next job or exit if queue is empty + pthread_mutex_lock(&pageJobMutex); + + if (pageJobQueue.empty()) { + pthread_mutex_unlock(&pageJobMutex); + return; + } + + PageJob pageJob = pageJobQueue.front(); + pageJobQueue.pop_front(); + + pthread_mutex_unlock(&pageJobMutex); + + // process the job + SplashOutputDev *splashOut = new SplashOutputDev(mono ? splashModeMono1 : gray ? splashModeMono8 : (jpegcmyk || overprint) ? splashModeDeviceN8 : splashModeRGB8, 4, false, *pageJob.paperColor, true, thinLineMode); + splashOut->setFontAntialias(fontAntialias); + splashOut->setVectorAntialias(vectorAntialias); + splashOut->setEnableFreeType(enableFreeType); +# ifdef USE_CMS + splashOut->setDisplayProfile(displayprofile); +# endif + splashOut->startDoc(pageJob.doc); + + savePageSlice(pageJob.doc, splashOut, pageJob.pg, x, y, w, h, pageJob.pg_w, pageJob.pg_h, pageJob.ppmFile); + + delete splashOut; + delete[] pageJob.ppmFile; } - - PageJob pageJob = pageJobQueue.front(); - pageJobQueue.pop_front(); - - pthread_mutex_unlock(&pageJobMutex); - - // process the job - SplashOutputDev *splashOut = new SplashOutputDev(mono ? splashModeMono1 : - gray ? splashModeMono8 : - (jpegcmyk || overprint) ? splashModeDeviceN8 : - splashModeRGB8, 4, false, *pageJob.paperColor, true, thinLineMode); - splashOut->setFontAntialias(fontAntialias); - splashOut->setVectorAntialias(vectorAntialias); - splashOut->setEnableFreeType(enableFreeType); -#ifdef USE_CMS - splashOut->setDisplayProfile(displayprofile); -#endif - splashOut->startDoc(pageJob.doc); - - savePageSlice(pageJob.doc, splashOut, pageJob.pg, x, y, w, h, pageJob.pg_w, pageJob.pg_h, pageJob.ppmFile); - - delete splashOut; - delete[] pageJob.ppmFile; - } } #endif // UTILS_USE_PTHREADS -int main(int argc, char *argv[]) { - PDFDoc *doc; - GooString *fileName = nullptr; - char *ppmRoot = nullptr; - char *ppmFile; - GooString *ownerPW, *userPW; - SplashColor paperColor; +int main(int argc, char *argv[]) +{ + PDFDoc *doc; + GooString *fileName = nullptr; + char *ppmRoot = nullptr; + char *ppmFile; + GooString *ownerPW, *userPW; + SplashColor paperColor; #ifndef UTILS_USE_PTHREADS - SplashOutputDev *splashOut; + SplashOutputDev *splashOut; #else - pthread_t* jobs; + pthread_t *jobs; #endif // UTILS_USE_PTHREADS - bool ok; - int exitCode; - int pg, pg_num_len; - double pg_w, pg_h; + bool ok; + int exitCode; + int pg, pg_num_len; + double pg_w, pg_h; #ifdef USE_CMS - cmsColorSpaceSignature displayprofilecolorspace; + cmsColorSpaceSignature displayprofilecolorspace; #endif - Win32Console win32Console(&argc, &argv); - exitCode = 99; - - // parse args - ok = parseArgs(argDesc, &argc, argv); - if (mono && gray) { - ok = false; - } - if ( resolution != 0.0 && - (x_resolution == 150.0 || - y_resolution == 150.0)) { - x_resolution = resolution; - y_resolution = resolution; - } - if (!ok || argc > 3 || printVersion || printHelp) { - fprintf(stderr, "pdftoppm version %s\n", PACKAGE_VERSION); - fprintf(stderr, "%s\n", popplerCopyright); - fprintf(stderr, "%s\n", xpdfCopyright); - if (!printVersion) { - printUsage("pdftoppm", "[PDF-file [PPM-file-prefix]]", argDesc); + Win32Console win32Console(&argc, &argv); + exitCode = 99; + + // parse args + ok = parseArgs(argDesc, &argc, argv); + if (mono && gray) { + ok = false; } - if (printVersion || printHelp) - exitCode = 0; - goto err0; - } - if (argc > 1) fileName = new GooString(argv[1]); - if (argc == 3) ppmRoot = argv[2]; - - if (antialiasStr[0]) { - if (!GlobalParams::parseYesNo2(antialiasStr, &fontAntialias)) { - fprintf(stderr, "Bad '-aa' value on command line\n"); + if (resolution != 0.0 && (x_resolution == 150.0 || y_resolution == 150.0)) { + x_resolution = resolution; + y_resolution = resolution; } - } - if (vectorAntialiasStr[0]) { - if (!GlobalParams::parseYesNo2(vectorAntialiasStr, &vectorAntialias)) { - fprintf(stderr, "Bad '-aaVector' value on command line\n"); + if (!ok || argc > 3 || printVersion || printHelp) { + fprintf(stderr, "pdftoppm version %s\n", PACKAGE_VERSION); + fprintf(stderr, "%s\n", popplerCopyright); + fprintf(stderr, "%s\n", xpdfCopyright); + if (!printVersion) { + printUsage("pdftoppm", "[PDF-file [PPM-file-prefix]]", argDesc); + } + if (printVersion || printHelp) + exitCode = 0; + goto err0; } - } - - if (jpegOpt.getLength() > 0) { - if (!jpeg) - fprintf(stderr, "Warning: -jpegopt only valid with jpeg output.\n"); - parseJpegOptions(); - } - - // read config file - globalParams = std::make_unique<GlobalParams>(); - if (enableFreeTypeStr[0]) { - if (!GlobalParams::parseYesNo2(enableFreeTypeStr, &enableFreeType)) { - fprintf(stderr, "Bad '-freetype' value on command line\n"); + if (argc > 1) + fileName = new GooString(argv[1]); + if (argc == 3) + ppmRoot = argv[2]; + + if (antialiasStr[0]) { + if (!GlobalParams::parseYesNo2(antialiasStr, &fontAntialias)) { + fprintf(stderr, "Bad '-aa' value on command line\n"); + } } - } - if (thinLineModeStr[0]) { - if (strcmp(thinLineModeStr, "solid") == 0) { - thinLineMode = splashThinLineSolid; - } else if (strcmp(thinLineModeStr, "shape") == 0) { - thinLineMode = splashThinLineShape; - } else if (strcmp(thinLineModeStr, "none") != 0) { - fprintf(stderr, "Bad '-thinlinemode' value on command line\n"); + if (vectorAntialiasStr[0]) { + if (!GlobalParams::parseYesNo2(vectorAntialiasStr, &vectorAntialias)) { + fprintf(stderr, "Bad '-aaVector' value on command line\n"); + } } - } - if (quiet) { - globalParams->setErrQuiet(quiet); - } - - // open PDF file - if (ownerPassword[0]) { - ownerPW = new GooString(ownerPassword); - } else { - ownerPW = nullptr; - } - if (userPassword[0]) { - userPW = new GooString(userPassword); - } else { - userPW = nullptr; - } - - if (fileName == nullptr) { - fileName = new GooString("fd://0"); - } - if (fileName->cmp("-") == 0) { - delete fileName; - fileName = new GooString("fd://0"); - } - doc = PDFDocFactory().createPDFDoc(*fileName, ownerPW, userPW); - delete fileName; - - if (userPW) { - delete userPW; - } - if (ownerPW) { - delete ownerPW; - } - if (!doc->isOk()) { - exitCode = 1; - goto err1; - } - - // get page range - if (firstPage < 1) - firstPage = 1; - if (singleFile && lastPage < 1) - lastPage = firstPage; - if (lastPage < 1 || lastPage > doc->getNumPages()) - lastPage = doc->getNumPages(); - if (lastPage < firstPage) { - fprintf(stderr, - "Wrong page range given: the first page (%d) can not be after the last page (%d).\n", - firstPage, lastPage); - goto err1; - } - - // If our page range selection and document size indicate we're only - // outputting a single page, ensure that even/odd page selection doesn't - // filter out that single page. - if (firstPage == lastPage && - ((printOnlyEven && firstPage % 2 == 1) || - (printOnlyOdd && firstPage % 2 == 0))) { - fprintf(stderr, "Invalid even/odd page selection, no pages match criteria.\n"); - goto err1; - } - - - if (singleFile && firstPage < lastPage) { - if (!quiet) { - fprintf(stderr, - "Warning: Single file will write only the first of the %d pages.\n", - lastPage + 1 - firstPage); + + if (jpegOpt.getLength() > 0) { + if (!jpeg) + fprintf(stderr, "Warning: -jpegopt only valid with jpeg output.\n"); + parseJpegOptions(); } - lastPage = firstPage; - } - - // write PPM files - if (jpegcmyk || overprint) { - globalParams->setOverprintPreview(true); - splashClearColor(paperColor); - } else { - paperColor[0] = 255; - paperColor[1] = 255; - paperColor[2] = 255; - } -#ifdef USE_CMS - if (!displayprofilename.toStr().empty()) { - displayprofile = make_GfxLCMSProfilePtr(cmsOpenProfileFromFile(displayprofilename.c_str(),"r")); - if (!displayprofile) { - fprintf(stderr, "Could not open the ICC profile \"%s\".\n", displayprofilename.c_str()); - goto err1; + // read config file + globalParams = std::make_unique<GlobalParams>(); + if (enableFreeTypeStr[0]) { + if (!GlobalParams::parseYesNo2(enableFreeTypeStr, &enableFreeType)) { + fprintf(stderr, "Bad '-freetype' value on command line\n"); + } } - if(!cmsIsIntentSupported(displayprofile.get(), INTENT_RELATIVE_COLORIMETRIC, LCMS_USED_AS_OUTPUT) && - !cmsIsIntentSupported(displayprofile.get(), INTENT_ABSOLUTE_COLORIMETRIC, LCMS_USED_AS_OUTPUT) && - !cmsIsIntentSupported(displayprofile.get(), INTENT_SATURATION, LCMS_USED_AS_OUTPUT) && - !cmsIsIntentSupported(displayprofile.get(), INTENT_PERCEPTUAL, LCMS_USED_AS_OUTPUT)) { - fprintf(stderr, "ICC profile \"%s\" is not an output profile.\n", displayprofilename.c_str()); - goto err1; + if (thinLineModeStr[0]) { + if (strcmp(thinLineModeStr, "solid") == 0) { + thinLineMode = splashThinLineSolid; + } else if (strcmp(thinLineModeStr, "shape") == 0) { + thinLineMode = splashThinLineShape; + } else if (strcmp(thinLineModeStr, "none") != 0) { + fprintf(stderr, "Bad '-thinlinemode' value on command line\n"); + } } - displayprofilecolorspace = cmsGetColorSpace(displayprofile.get()); - if (jpegcmyk || overprint) { - if (displayprofilecolorspace != cmsSigCmykData) { - fprintf(stderr, "Warning: Supplied ICC profile \"%s\" is not a CMYK profile.\n", - displayprofilename.c_str()); - } - } else if (mono || gray) { - if (displayprofilecolorspace != cmsSigGrayData) { - fprintf(stderr, "Warning: Supplied ICC profile \"%s\" is not a monochrome profile.\n", - displayprofilename.c_str()); - } + if (quiet) { + globalParams->setErrQuiet(quiet); + } + + // open PDF file + if (ownerPassword[0]) { + ownerPW = new GooString(ownerPassword); } else { - if (displayprofilecolorspace != cmsSigRgbData) { - fprintf(stderr, "Warning: Supplied ICC profile \"%s\" is not a RGB profile.\n", - displayprofilename.c_str()); - } + ownerPW = nullptr; + } + if (userPassword[0]) { + userPW = new GooString(userPassword); + } else { + userPW = nullptr; } - } -#endif - -#ifndef UTILS_USE_PTHREADS - splashOut = new SplashOutputDev(mono ? splashModeMono1 : - gray ? splashModeMono8 : - (jpegcmyk || overprint) ? splashModeDeviceN8 : - splashModeRGB8, 4, - false, paperColor, true, thinLineMode); + if (fileName == nullptr) { + fileName = new GooString("fd://0"); + } + if (fileName->cmp("-") == 0) { + delete fileName; + fileName = new GooString("fd://0"); + } + doc = PDFDocFactory().createPDFDoc(*fileName, ownerPW, userPW); + delete fileName; + if (userPW) { + delete userPW; + } + if (ownerPW) { + delete ownerPW; + } + if (!doc->isOk()) { + exitCode = 1; + goto err1; + } + // get page range + if (firstPage < 1) + firstPage = 1; + if (singleFile && lastPage < 1) + lastPage = firstPage; + if (lastPage < 1 || lastPage > doc->getNumPages()) + lastPage = doc->getNumPages(); + if (lastPage < firstPage) { + fprintf(stderr, "Wrong page range given: the first page (%d) can not be after the last page (%d).\n", firstPage, lastPage); + goto err1; + } - splashOut->setFontAntialias(fontAntialias); - splashOut->setVectorAntialias(vectorAntialias); - splashOut->setEnableFreeType(enableFreeType); -#ifdef USE_CMS - splashOut->setDisplayProfile(displayprofile); -#endif - splashOut->startDoc(doc); - -#endif // UTILS_USE_PTHREADS - - if (sz != 0) param_w = param_h = sz; - pg_num_len = numberOfCharacters(doc->getNumPages()); - for (pg = firstPage; pg <= lastPage; ++pg) { - if (printOnlyEven && pg % 2 == 1) continue; - if (printOnlyOdd && pg % 2 == 0) continue; - if (useCropBox) { - pg_w = doc->getPageCropWidth(pg); - pg_h = doc->getPageCropHeight(pg); - } else { - pg_w = doc->getPageMediaWidth(pg); - pg_h = doc->getPageMediaHeight(pg); + // If our page range selection and document size indicate we're only + // outputting a single page, ensure that even/odd page selection doesn't + // filter out that single page. + if (firstPage == lastPage && ((printOnlyEven && firstPage % 2 == 1) || (printOnlyOdd && firstPage % 2 == 0))) { + fprintf(stderr, "Invalid even/odd page selection, no pages match criteria.\n"); + goto err1; } - if (scaleDimensionBeforeRotation && needToRotate(doc->getPageRotate(pg))) - std::swap(pg_w, pg_h); + if (singleFile && firstPage < lastPage) { + if (!quiet) { + fprintf(stderr, "Warning: Single file will write only the first of the %d pages.\n", lastPage + 1 - firstPage); + } + lastPage = firstPage; + } - if (scaleTo != 0) { - resolution = (72.0 * scaleTo) / (pg_w > pg_h ? pg_w : pg_h); - x_resolution = y_resolution = resolution; + // write PPM files + if (jpegcmyk || overprint) { + globalParams->setOverprintPreview(true); + splashClearColor(paperColor); } else { - if (x_scaleTo > 0) { - x_resolution = (72.0 * x_scaleTo) / pg_w; - if (y_scaleTo == -1) - y_resolution = x_resolution; - } - if (y_scaleTo > 0) { - y_resolution = (72.0 * y_scaleTo) / pg_h; - if (x_scaleTo == -1) - x_resolution = y_resolution; - } + paperColor[0] = 255; + paperColor[1] = 255; + paperColor[2] = 255; } - pg_w = pg_w * (x_resolution / 72.0); - pg_h = pg_h * (y_resolution / 72.0); - - if (!scaleDimensionBeforeRotation && needToRotate(doc->getPageRotate(pg))) - std::swap(pg_w, pg_h); - - if (ppmRoot != nullptr) { - const char *ext = png ? "png" : (jpeg || jpegcmyk) ? "jpg" : tiff ? "tif" : mono ? "pbm" : gray ? "pgm" : "ppm"; - if (singleFile && !forceNum ) { - ppmFile = new char[strlen(ppmRoot) + 1 + strlen(ext) + 1]; - sprintf(ppmFile, "%s.%s", ppmRoot, ext); - } else { - ppmFile = new char[strlen(ppmRoot) + 1 + pg_num_len + 1 + strlen(ext) + 1]; - sprintf(ppmFile, "%s%s%0*d.%s", ppmRoot, sep, pg_num_len, pg, ext); - } - } else { - ppmFile = nullptr; + +#ifdef USE_CMS + if (!displayprofilename.toStr().empty()) { + displayprofile = make_GfxLCMSProfilePtr(cmsOpenProfileFromFile(displayprofilename.c_str(), "r")); + if (!displayprofile) { + fprintf(stderr, "Could not open the ICC profile \"%s\".\n", displayprofilename.c_str()); + goto err1; + } + if (!cmsIsIntentSupported(displayprofile.get(), INTENT_RELATIVE_COLORIMETRIC, LCMS_USED_AS_OUTPUT) && !cmsIsIntentSupported(displayprofile.get(), INTENT_ABSOLUTE_COLORIMETRIC, LCMS_USED_AS_OUTPUT) + && !cmsIsIntentSupported(displayprofile.get(), INTENT_SATURATION, LCMS_USED_AS_OUTPUT) && !cmsIsIntentSupported(displayprofile.get(), INTENT_PERCEPTUAL, LCMS_USED_AS_OUTPUT)) { + fprintf(stderr, "ICC profile \"%s\" is not an output profile.\n", displayprofilename.c_str()); + goto err1; + } + displayprofilecolorspace = cmsGetColorSpace(displayprofile.get()); + if (jpegcmyk || overprint) { + if (displayprofilecolorspace != cmsSigCmykData) { + fprintf(stderr, "Warning: Supplied ICC profile \"%s\" is not a CMYK profile.\n", displayprofilename.c_str()); + } + } else if (mono || gray) { + if (displayprofilecolorspace != cmsSigGrayData) { + fprintf(stderr, "Warning: Supplied ICC profile \"%s\" is not a monochrome profile.\n", displayprofilename.c_str()); + } + } else { + if (displayprofilecolorspace != cmsSigRgbData) { + fprintf(stderr, "Warning: Supplied ICC profile \"%s\" is not a RGB profile.\n", displayprofilename.c_str()); + } + } } +#endif + +#ifndef UTILS_USE_PTHREADS + + splashOut = new SplashOutputDev(mono ? splashModeMono1 : gray ? splashModeMono8 : (jpegcmyk || overprint) ? splashModeDeviceN8 : splashModeRGB8, 4, false, paperColor, true, thinLineMode); + + splashOut->setFontAntialias(fontAntialias); + splashOut->setVectorAntialias(vectorAntialias); + splashOut->setEnableFreeType(enableFreeType); +# ifdef USE_CMS + splashOut->setDisplayProfile(displayprofile); +# endif + splashOut->startDoc(doc); + +#endif // UTILS_USE_PTHREADS + + if (sz != 0) + param_w = param_h = sz; + pg_num_len = numberOfCharacters(doc->getNumPages()); + for (pg = firstPage; pg <= lastPage; ++pg) { + if (printOnlyEven && pg % 2 == 1) + continue; + if (printOnlyOdd && pg % 2 == 0) + continue; + if (useCropBox) { + pg_w = doc->getPageCropWidth(pg); + pg_h = doc->getPageCropHeight(pg); + } else { + pg_w = doc->getPageMediaWidth(pg); + pg_h = doc->getPageMediaHeight(pg); + } + + if (scaleDimensionBeforeRotation && needToRotate(doc->getPageRotate(pg))) + std::swap(pg_w, pg_h); + + if (scaleTo != 0) { + resolution = (72.0 * scaleTo) / (pg_w > pg_h ? pg_w : pg_h); + x_resolution = y_resolution = resolution; + } else { + if (x_scaleTo > 0) { + x_resolution = (72.0 * x_scaleTo) / pg_w; + if (y_scaleTo == -1) + y_resolution = x_resolution; + } + if (y_scaleTo > 0) { + y_resolution = (72.0 * y_scaleTo) / pg_h; + if (x_scaleTo == -1) + x_resolution = y_resolution; + } + } + pg_w = pg_w * (x_resolution / 72.0); + pg_h = pg_h * (y_resolution / 72.0); + + if (!scaleDimensionBeforeRotation && needToRotate(doc->getPageRotate(pg))) + std::swap(pg_w, pg_h); + + if (ppmRoot != nullptr) { + const char *ext = png ? "png" : (jpeg || jpegcmyk) ? "jpg" : tiff ? "tif" : mono ? "pbm" : gray ? "pgm" : "ppm"; + if (singleFile && !forceNum) { + ppmFile = new char[strlen(ppmRoot) + 1 + strlen(ext) + 1]; + sprintf(ppmFile, "%s.%s", ppmRoot, ext); + } else { + ppmFile = new char[strlen(ppmRoot) + 1 + pg_num_len + 1 + strlen(ext) + 1]; + sprintf(ppmFile, "%s%s%0*d.%s", ppmRoot, sep, pg_num_len, pg, ext); + } + } else { + ppmFile = nullptr; + } #ifndef UTILS_USE_PTHREADS - // process job in main thread - savePageSlice(doc, splashOut, pg, param_x, param_y, param_w, param_h, pg_w, pg_h, ppmFile); - - delete[] ppmFile; + // process job in main thread + savePageSlice(doc, splashOut, pg, param_x, param_y, param_w, param_h, pg_w, pg_h, ppmFile); + + delete[] ppmFile; #else - - // queue job for worker threads - PageJob pageJob = { - .doc = doc, - .pg = pg, - - .pg_w = pg_w, .pg_h = pg_h, - - .paperColor = &paperColor, - - .ppmFile = ppmFile - }; - - pageJobQueue.push_back(pageJob); - + + // queue job for worker threads + PageJob pageJob = { .doc = doc, + .pg = pg, + + .pg_w = pg_w, + .pg_h = pg_h, + + .paperColor = &paperColor, + + .ppmFile = ppmFile }; + + pageJobQueue.push_back(pageJob); + #endif // UTILS_USE_PTHREADS - } + } #ifndef UTILS_USE_PTHREADS - delete splashOut; + delete splashOut; #else - - // spawn worker threads and wait on them - jobs = (pthread_t*)malloc(numberOfJobs * sizeof(pthread_t)); - - for(int i=0; i < numberOfJobs; ++i) { - if(pthread_create(&jobs[i], NULL, (void* (*)(void*))processPageJobs, NULL) != 0) { - fprintf(stderr, "pthread_create() failed with errno: %d\n", errno); - exit(EXIT_FAILURE); + + // spawn worker threads and wait on them + jobs = (pthread_t *)malloc(numberOfJobs * sizeof(pthread_t)); + + for (int i = 0; i < numberOfJobs; ++i) { + if (pthread_create(&jobs[i], NULL, (void *(*)(void *))processPageJobs, NULL) != 0) { + fprintf(stderr, "pthread_create() failed with errno: %d\n", errno); + exit(EXIT_FAILURE); + } } - } - - for(int i=0; i < numberOfJobs; ++i) { - if(pthread_join(jobs[i], NULL) != 0) { - fprintf(stderr, "pthread_join() failed with errno: %d\n", errno); - exit(EXIT_FAILURE); + + for (int i = 0; i < numberOfJobs; ++i) { + if (pthread_join(jobs[i], NULL) != 0) { + fprintf(stderr, "pthread_join() failed with errno: %d\n", errno); + exit(EXIT_FAILURE); + } } - } - free(jobs); - + free(jobs); + #endif // UTILS_USE_PTHREADS - exitCode = 0; + exitCode = 0; - // clean up - err1: - delete doc; - err0: + // clean up +err1: + delete doc; +err0: - return exitCode; + return exitCode; } diff --git a/utils/pdftops.cc b/utils/pdftops.cc index 097b670e..7aa794e1 100644 --- a/utils/pdftops.cc +++ b/utils/pdftops.cc @@ -55,28 +55,28 @@ #include "Error.h" #include "Win32Console.h" -static bool setPSPaperSize(char *size, int &psPaperWidth, int &psPaperHeight) { - if (!strcmp(size, "match")) { - psPaperWidth = psPaperHeight = -1; - } else if (!strcmp(size, "letter")) { - psPaperWidth = 612; - psPaperHeight = 792; - } else if (!strcmp(size, "legal")) { - psPaperWidth = 612; - psPaperHeight = 1008; - } else if (!strcmp(size, "A4")) { - psPaperWidth = 595; - psPaperHeight = 842; - } else if (!strcmp(size, "A3")) { - psPaperWidth = 842; - psPaperHeight = 1190; - } else { - return false; - } - return true; +static bool setPSPaperSize(char *size, int &psPaperWidth, int &psPaperHeight) +{ + if (!strcmp(size, "match")) { + psPaperWidth = psPaperHeight = -1; + } else if (!strcmp(size, "letter")) { + psPaperWidth = 612; + psPaperHeight = 792; + } else if (!strcmp(size, "legal")) { + psPaperWidth = 612; + psPaperHeight = 1008; + } else if (!strcmp(size, "A4")) { + psPaperWidth = 595; + psPaperHeight = 842; + } else if (!strcmp(size, "A3")) { + psPaperWidth = 842; + psPaperHeight = 1190; + } else { + return false; + } + return true; } - static int firstPage = 1; static int lastPage = 0; static bool level1 = false; @@ -118,340 +118,281 @@ static bool printVersion = false; static bool printHelp = false; static bool overprint = false; -static const ArgDesc argDesc[] = { - {"-f", argInt, &firstPage, 0, - "first page to print"}, - {"-l", argInt, &lastPage, 0, - "last page to print"}, - {"-level1", argFlag, &level1, 0, - "generate Level 1 PostScript"}, - {"-level1sep", argFlag, &level1Sep, 0, - "generate Level 1 separable PostScript"}, - {"-level2", argFlag, &level2, 0, - "generate Level 2 PostScript"}, - {"-level2sep", argFlag, &level2Sep, 0, - "generate Level 2 separable PostScript"}, - {"-level3", argFlag, &level3, 0, - "generate Level 3 PostScript"}, - {"-level3sep", argFlag, &level3Sep, 0, - "generate Level 3 separable PostScript"}, - {"-origpagesizes",argFlag, &origPageSizes,0, - "conserve original page sizes"}, - {"-eps", argFlag, &doEPS, 0, - "generate Encapsulated PostScript (EPS)"}, - {"-form", argFlag, &doForm, 0, - "generate a PostScript form"}, +static const ArgDesc argDesc[] = { { "-f", argInt, &firstPage, 0, "first page to print" }, + { "-l", argInt, &lastPage, 0, "last page to print" }, + { "-level1", argFlag, &level1, 0, "generate Level 1 PostScript" }, + { "-level1sep", argFlag, &level1Sep, 0, "generate Level 1 separable PostScript" }, + { "-level2", argFlag, &level2, 0, "generate Level 2 PostScript" }, + { "-level2sep", argFlag, &level2Sep, 0, "generate Level 2 separable PostScript" }, + { "-level3", argFlag, &level3, 0, "generate Level 3 PostScript" }, + { "-level3sep", argFlag, &level3Sep, 0, "generate Level 3 separable PostScript" }, + { "-origpagesizes", argFlag, &origPageSizes, 0, "conserve original page sizes" }, + { "-eps", argFlag, &doEPS, 0, "generate Encapsulated PostScript (EPS)" }, + { "-form", argFlag, &doForm, 0, "generate a PostScript form" }, #ifdef OPI_SUPPORT - {"-opi", argFlag, &doOPI, 0, - "generate OPI comments"}, + { "-opi", argFlag, &doOPI, 0, "generate OPI comments" }, #endif - {"-r", argInt, &splashResolution, 0, - "resolution for rasterization, in DPI (default is 300)"}, - {"-binary", argFlag, &psBinary, 0, - "write binary data in Level 1 PostScript"}, - {"-noembt1", argFlag, &noEmbedT1Fonts, 0, - "don't embed Type 1 fonts"}, - {"-noembtt", argFlag, &noEmbedTTFonts, 0, - "don't embed TrueType fonts"}, - {"-noembcidps", argFlag, &noEmbedCIDPSFonts, 0, - "don't embed CID PostScript fonts"}, - {"-noembcidtt", argFlag, &noEmbedCIDTTFonts, 0, - "don't embed CID TrueType fonts"}, - {"-passfonts", argFlag, &fontPassthrough,0, - "don't substitute missing fonts"}, - {"-aaRaster", argString, rasterAntialiasStr, sizeof(rasterAntialiasStr), - "enable anti-aliasing on rasterization: yes, no"}, - {"-rasterize", argString, forceRasterizeStr, sizeof(forceRasterizeStr), - "control rasterization: always, never, whenneeded"}, - {"-optimizecolorspace", argFlag, &optimizeColorSpace,0, - "convert gray RGB images to gray color space"}, - {"-passlevel1customcolor", argFlag, &passLevel1CustomColor, 0, - "pass custom color in level1sep"}, - {"-preload", argFlag, &preload, 0, - "preload images and forms"}, - {"-paper", argString, paperSize, sizeof(paperSize), - "paper size (letter, legal, A4, A3, match)"}, - {"-paperw", argInt, &paperWidth, 0, - "paper width, in points"}, - {"-paperh", argInt, &paperHeight, 0, - "paper height, in points"}, - {"-nocrop", argFlag, &noCrop, 0, - "don't crop pages to CropBox"}, - {"-expand", argFlag, &expand, 0, - "expand pages smaller than the paper size"}, - {"-noshrink", argFlag, &noShrink, 0, - "don't shrink pages larger than the paper size"}, - {"-nocenter", argFlag, &noCenter, 0, - "don't center pages smaller than the paper size"}, - {"-duplex", argFlag, &duplex, 0, - "enable duplex printing"}, - {"-opw", argString, ownerPassword, sizeof(ownerPassword), - "owner password (for encrypted files)"}, - {"-upw", argString, userPassword, sizeof(userPassword), - "user password (for encrypted files)"}, - {"-overprint",argFlag, &overprint, 0, - "enable overprint"}, - {"-q", argFlag, &quiet, 0, - "don't print any messages or errors"}, - {"-v", argFlag, &printVersion, 0, - "print copyright and version info"}, - {"-h", argFlag, &printHelp, 0, - "print usage information"}, - {"-help", argFlag, &printHelp, 0, - "print usage information"}, - {"--help", argFlag, &printHelp, 0, - "print usage information"}, - {"-?", argFlag, &printHelp, 0, - "print usage information"}, - {} -}; + { "-r", argInt, &splashResolution, 0, "resolution for rasterization, in DPI (default is 300)" }, + { "-binary", argFlag, &psBinary, 0, "write binary data in Level 1 PostScript" }, + { "-noembt1", argFlag, &noEmbedT1Fonts, 0, "don't embed Type 1 fonts" }, + { "-noembtt", argFlag, &noEmbedTTFonts, 0, "don't embed TrueType fonts" }, + { "-noembcidps", argFlag, &noEmbedCIDPSFonts, 0, "don't embed CID PostScript fonts" }, + { "-noembcidtt", argFlag, &noEmbedCIDTTFonts, 0, "don't embed CID TrueType fonts" }, + { "-passfonts", argFlag, &fontPassthrough, 0, "don't substitute missing fonts" }, + { "-aaRaster", argString, rasterAntialiasStr, sizeof(rasterAntialiasStr), "enable anti-aliasing on rasterization: yes, no" }, + { "-rasterize", argString, forceRasterizeStr, sizeof(forceRasterizeStr), "control rasterization: always, never, whenneeded" }, + { "-optimizecolorspace", argFlag, &optimizeColorSpace, 0, "convert gray RGB images to gray color space" }, + { "-passlevel1customcolor", argFlag, &passLevel1CustomColor, 0, "pass custom color in level1sep" }, + { "-preload", argFlag, &preload, 0, "preload images and forms" }, + { "-paper", argString, paperSize, sizeof(paperSize), "paper size (letter, legal, A4, A3, match)" }, + { "-paperw", argInt, &paperWidth, 0, "paper width, in points" }, + { "-paperh", argInt, &paperHeight, 0, "paper height, in points" }, + { "-nocrop", argFlag, &noCrop, 0, "don't crop pages to CropBox" }, + { "-expand", argFlag, &expand, 0, "expand pages smaller than the paper size" }, + { "-noshrink", argFlag, &noShrink, 0, "don't shrink pages larger than the paper size" }, + { "-nocenter", argFlag, &noCenter, 0, "don't center pages smaller than the paper size" }, + { "-duplex", argFlag, &duplex, 0, "enable duplex printing" }, + { "-opw", argString, ownerPassword, sizeof(ownerPassword), "owner password (for encrypted files)" }, + { "-upw", argString, userPassword, sizeof(userPassword), "user password (for encrypted files)" }, + { "-overprint", argFlag, &overprint, 0, "enable overprint" }, + { "-q", argFlag, &quiet, 0, "don't print any messages or errors" }, + { "-v", argFlag, &printVersion, 0, "print copyright and version info" }, + { "-h", argFlag, &printHelp, 0, "print usage information" }, + { "-help", argFlag, &printHelp, 0, "print usage information" }, + { "--help", argFlag, &printHelp, 0, "print usage information" }, + { "-?", argFlag, &printHelp, 0, "print usage information" }, + {} }; -int main(int argc, char *argv[]) { - PDFDoc *doc; - GooString *fileName; - GooString *psFileName; - PSLevel level; - PSOutMode mode; - GooString *ownerPW, *userPW; - PSOutputDev *psOut; - bool ok; - int exitCode; - bool rasterAntialias = false; - std::vector<int> pages; +int main(int argc, char *argv[]) +{ + PDFDoc *doc; + GooString *fileName; + GooString *psFileName; + PSLevel level; + PSOutMode mode; + GooString *ownerPW, *userPW; + PSOutputDev *psOut; + bool ok; + int exitCode; + bool rasterAntialias = false; + std::vector<int> pages; - Win32Console win32Console(&argc, &argv); - exitCode = 99; + Win32Console win32Console(&argc, &argv); + exitCode = 99; - // parse args - ok = parseArgs(argDesc, &argc, argv); - if (!ok || argc < 2 || argc > 3 || printVersion || printHelp) { - fprintf(stderr, "pdftops version %s\n", PACKAGE_VERSION); - fprintf(stderr, "%s\n", popplerCopyright); - fprintf(stderr, "%s\n", xpdfCopyright); - if (!printVersion) { - printUsage("pdftops", "<PDF-file> [<PS-file>]", argDesc); + // parse args + ok = parseArgs(argDesc, &argc, argv); + if (!ok || argc < 2 || argc > 3 || printVersion || printHelp) { + fprintf(stderr, "pdftops version %s\n", PACKAGE_VERSION); + fprintf(stderr, "%s\n", popplerCopyright); + fprintf(stderr, "%s\n", xpdfCopyright); + if (!printVersion) { + printUsage("pdftops", "<PDF-file> [<PS-file>]", argDesc); + } + if (printVersion || printHelp) + exit(0); + else + exit(1); + } + if ((level1 ? 1 : 0) + (level1Sep ? 1 : 0) + (level2 ? 1 : 0) + (level2Sep ? 1 : 0) + (level3 ? 1 : 0) + (level3Sep ? 1 : 0) > 1) { + fprintf(stderr, "Error: use only one of the 'level' options.\n"); + exit(1); + } + if ((doEPS ? 1 : 0) + (doForm ? 1 : 0) > 1) { + fprintf(stderr, "Error: use only one of -eps, and -form\n"); + exit(1); } - if (printVersion || printHelp) - exit(0); - else - exit(1); - } - if ((level1 ? 1 : 0) + - (level1Sep ? 1 : 0) + - (level2 ? 1 : 0) + - (level2Sep ? 1 : 0) + - (level3 ? 1 : 0) + - (level3Sep ? 1 : 0) > 1) { - fprintf(stderr, "Error: use only one of the 'level' options.\n"); - exit(1); - } - if ((doEPS ? 1 : 0) + - (doForm ? 1 : 0) > 1) { - fprintf(stderr, "Error: use only one of -eps, and -form\n"); - exit(1); - } - if (level1) { - level = psLevel1; - } else if (level1Sep) { - level = psLevel1Sep; - } else if (level2Sep) { - level = psLevel2Sep; - } else if (level3) { - level = psLevel3; - } else if (level3Sep) { - level = psLevel3Sep; - } else { - level = psLevel2; - } - if (doForm && level < psLevel2) { - fprintf(stderr, "Error: forms are only available with Level 2 output.\n"); - exit(1); - } - mode = doEPS ? psModeEPS - : doForm ? psModeForm - : psModePS; - fileName = new GooString(argv[1]); + if (level1) { + level = psLevel1; + } else if (level1Sep) { + level = psLevel1Sep; + } else if (level2Sep) { + level = psLevel2Sep; + } else if (level3) { + level = psLevel3; + } else if (level3Sep) { + level = psLevel3Sep; + } else { + level = psLevel2; + } + if (doForm && level < psLevel2) { + fprintf(stderr, "Error: forms are only available with Level 2 output.\n"); + exit(1); + } + mode = doEPS ? psModeEPS : doForm ? psModeForm : psModePS; + fileName = new GooString(argv[1]); - // read config file - globalParams = std::make_unique<GlobalParams>(); - if (origPageSizes) { - paperWidth = paperHeight = -1; - } - if (paperSize[0]) { + // read config file + globalParams = std::make_unique<GlobalParams>(); if (origPageSizes) { - fprintf(stderr, "Error: -origpagesizes and -paper may not be used together.\n"); - exit(1); + paperWidth = paperHeight = -1; + } + if (paperSize[0]) { + if (origPageSizes) { + fprintf(stderr, "Error: -origpagesizes and -paper may not be used together.\n"); + exit(1); + } + if (!setPSPaperSize(paperSize, paperWidth, paperHeight)) { + fprintf(stderr, "Invalid paper size\n"); + delete fileName; + goto err0; + } } - if (!setPSPaperSize(paperSize, paperWidth, paperHeight)) { - fprintf(stderr, "Invalid paper size\n"); - delete fileName; - goto err0; + if (overprint) { + globalParams->setOverprintPreview(true); + } + if (expand) { + globalParams->setPSExpandSmaller(true); + } + if (noShrink) { + globalParams->setPSShrinkLarger(false); + } + if (level1 || level1Sep || level2 || level2Sep || level3 || level3Sep) { + globalParams->setPSLevel(level); + } + if (quiet) { + globalParams->setErrQuiet(quiet); } - } - if (overprint) { - globalParams->setOverprintPreview(true); - } - if (expand) { - globalParams->setPSExpandSmaller(true); - } - if (noShrink) { - globalParams->setPSShrinkLarger(false); - } - if (level1 || level1Sep || level2 || level2Sep || level3 || level3Sep) { - globalParams->setPSLevel(level); - } - if (quiet) { - globalParams->setErrQuiet(quiet); - } - // open PDF file - if (ownerPassword[0] != '\001') { - ownerPW = new GooString(ownerPassword); - } else { - ownerPW = nullptr; - } - if (userPassword[0] != '\001') { - userPW = new GooString(userPassword); - } else { - userPW = nullptr; - } - if (fileName->cmp("-") == 0) { - delete fileName; - fileName = new GooString("fd://0"); - } + // open PDF file + if (ownerPassword[0] != '\001') { + ownerPW = new GooString(ownerPassword); + } else { + ownerPW = nullptr; + } + if (userPassword[0] != '\001') { + userPW = new GooString(userPassword); + } else { + userPW = nullptr; + } + if (fileName->cmp("-") == 0) { + delete fileName; + fileName = new GooString("fd://0"); + } - doc = PDFDocFactory().createPDFDoc(*fileName, ownerPW, userPW); + doc = PDFDocFactory().createPDFDoc(*fileName, ownerPW, userPW); - if (userPW) { - delete userPW; - } - if (ownerPW) { - delete ownerPW; - } - if (!doc->isOk()) { - exitCode = 1; - goto err1; - } + if (userPW) { + delete userPW; + } + if (ownerPW) { + delete ownerPW; + } + if (!doc->isOk()) { + exitCode = 1; + goto err1; + } #ifdef ENFORCE_PERMISSIONS - // check for print permission - if (!doc->okToPrint()) { - error(errNotAllowed, -1, "Printing this document is not allowed."); - exitCode = 3; - goto err1; - } + // check for print permission + if (!doc->okToPrint()) { + error(errNotAllowed, -1, "Printing this document is not allowed."); + exitCode = 3; + goto err1; + } #endif - // construct PostScript file name - if (argc == 3) { - psFileName = new GooString(argv[2]); - } else if (fileName->cmp("fd://0") == 0) { - error(errCommandLine, -1, "You have to provide an output filename when reading from stdin."); - goto err1; - } else { - const char *p = fileName->c_str() + fileName->getLength() - 4; - if (!strcmp(p, ".pdf") || !strcmp(p, ".PDF")) { - psFileName = new GooString(fileName->c_str(), - fileName->getLength() - 4); + // construct PostScript file name + if (argc == 3) { + psFileName = new GooString(argv[2]); + } else if (fileName->cmp("fd://0") == 0) { + error(errCommandLine, -1, "You have to provide an output filename when reading from stdin."); + goto err1; } else { - psFileName = fileName->copy(); + const char *p = fileName->c_str() + fileName->getLength() - 4; + if (!strcmp(p, ".pdf") || !strcmp(p, ".PDF")) { + psFileName = new GooString(fileName->c_str(), fileName->getLength() - 4); + } else { + psFileName = fileName->copy(); + } + psFileName->append(doEPS ? ".eps" : ".ps"); } - psFileName->append(doEPS ? ".eps" : ".ps"); - } - // get page range - if (firstPage < 1) { - firstPage = 1; - } - if (lastPage < 1 || lastPage > doc->getNumPages()) { - lastPage = doc->getNumPages(); - } - if (lastPage < firstPage) { - error(errCommandLine, -1, - "Wrong page range given: the first page ({0:d}) can not be after the last page ({1:d}).", - firstPage, lastPage); - goto err2; - } + // get page range + if (firstPage < 1) { + firstPage = 1; + } + if (lastPage < 1 || lastPage > doc->getNumPages()) { + lastPage = doc->getNumPages(); + } + if (lastPage < firstPage) { + error(errCommandLine, -1, "Wrong page range given: the first page ({0:d}) can not be after the last page ({1:d}).", firstPage, lastPage); + goto err2; + } - // check for multi-page EPS or form - if ((doEPS || doForm) && firstPage != lastPage) { - error(errCommandLine, -1, "EPS and form files can only contain one page."); - goto err2; - } + // check for multi-page EPS or form + if ((doEPS || doForm) && firstPage != lastPage) { + error(errCommandLine, -1, "EPS and form files can only contain one page."); + goto err2; + } - for (int i = firstPage; i <= lastPage; ++i) { - pages.push_back(i); - } + for (int i = firstPage; i <= lastPage; ++i) { + pages.push_back(i); + } - // write PostScript file - psOut = new PSOutputDev(psFileName->c_str(), doc, - nullptr, pages, mode, - paperWidth, - paperHeight, - noCrop, - duplex); - if (noCenter) { - psOut->setPSCenter(false); - } + // write PostScript file + psOut = new PSOutputDev(psFileName->c_str(), doc, nullptr, pages, mode, paperWidth, paperHeight, noCrop, duplex); + if (noCenter) { + psOut->setPSCenter(false); + } - if (rasterAntialiasStr[0]) { - if (!GlobalParams::parseYesNo2(rasterAntialiasStr, &rasterAntialias)) { - fprintf(stderr, "Bad '-aaRaster' value on command line\n"); + if (rasterAntialiasStr[0]) { + if (!GlobalParams::parseYesNo2(rasterAntialiasStr, &rasterAntialias)) { + fprintf(stderr, "Bad '-aaRaster' value on command line\n"); + } } - } - if (forceRasterizeStr[0]) { - PSForceRasterize forceRasterize = psRasterizeWhenNeeded; - if (strcmp(forceRasterizeStr, "whenneeded") == 0) { - forceRasterize = psRasterizeWhenNeeded; - } else if (strcmp(forceRasterizeStr, "always") == 0) { - forceRasterize = psAlwaysRasterize; - } else if (strcmp(forceRasterizeStr, "never") == 0) { - forceRasterize = psNeverRasterize; - } else { - fprintf(stderr, "Bad '-rasterize' value on command line\n"); + if (forceRasterizeStr[0]) { + PSForceRasterize forceRasterize = psRasterizeWhenNeeded; + if (strcmp(forceRasterizeStr, "whenneeded") == 0) { + forceRasterize = psRasterizeWhenNeeded; + } else if (strcmp(forceRasterizeStr, "always") == 0) { + forceRasterize = psAlwaysRasterize; + } else if (strcmp(forceRasterizeStr, "never") == 0) { + forceRasterize = psNeverRasterize; + } else { + fprintf(stderr, "Bad '-rasterize' value on command line\n"); + } + psOut->setForceRasterize(forceRasterize); } - psOut->setForceRasterize(forceRasterize); - } - if (splashResolution > 0) { - psOut->setRasterResolution(splashResolution); - } - psOut->setEmbedType1(!noEmbedT1Fonts); - psOut->setEmbedTrueType(!noEmbedTTFonts); - psOut->setEmbedCIDPostScript(!noEmbedCIDPSFonts); - psOut->setEmbedCIDTrueType(!noEmbedCIDTTFonts); - psOut->setFontPassthrough(fontPassthrough); - psOut->setPreloadImagesForms(preload); - psOut->setOptimizeColorSpace(optimizeColorSpace); - psOut->setPassLevel1CustomColor(passLevel1CustomColor); + if (splashResolution > 0) { + psOut->setRasterResolution(splashResolution); + } + psOut->setEmbedType1(!noEmbedT1Fonts); + psOut->setEmbedTrueType(!noEmbedTTFonts); + psOut->setEmbedCIDPostScript(!noEmbedCIDPSFonts); + psOut->setEmbedCIDTrueType(!noEmbedCIDTTFonts); + psOut->setFontPassthrough(fontPassthrough); + psOut->setPreloadImagesForms(preload); + psOut->setOptimizeColorSpace(optimizeColorSpace); + psOut->setPassLevel1CustomColor(passLevel1CustomColor); #ifdef OPI_SUPPORT - psOut->setGenerateOPI(doOPI); + psOut->setGenerateOPI(doOPI); #endif - psOut->setUseBinary(psBinary); + psOut->setUseBinary(psBinary); - psOut->setRasterAntialias(rasterAntialias); - if (psOut->isOk()) { - for (int i = firstPage; i <= lastPage; ++i) { - doc->displayPage(psOut, i, 72, 72, - 0, noCrop, !noCrop, true); + psOut->setRasterAntialias(rasterAntialias); + if (psOut->isOk()) { + for (int i = firstPage; i <= lastPage; ++i) { + doc->displayPage(psOut, i, 72, 72, 0, noCrop, !noCrop, true); + } + } else { + delete psOut; + exitCode = 2; + goto err2; } - } else { delete psOut; - exitCode = 2; - goto err2; - } - delete psOut; - exitCode = 0; + exitCode = 0; - // clean up - err2: - delete psFileName; - err1: - delete doc; - delete fileName; - err0: + // clean up +err2: + delete psFileName; +err1: + delete doc; + delete fileName; +err0: - return exitCode; + return exitCode; } diff --git a/utils/pdftotext.cc b/utils/pdftotext.cc index e90588a5..f17cfd53 100644 --- a/utils/pdftotext.cc +++ b/utils/pdftotext.cc @@ -67,8 +67,7 @@ #include <iomanip> #include "Win32Console.h" -static void printInfoString(FILE *f, Dict *infoDict, const char *key, - const char *text1, const char *text2, const UnicodeMap *uMap); +static void printInfoString(FILE *f, Dict *infoDict, const char *key, const char *text1, const char *text2, const UnicodeMap *uMap); static void printInfoDate(FILE *f, Dict *infoDict, const char *key, const char *fmt); void printDocBBox(FILE *f, PDFDoc *doc, TextOutputDev *textOut, int first, int last); void printWordBBox(FILE *f, PDFDoc *doc, TextOutputDev *textOut, int first, int last); @@ -97,481 +96,446 @@ static bool printVersion = false; static bool printHelp = false; static bool printEnc = false; -static const ArgDesc argDesc[] = { - {"-f", argInt, &firstPage, 0, - "first page to convert"}, - {"-l", argInt, &lastPage, 0, - "last page to convert"}, - {"-r", argFP, &resolution, 0, - "resolution, in DPI (default is 72)"}, - {"-x", argInt, &x, 0, - "x-coordinate of the crop area top left corner"}, - {"-y", argInt, &y, 0, - "y-coordinate of the crop area top left corner"}, - {"-W", argInt, &w, 0, - "width of crop area in pixels (default is 0)"}, - {"-H", argInt, &h, 0, - "height of crop area in pixels (default is 0)"}, - {"-layout", argFlag, &physLayout, 0, - "maintain original physical layout"}, - {"-fixed", argFP, &fixedPitch, 0, - "assume fixed-pitch (or tabular) text"}, - {"-raw", argFlag, &rawOrder, 0, - "keep strings in content stream order"}, - {"-nodiag", argFlag, &discardDiag, 0, - "discard diagonal text"}, - {"-htmlmeta", argFlag, &htmlMeta, 0, - "generate a simple HTML file, including the meta information"}, - {"-enc", argString, textEncName, sizeof(textEncName), - "output text encoding name"}, - {"-listenc",argFlag, &printEnc, 0, - "list available encodings"}, - {"-eol", argString, textEOLStr, sizeof(textEOLStr), - "output end-of-line convention (unix, dos, or mac)"}, - {"-nopgbrk", argFlag, &noPageBreaks, 0, - "don't insert page breaks between pages"}, - {"-bbox", argFlag, &bbox, 0, - "output bounding box for each word and page size to html. Sets -htmlmeta"}, - {"-bbox-layout", argFlag, &bboxLayout, 0, - "like -bbox but with extra layout bounding box data. Sets -htmlmeta"}, - {"-opw", argString, ownerPassword, sizeof(ownerPassword), - "owner password (for encrypted files)"}, - {"-upw", argString, userPassword, sizeof(userPassword), - "user password (for encrypted files)"}, - {"-q", argFlag, &quiet, 0, - "don't print any messages or errors"}, - {"-v", argFlag, &printVersion, 0, - "print copyright and version info"}, - {"-h", argFlag, &printHelp, 0, - "print usage information"}, - {"-help", argFlag, &printHelp, 0, - "print usage information"}, - {"--help", argFlag, &printHelp, 0, - "print usage information"}, - {"-?", argFlag, &printHelp, 0, - "print usage information"}, - {} -}; - -static std::string myStringReplace(const std::string &inString, const std::string &oldToken, const std::string &newToken) { - std::string result = inString; - size_t foundLoc; - int advance = 0; - do { - foundLoc = result.find(oldToken, advance); - if (foundLoc != std::string::npos){ - result.replace(foundLoc, oldToken.length(), newToken); - advance = foundLoc + newToken.length(); - } - } while (foundLoc != std::string::npos ); - return result; +static const ArgDesc argDesc[] = { { "-f", argInt, &firstPage, 0, "first page to convert" }, + { "-l", argInt, &lastPage, 0, "last page to convert" }, + { "-r", argFP, &resolution, 0, "resolution, in DPI (default is 72)" }, + { "-x", argInt, &x, 0, "x-coordinate of the crop area top left corner" }, + { "-y", argInt, &y, 0, "y-coordinate of the crop area top left corner" }, + { "-W", argInt, &w, 0, "width of crop area in pixels (default is 0)" }, + { "-H", argInt, &h, 0, "height of crop area in pixels (default is 0)" }, + { "-layout", argFlag, &physLayout, 0, "maintain original physical layout" }, + { "-fixed", argFP, &fixedPitch, 0, "assume fixed-pitch (or tabular) text" }, + { "-raw", argFlag, &rawOrder, 0, "keep strings in content stream order" }, + { "-nodiag", argFlag, &discardDiag, 0, "discard diagonal text" }, + { "-htmlmeta", argFlag, &htmlMeta, 0, "generate a simple HTML file, including the meta information" }, + { "-enc", argString, textEncName, sizeof(textEncName), "output text encoding name" }, + { "-listenc", argFlag, &printEnc, 0, "list available encodings" }, + { "-eol", argString, textEOLStr, sizeof(textEOLStr), "output end-of-line convention (unix, dos, or mac)" }, + { "-nopgbrk", argFlag, &noPageBreaks, 0, "don't insert page breaks between pages" }, + { "-bbox", argFlag, &bbox, 0, "output bounding box for each word and page size to html. Sets -htmlmeta" }, + { "-bbox-layout", argFlag, &bboxLayout, 0, "like -bbox but with extra layout bounding box data. Sets -htmlmeta" }, + { "-opw", argString, ownerPassword, sizeof(ownerPassword), "owner password (for encrypted files)" }, + { "-upw", argString, userPassword, sizeof(userPassword), "user password (for encrypted files)" }, + { "-q", argFlag, &quiet, 0, "don't print any messages or errors" }, + { "-v", argFlag, &printVersion, 0, "print copyright and version info" }, + { "-h", argFlag, &printHelp, 0, "print usage information" }, + { "-help", argFlag, &printHelp, 0, "print usage information" }, + { "--help", argFlag, &printHelp, 0, "print usage information" }, + { "-?", argFlag, &printHelp, 0, "print usage information" }, + {} }; + +static std::string myStringReplace(const std::string &inString, const std::string &oldToken, const std::string &newToken) +{ + std::string result = inString; + size_t foundLoc; + int advance = 0; + do { + foundLoc = result.find(oldToken, advance); + if (foundLoc != std::string::npos) { + result.replace(foundLoc, oldToken.length(), newToken); + advance = foundLoc + newToken.length(); + } + } while (foundLoc != std::string::npos); + return result; } -static std::string myXmlTokenReplace(const char *inString){ - std::string myString(inString); - myString = myStringReplace(myString, "&", "&" ); - myString = myStringReplace(myString, "'", "'" ); - myString = myStringReplace(myString, "\"", """ ); - myString = myStringReplace(myString, "<", "<" ); - myString = myStringReplace(myString, ">", ">" ); - return myString; +static std::string myXmlTokenReplace(const char *inString) +{ + std::string myString(inString); + myString = myStringReplace(myString, "&", "&"); + myString = myStringReplace(myString, "'", "'"); + myString = myStringReplace(myString, "\"", """); + myString = myStringReplace(myString, "<", "<"); + myString = myStringReplace(myString, ">", ">"); + return myString; } -int main(int argc, char *argv[]) { - PDFDoc *doc; - GooString *fileName; - GooString *textFileName; - GooString *ownerPW, *userPW; - TextOutputDev *textOut; - FILE *f; - const UnicodeMap *uMap; - Object info; - bool ok; - int exitCode; - EndOfLineKind textEOL = TextOutputDev::defaultEndOfLine(); - - Win32Console win32Console(&argc, &argv); - exitCode = 99; - - // parse args - ok = parseArgs(argDesc, &argc, argv); - if (bboxLayout) { - bbox = true; - } - if (bbox) { - htmlMeta = true; - } - if (!ok || (argc < 2 && !printEnc) || argc > 3 || printVersion || printHelp) { - fprintf(stderr, "pdftotext version %s\n", PACKAGE_VERSION); - fprintf(stderr, "%s\n", popplerCopyright); - fprintf(stderr, "%s\n", xpdfCopyright); - if (!printVersion) { - printUsage("pdftotext", "<PDF-file> [<text-file>]", argDesc); +int main(int argc, char *argv[]) +{ + PDFDoc *doc; + GooString *fileName; + GooString *textFileName; + GooString *ownerPW, *userPW; + TextOutputDev *textOut; + FILE *f; + const UnicodeMap *uMap; + Object info; + bool ok; + int exitCode; + EndOfLineKind textEOL = TextOutputDev::defaultEndOfLine(); + + Win32Console win32Console(&argc, &argv); + exitCode = 99; + + // parse args + ok = parseArgs(argDesc, &argc, argv); + if (bboxLayout) { + bbox = true; + } + if (bbox) { + htmlMeta = true; + } + if (!ok || (argc < 2 && !printEnc) || argc > 3 || printVersion || printHelp) { + fprintf(stderr, "pdftotext version %s\n", PACKAGE_VERSION); + fprintf(stderr, "%s\n", popplerCopyright); + fprintf(stderr, "%s\n", xpdfCopyright); + if (!printVersion) { + printUsage("pdftotext", "<PDF-file> [<text-file>]", argDesc); + } + if (printVersion || printHelp) + exitCode = 0; + goto err0; } - if (printVersion || printHelp) - exitCode = 0; - goto err0; - } - // read config file - globalParams = std::make_unique<GlobalParams>(); + // read config file + globalParams = std::make_unique<GlobalParams>(); - if (printEnc) { - printEncodings(); - exitCode = 0; - goto err0; - } - - fileName = new GooString(argv[1]); - if (fixedPitch) { - physLayout = true; - } - - if (textEncName[0]) { - globalParams->setTextEncoding(textEncName); - } - if (textEOLStr[0]) { - if (!strcmp(textEOLStr, "unix")) { - textEOL = eolUnix; - } else if (!strcmp(textEOLStr, "dos")) { - textEOL = eolDOS; - } else if (!strcmp(textEOLStr, "mac")) { - textEOL = eolMac; - } else { - fprintf(stderr, "Bad '-eol' value on command line\n"); + if (printEnc) { + printEncodings(); + exitCode = 0; + goto err0; } - } - if (quiet) { - globalParams->setErrQuiet(quiet); - } - - // get mapping to output encoding - if (!(uMap = globalParams->getTextEncoding())) { - error(errCommandLine, -1, "Couldn't get text encoding"); - delete fileName; - goto err1; - } - - // open PDF file - if (ownerPassword[0] != '\001') { - ownerPW = new GooString(ownerPassword); - } else { - ownerPW = nullptr; - } - if (userPassword[0] != '\001') { - userPW = new GooString(userPassword); - } else { - userPW = nullptr; - } - - if (fileName->cmp("-") == 0) { - delete fileName; - fileName = new GooString("fd://0"); - } - - doc = PDFDocFactory().createPDFDoc(*fileName, ownerPW, userPW); - - if (userPW) { - delete userPW; - } - if (ownerPW) { - delete ownerPW; - } - if (!doc->isOk()) { - exitCode = 1; - goto err2; - } -#ifdef ENFORCE_PERMISSIONS - // check for copy permission - if (!doc->okToCopy()) { - error(errNotAllowed, -1, "Copying of text from this document is not allowed."); - exitCode = 3; - goto err2; - } -#endif + fileName = new GooString(argv[1]); + if (fixedPitch) { + physLayout = true; + } - // construct text file name - if (argc == 3) { - textFileName = new GooString(argv[2]); - } else if (fileName->cmp("fd://0") == 0) { - error(errCommandLine, -1, "You have to provide an output filename when reading from stdin."); - goto err2; - } else { - const char *p = fileName->c_str() + fileName->getLength() - 4; - if (!strcmp(p, ".pdf") || !strcmp(p, ".PDF")) { - textFileName = new GooString(fileName->c_str(), - fileName->getLength() - 4); + if (textEncName[0]) { + globalParams->setTextEncoding(textEncName); + } + if (textEOLStr[0]) { + if (!strcmp(textEOLStr, "unix")) { + textEOL = eolUnix; + } else if (!strcmp(textEOLStr, "dos")) { + textEOL = eolDOS; + } else if (!strcmp(textEOLStr, "mac")) { + textEOL = eolMac; + } else { + fprintf(stderr, "Bad '-eol' value on command line\n"); + } + } + if (quiet) { + globalParams->setErrQuiet(quiet); + } + + // get mapping to output encoding + if (!(uMap = globalParams->getTextEncoding())) { + error(errCommandLine, -1, "Couldn't get text encoding"); + delete fileName; + goto err1; + } + + // open PDF file + if (ownerPassword[0] != '\001') { + ownerPW = new GooString(ownerPassword); } else { - textFileName = fileName->copy(); + ownerPW = nullptr; } - textFileName->append(htmlMeta ? ".html" : ".txt"); - } - - // get page range - if (firstPage < 1) { - firstPage = 1; - } - if (lastPage < 1 || lastPage > doc->getNumPages()) { - lastPage = doc->getNumPages(); - } - if (lastPage < firstPage) { - error(errCommandLine, -1, - "Wrong page range given: the first page ({0:d}) can not be after the last page ({1:d}).", - firstPage, lastPage); - goto err3; - } - - // write HTML header - if (htmlMeta) { - if (!textFileName->cmp("-")) { - f = stdout; + if (userPassword[0] != '\001') { + userPW = new GooString(userPassword); } else { - if (!(f = fopen(textFileName->c_str(), "wb"))) { - error(errIO, -1, "Couldn't open text file '{0:t}'", textFileName); - exitCode = 2; - goto err3; - } + userPW = nullptr; + } + + if (fileName->cmp("-") == 0) { + delete fileName; + fileName = new GooString("fd://0"); } - fputs("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">", f); - fputs("<html xmlns=\"http://www.w3.org/1999/xhtml\">\n", f); - fputs("<head>\n", f); - info = doc->getDocInfo(); - if (info.isDict()) { - Object obj = info.getDict()->lookup("Title"); - if (obj.isString()) { - printInfoString(f, info.getDict(), "Title", "<title>", "</title>\n", uMap); - } else { - fputs("<title></title>\n", f); - } - printInfoString(f, info.getDict(), "Subject", - "<meta name=\"Subject\" content=\"", "\"/>\n", uMap); - printInfoString(f, info.getDict(), "Keywords", - "<meta name=\"Keywords\" content=\"", "\"/>\n", uMap); - printInfoString(f, info.getDict(), "Author", - "<meta name=\"Author\" content=\"", "\"/>\n", uMap); - printInfoString(f, info.getDict(), "Creator", - "<meta name=\"Creator\" content=\"", "\"/>\n", uMap); - printInfoString(f, info.getDict(), "Producer", - "<meta name=\"Producer\" content=\"", "\"/>\n", uMap); - printInfoDate(f, info.getDict(), "CreationDate", - "<meta name=\"CreationDate\" content=\"\"/>\n"); - printInfoDate(f, info.getDict(), "LastModifiedDate", - "<meta name=\"ModDate\" content=\"\"/>\n"); + + doc = PDFDocFactory().createPDFDoc(*fileName, ownerPW, userPW); + + if (userPW) { + delete userPW; } - fputs("</head>\n", f); - fputs("<body>\n", f); - if (!bbox) { - fputs("<pre>\n", f); - if (f != stdout) { - fclose(f); - } + if (ownerPW) { + delete ownerPW; } - } - - // write text file - if (htmlMeta && bbox) { // htmlMeta && is superfluous but makes gcc happier - textOut = new TextOutputDev(nullptr, physLayout, fixedPitch, rawOrder, htmlMeta, discardDiag); - - if (textOut->isOk()) { - textOut->setTextEOL(textEOL); - if (noPageBreaks) { - textOut->setTextPageBreaks(false); - } - if (bboxLayout) { - printDocBBox(f, doc, textOut, firstPage, lastPage); - } - else { - printWordBBox(f, doc, textOut, firstPage, lastPage); - } + if (!doc->isOk()) { + exitCode = 1; + goto err2; } - if (f != stdout) { - fclose(f); + +#ifdef ENFORCE_PERMISSIONS + // check for copy permission + if (!doc->okToCopy()) { + error(errNotAllowed, -1, "Copying of text from this document is not allowed."); + exitCode = 3; + goto err2; } - } else { - textOut = new TextOutputDev(textFileName->c_str(), - physLayout, fixedPitch, rawOrder, htmlMeta, discardDiag); - if (textOut->isOk()) { - textOut->setTextEOL(textEOL); - if (noPageBreaks) { - textOut->setTextPageBreaks(false); - } - if ((w==0) && (h==0) && (x==0) && (y==0)) { - doc->displayPages(textOut, firstPage, lastPage, resolution, resolution, 0, - true, false, false); - } else { - - for (int page = firstPage; page <= lastPage; ++page) { - doc->displayPageSlice(textOut, page, resolution, resolution, 0, - true, false, false, - x, y, w, h); - } - } +#endif + // construct text file name + if (argc == 3) { + textFileName = new GooString(argv[2]); + } else if (fileName->cmp("fd://0") == 0) { + error(errCommandLine, -1, "You have to provide an output filename when reading from stdin."); + goto err2; } else { - delete textOut; - exitCode = 2; - goto err3; + const char *p = fileName->c_str() + fileName->getLength() - 4; + if (!strcmp(p, ".pdf") || !strcmp(p, ".PDF")) { + textFileName = new GooString(fileName->c_str(), fileName->getLength() - 4); + } else { + textFileName = fileName->copy(); + } + textFileName->append(htmlMeta ? ".html" : ".txt"); + } + + // get page range + if (firstPage < 1) { + firstPage = 1; + } + if (lastPage < 1 || lastPage > doc->getNumPages()) { + lastPage = doc->getNumPages(); + } + if (lastPage < firstPage) { + error(errCommandLine, -1, "Wrong page range given: the first page ({0:d}) can not be after the last page ({1:d}).", firstPage, lastPage); + goto err3; + } + + // write HTML header + if (htmlMeta) { + if (!textFileName->cmp("-")) { + f = stdout; + } else { + if (!(f = fopen(textFileName->c_str(), "wb"))) { + error(errIO, -1, "Couldn't open text file '{0:t}'", textFileName); + exitCode = 2; + goto err3; + } + } + fputs("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">", f); + fputs("<html xmlns=\"http://www.w3.org/1999/xhtml\">\n", f); + fputs("<head>\n", f); + info = doc->getDocInfo(); + if (info.isDict()) { + Object obj = info.getDict()->lookup("Title"); + if (obj.isString()) { + printInfoString(f, info.getDict(), "Title", "<title>", "</title>\n", uMap); + } else { + fputs("<title></title>\n", f); + } + printInfoString(f, info.getDict(), "Subject", "<meta name=\"Subject\" content=\"", "\"/>\n", uMap); + printInfoString(f, info.getDict(), "Keywords", "<meta name=\"Keywords\" content=\"", "\"/>\n", uMap); + printInfoString(f, info.getDict(), "Author", "<meta name=\"Author\" content=\"", "\"/>\n", uMap); + printInfoString(f, info.getDict(), "Creator", "<meta name=\"Creator\" content=\"", "\"/>\n", uMap); + printInfoString(f, info.getDict(), "Producer", "<meta name=\"Producer\" content=\"", "\"/>\n", uMap); + printInfoDate(f, info.getDict(), "CreationDate", "<meta name=\"CreationDate\" content=\"\"/>\n"); + printInfoDate(f, info.getDict(), "LastModifiedDate", "<meta name=\"ModDate\" content=\"\"/>\n"); + } + fputs("</head>\n", f); + fputs("<body>\n", f); + if (!bbox) { + fputs("<pre>\n", f); + if (f != stdout) { + fclose(f); + } + } } - } - delete textOut; - // write end of HTML file - if (htmlMeta) { - if (!textFileName->cmp("-")) { - f = stdout; + // write text file + if (htmlMeta && bbox) { // htmlMeta && is superfluous but makes gcc happier + textOut = new TextOutputDev(nullptr, physLayout, fixedPitch, rawOrder, htmlMeta, discardDiag); + + if (textOut->isOk()) { + textOut->setTextEOL(textEOL); + if (noPageBreaks) { + textOut->setTextPageBreaks(false); + } + if (bboxLayout) { + printDocBBox(f, doc, textOut, firstPage, lastPage); + } else { + printWordBBox(f, doc, textOut, firstPage, lastPage); + } + } + if (f != stdout) { + fclose(f); + } } else { - if (!(f = fopen(textFileName->c_str(), "ab"))) { - error(errIO, -1, "Couldn't open text file '{0:t}'", textFileName); - exitCode = 2; - goto err3; - } + textOut = new TextOutputDev(textFileName->c_str(), physLayout, fixedPitch, rawOrder, htmlMeta, discardDiag); + if (textOut->isOk()) { + textOut->setTextEOL(textEOL); + if (noPageBreaks) { + textOut->setTextPageBreaks(false); + } + if ((w == 0) && (h == 0) && (x == 0) && (y == 0)) { + doc->displayPages(textOut, firstPage, lastPage, resolution, resolution, 0, true, false, false); + } else { + + for (int page = firstPage; page <= lastPage; ++page) { + doc->displayPageSlice(textOut, page, resolution, resolution, 0, true, false, false, x, y, w, h); + } + } + + } else { + delete textOut; + exitCode = 2; + goto err3; + } } - if (!bbox) fputs("</pre>\n", f); - fputs("</body>\n", f); - fputs("</html>\n", f); - if (f != stdout) { - fclose(f); + delete textOut; + + // write end of HTML file + if (htmlMeta) { + if (!textFileName->cmp("-")) { + f = stdout; + } else { + if (!(f = fopen(textFileName->c_str(), "ab"))) { + error(errIO, -1, "Couldn't open text file '{0:t}'", textFileName); + exitCode = 2; + goto err3; + } + } + if (!bbox) + fputs("</pre>\n", f); + fputs("</body>\n", f); + fputs("</html>\n", f); + if (f != stdout) { + fclose(f); + } } - } - exitCode = 0; + exitCode = 0; - // clean up - err3: - delete textFileName; - err2: - delete doc; - delete fileName; - err1: - err0: + // clean up +err3: + delete textFileName; +err2: + delete doc; + delete fileName; +err1: +err0: - return exitCode; + return exitCode; } -static void printInfoString(FILE *f, Dict *infoDict, const char *key, - const char *text1, const char *text2, const UnicodeMap *uMap) { - const GooString *s1; - bool isUnicode; - Unicode u; - char buf[9]; - int i, n; - - Object obj = infoDict->lookup(key); - if (obj.isString()) { - fputs(text1, f); - s1 = obj.getString(); - if ((s1->getChar(0) & 0xff) == 0xfe && - (s1->getChar(1) & 0xff) == 0xff) { - isUnicode = true; - i = 2; - } else { - isUnicode = false; - i = 0; - } - while (i < obj.getString()->getLength()) { - if (isUnicode) { - u = ((s1->getChar(i) & 0xff) << 8) | - (s1->getChar(i+1) & 0xff); - i += 2; - } else { - u = pdfDocEncoding[s1->getChar(i) & 0xff]; - ++i; - } - n = uMap->mapUnicode(u, buf, sizeof(buf)); - buf[n] = '\0'; - const std::string myString = myXmlTokenReplace(buf); - fputs(myString.c_str(), f); +static void printInfoString(FILE *f, Dict *infoDict, const char *key, const char *text1, const char *text2, const UnicodeMap *uMap) +{ + const GooString *s1; + bool isUnicode; + Unicode u; + char buf[9]; + int i, n; + + Object obj = infoDict->lookup(key); + if (obj.isString()) { + fputs(text1, f); + s1 = obj.getString(); + if ((s1->getChar(0) & 0xff) == 0xfe && (s1->getChar(1) & 0xff) == 0xff) { + isUnicode = true; + i = 2; + } else { + isUnicode = false; + i = 0; + } + while (i < obj.getString()->getLength()) { + if (isUnicode) { + u = ((s1->getChar(i) & 0xff) << 8) | (s1->getChar(i + 1) & 0xff); + i += 2; + } else { + u = pdfDocEncoding[s1->getChar(i) & 0xff]; + ++i; + } + n = uMap->mapUnicode(u, buf, sizeof(buf)); + buf[n] = '\0'; + const std::string myString = myXmlTokenReplace(buf); + fputs(myString.c_str(), f); + } + fputs(text2, f); } - fputs(text2, f); - } } -static void printInfoDate(FILE *f, Dict *infoDict, const char *key, const char *fmt) { - Object obj = infoDict->lookup(key); - if (obj.isString()) { - const char *s = obj.getString()->c_str(); - if (s[0] == 'D' && s[1] == ':') { - s += 2; +static void printInfoDate(FILE *f, Dict *infoDict, const char *key, const char *fmt) +{ + Object obj = infoDict->lookup(key); + if (obj.isString()) { + const char *s = obj.getString()->c_str(); + if (s[0] == 'D' && s[1] == ':') { + s += 2; + } + fprintf(f, fmt, s); } - fprintf(f, fmt, s); - } } -static void printLine(FILE *f, const TextLine *line) { - double xMin, yMin, xMax, yMax; - double lineXMin = 0, lineYMin = 0, lineXMax = 0, lineYMax = 0; - const TextWord *word; - std::stringstream wordXML; - wordXML << std::fixed << std::setprecision(6); - - for (word = line->getWords(); word; word = word->getNext()) { - word->getBBox(&xMin, &yMin, &xMax, &yMax); - - if (lineXMin == 0 || lineXMin > xMin) lineXMin = xMin; - if (lineYMin == 0 || lineYMin > yMin) lineYMin = yMin; - if (lineXMax < xMax) lineXMax = xMax; - if (lineYMax < yMax) lineYMax = yMax; - - GooString *wordText = word->getText(); - const std::string myString = myXmlTokenReplace(wordText->c_str()); - wordXML << " <word xMin=\"" << xMin << "\" yMin=\"" << yMin << "\" xMax=\"" << - xMax << "\" yMax=\"" << yMax << "\">" << myString << "</word>\n"; - delete wordText; - } - fprintf(f, " <line xMin=\"%f\" yMin=\"%f\" xMax=\"%f\" yMax=\"%f\">\n", - lineXMin, lineYMin, lineXMax, lineYMax); - fputs(wordXML.str().c_str(), f); - fputs(" </line>\n", f); +static void printLine(FILE *f, const TextLine *line) +{ + double xMin, yMin, xMax, yMax; + double lineXMin = 0, lineYMin = 0, lineXMax = 0, lineYMax = 0; + const TextWord *word; + std::stringstream wordXML; + wordXML << std::fixed << std::setprecision(6); + + for (word = line->getWords(); word; word = word->getNext()) { + word->getBBox(&xMin, &yMin, &xMax, &yMax); + + if (lineXMin == 0 || lineXMin > xMin) + lineXMin = xMin; + if (lineYMin == 0 || lineYMin > yMin) + lineYMin = yMin; + if (lineXMax < xMax) + lineXMax = xMax; + if (lineYMax < yMax) + lineYMax = yMax; + + GooString *wordText = word->getText(); + const std::string myString = myXmlTokenReplace(wordText->c_str()); + wordXML << " <word xMin=\"" << xMin << "\" yMin=\"" << yMin << "\" xMax=\"" << xMax << "\" yMax=\"" << yMax << "\">" << myString << "</word>\n"; + delete wordText; + } + fprintf(f, " <line xMin=\"%f\" yMin=\"%f\" xMax=\"%f\" yMax=\"%f\">\n", lineXMin, lineYMin, lineXMax, lineYMax); + fputs(wordXML.str().c_str(), f); + fputs(" </line>\n", f); } -void printDocBBox(FILE *f, PDFDoc *doc, TextOutputDev *textOut, int first, int last) { - double xMin, yMin, xMax, yMax; - const TextFlow *flow; - const TextBlock *blk; - const TextLine *line; - - fprintf(f, "<doc>\n"); - for (int page = first; page <= last; ++page) { - fprintf(f, " <page width=\"%f\" height=\"%f\">\n",doc->getPageMediaWidth(page), doc->getPageMediaHeight(page)); - doc->displayPage(textOut, page, resolution, resolution, 0, true, false, false); - for (flow = textOut->getFlows(); flow; flow = flow->getNext()) { - fprintf(f, " <flow>\n"); - for (blk = flow->getBlocks(); blk; blk = blk->getNext()) { - blk->getBBox(&xMin, &yMin, &xMax, &yMax); - fprintf(f, " <block xMin=\"%f\" yMin=\"%f\" xMax=\"%f\" yMax=\"%f\">\n", xMin, yMin, xMax, yMax); - for (line = blk->getLines(); line; line = line->getNext()) { - printLine(f, line); +void printDocBBox(FILE *f, PDFDoc *doc, TextOutputDev *textOut, int first, int last) +{ + double xMin, yMin, xMax, yMax; + const TextFlow *flow; + const TextBlock *blk; + const TextLine *line; + + fprintf(f, "<doc>\n"); + for (int page = first; page <= last; ++page) { + fprintf(f, " <page width=\"%f\" height=\"%f\">\n", doc->getPageMediaWidth(page), doc->getPageMediaHeight(page)); + doc->displayPage(textOut, page, resolution, resolution, 0, true, false, false); + for (flow = textOut->getFlows(); flow; flow = flow->getNext()) { + fprintf(f, " <flow>\n"); + for (blk = flow->getBlocks(); blk; blk = blk->getNext()) { + blk->getBBox(&xMin, &yMin, &xMax, &yMax); + fprintf(f, " <block xMin=\"%f\" yMin=\"%f\" xMax=\"%f\" yMax=\"%f\">\n", xMin, yMin, xMax, yMax); + for (line = blk->getLines(); line; line = line->getNext()) { + printLine(f, line); + } + fprintf(f, " </block>\n"); + } + fprintf(f, " </flow>\n"); } - fprintf(f, " </block>\n"); - } - fprintf(f, " </flow>\n"); + fprintf(f, " </page>\n"); } - fprintf(f, " </page>\n"); - } - fprintf(f, "</doc>\n"); + fprintf(f, "</doc>\n"); } -void printWordBBox(FILE *f, PDFDoc *doc, TextOutputDev *textOut, int first, int last) { - fprintf(f, "<doc>\n"); - for (int page = first; page <= last; ++page) { - fprintf(f, " <page width=\"%f\" height=\"%f\">\n",doc->getPageMediaWidth(page), doc->getPageMediaHeight(page)); - doc->displayPage(textOut, page, resolution, resolution, 0, true, false, false); - TextWordList *wordlist = textOut->makeWordList(); - const int word_length = wordlist != nullptr ? wordlist->getLength() : 0; - TextWord *word; - double xMinA, yMinA, xMaxA, yMaxA; - if (word_length == 0) - fprintf(stderr, "no word list\n"); - - for (int i = 0; i < word_length; ++i) { - word = wordlist->get(i); - word->getBBox(&xMinA, &yMinA, &xMaxA, &yMaxA); - const std::string myString = myXmlTokenReplace(word->getText()->c_str()); - fprintf(f," <word xMin=\"%f\" yMin=\"%f\" xMax=\"%f\" yMax=\"%f\">%s</word>\n", xMinA, yMinA, xMaxA, yMaxA, myString.c_str()); +void printWordBBox(FILE *f, PDFDoc *doc, TextOutputDev *textOut, int first, int last) +{ + fprintf(f, "<doc>\n"); + for (int page = first; page <= last; ++page) { + fprintf(f, " <page width=\"%f\" height=\"%f\">\n", doc->getPageMediaWidth(page), doc->getPageMediaHeight(page)); + doc->displayPage(textOut, page, resolution, resolution, 0, true, false, false); + TextWordList *wordlist = textOut->makeWordList(); + const int word_length = wordlist != nullptr ? wordlist->getLength() : 0; + TextWord *word; + double xMinA, yMinA, xMaxA, yMaxA; + if (word_length == 0) + fprintf(stderr, "no word list\n"); + + for (int i = 0; i < word_length; ++i) { + word = wordlist->get(i); + word->getBBox(&xMinA, &yMinA, &xMaxA, &yMaxA); + const std::string myString = myXmlTokenReplace(word->getText()->c_str()); + fprintf(f, " <word xMin=\"%f\" yMin=\"%f\" xMax=\"%f\" yMax=\"%f\">%s</word>\n", xMinA, yMinA, xMaxA, yMaxA, myString.c_str()); + } + fprintf(f, " </page>\n"); + delete wordlist; } - fprintf(f, " </page>\n"); - delete wordlist; - } - fprintf(f, "</doc>\n"); + fprintf(f, "</doc>\n"); } diff --git a/utils/pdfunite.cc b/utils/pdfunite.cc index a8116e37..1ea1a346 100644 --- a/utils/pdfunite.cc +++ b/utils/pdfunite.cc @@ -28,383 +28,371 @@ static bool printVersion = false; static bool printHelp = false; -static const ArgDesc argDesc[] = { - {"-v", argFlag, &printVersion, 0, - "print copyright and version info"}, - {"-h", argFlag, &printHelp, 0, - "print usage information"}, - {"-help", argFlag, &printHelp, 0, - "print usage information"}, - {"--help", argFlag, &printHelp, 0, - "print usage information"}, - {"-?", argFlag, &printHelp, 0, - "print usage information"}, - { } -}; +static const ArgDesc argDesc[] = { { "-v", argFlag, &printVersion, 0, "print copyright and version info" }, { "-h", argFlag, &printHelp, 0, "print usage information" }, { "-help", argFlag, &printHelp, 0, "print usage information" }, + { "--help", argFlag, &printHelp, 0, "print usage information" }, { "-?", argFlag, &printHelp, 0, "print usage information" }, {} }; -static void doMergeNameTree(PDFDoc *doc, XRef *srcXRef, XRef *countRef, int oldRefNum, int newRefNum, Dict *srcNameTree, Dict *mergeNameTree, int numOffset) { - Object mergeNameArray = mergeNameTree->lookup("Names"); - Object srcNameArray = srcNameTree->lookup("Names"); - if (mergeNameArray.isArray() && srcNameArray.isArray()) { - Array *newNameArray = new Array(srcXRef); - int j = 0; - for (int i = 0; i < srcNameArray.arrayGetLength() - 1; i += 2) { - const Object &key = srcNameArray.arrayGetNF(i); - const Object &value = srcNameArray.arrayGetNF(i + 1); - if (key.isString() && value.isRef()) { +static void doMergeNameTree(PDFDoc *doc, XRef *srcXRef, XRef *countRef, int oldRefNum, int newRefNum, Dict *srcNameTree, Dict *mergeNameTree, int numOffset) +{ + Object mergeNameArray = mergeNameTree->lookup("Names"); + Object srcNameArray = srcNameTree->lookup("Names"); + if (mergeNameArray.isArray() && srcNameArray.isArray()) { + Array *newNameArray = new Array(srcXRef); + int j = 0; + for (int i = 0; i < srcNameArray.arrayGetLength() - 1; i += 2) { + const Object &key = srcNameArray.arrayGetNF(i); + const Object &value = srcNameArray.arrayGetNF(i + 1); + if (key.isString() && value.isRef()) { + while (j < mergeNameArray.arrayGetLength() - 1) { + const Object &mkey = mergeNameArray.arrayGetNF(j); + const Object &mvalue = mergeNameArray.arrayGetNF(j + 1); + if (mkey.isString() && mvalue.isRef()) { + if (mkey.getString()->cmp(key.getString()) < 0) { + newNameArray->add(Object(new GooString(mkey.getString()->c_str()))); + newNameArray->add(Object({ mvalue.getRef().num + numOffset, mvalue.getRef().gen })); + j += 2; + } else if (mkey.getString()->cmp(key.getString()) == 0) { + j += 2; + } else { + break; + } + } else { + j += 2; + } + } + newNameArray->add(Object(new GooString(key.getString()->c_str()))); + newNameArray->add(Object(value.getRef())); + } + } while (j < mergeNameArray.arrayGetLength() - 1) { - const Object &mkey = mergeNameArray.arrayGetNF(j); - const Object &mvalue = mergeNameArray.arrayGetNF(j + 1); - if (mkey.isString() && mvalue.isRef()) { - if (mkey.getString()->cmp(key.getString()) < 0) { - newNameArray->add(Object(new GooString(mkey.getString()->c_str()))); - newNameArray->add(Object( { mvalue.getRef().num + numOffset, mvalue.getRef().gen } )); - j += 2; - } else if (mkey.getString()->cmp(key.getString()) == 0) { - j += 2; - } else { - break; + const Object &mkey = mergeNameArray.arrayGetNF(j); + const Object &mvalue = mergeNameArray.arrayGetNF(j + 1); + if (mkey.isString() && mvalue.isRef()) { + newNameArray->add(Object(new GooString(mkey.getString()->c_str()))); + newNameArray->add(Object({ mvalue.getRef().num + numOffset, mvalue.getRef().gen })); } - } else { j += 2; - } } - newNameArray->add(Object(new GooString(key.getString()->c_str()))); - newNameArray->add(Object(value.getRef())); - } - } - while (j < mergeNameArray.arrayGetLength() - 1) { - const Object &mkey = mergeNameArray.arrayGetNF(j); - const Object &mvalue = mergeNameArray.arrayGetNF(j + 1); - if (mkey.isString() && mvalue.isRef()) { - newNameArray->add(Object(new GooString(mkey.getString()->c_str()))); - newNameArray->add(Object( { mvalue.getRef().num + numOffset, mvalue.getRef().gen } )); - } - j += 2; - } - srcNameTree->set("Names", Object(newNameArray)); - doc->markPageObjects(mergeNameTree, srcXRef, countRef, numOffset, oldRefNum, newRefNum); - } else if (srcNameArray.isNull() && mergeNameArray.isArray()) { - Array *newNameArray = new Array(srcXRef); - for (int i = 0; i < mergeNameArray.arrayGetLength() - 1; i += 2) { - const Object &key = mergeNameArray.arrayGetNF(i); - const Object &value = mergeNameArray.arrayGetNF(i + 1); - if (key.isString() && value.isRef()) { - newNameArray->add(Object(new GooString(key.getString()->c_str()))); - newNameArray->add(Object( { value.getRef().num + numOffset, value.getRef().gen } )); - } + srcNameTree->set("Names", Object(newNameArray)); + doc->markPageObjects(mergeNameTree, srcXRef, countRef, numOffset, oldRefNum, newRefNum); + } else if (srcNameArray.isNull() && mergeNameArray.isArray()) { + Array *newNameArray = new Array(srcXRef); + for (int i = 0; i < mergeNameArray.arrayGetLength() - 1; i += 2) { + const Object &key = mergeNameArray.arrayGetNF(i); + const Object &value = mergeNameArray.arrayGetNF(i + 1); + if (key.isString() && value.isRef()) { + newNameArray->add(Object(new GooString(key.getString()->c_str()))); + newNameArray->add(Object({ value.getRef().num + numOffset, value.getRef().gen })); + } + } + srcNameTree->add("Names", Object(newNameArray)); + doc->markPageObjects(mergeNameTree, srcXRef, countRef, numOffset, oldRefNum, newRefNum); } - srcNameTree->add("Names", Object(newNameArray)); - doc->markPageObjects(mergeNameTree, srcXRef, countRef, numOffset, oldRefNum, newRefNum); - } } -static void doMergeNameDict(PDFDoc *doc, XRef *srcXRef, XRef *countRef, int oldRefNum, int newRefNum, Dict *srcNameDict, Dict *mergeNameDict, int numOffset) { - for (int i = 0; i < mergeNameDict->getLength(); i++) { - const char *key = mergeNameDict->getKey(i); - Object mergeNameTree = mergeNameDict->lookup(key); - Object srcNameTree = srcNameDict->lookup(key); - if (srcNameTree.isDict() && mergeNameTree.isDict()) { - doMergeNameTree(doc, srcXRef, countRef, oldRefNum, newRefNum, srcNameTree.getDict(), mergeNameTree.getDict(), numOffset); - } else if (srcNameTree.isNull() && mergeNameTree.isDict()) { - Object newNameTree(new Dict(srcXRef)); - doMergeNameTree(doc, srcXRef, countRef, oldRefNum, newRefNum, newNameTree.getDict(), mergeNameTree.getDict(), numOffset); - srcNameDict->add(key, std::move(newNameTree)); +static void doMergeNameDict(PDFDoc *doc, XRef *srcXRef, XRef *countRef, int oldRefNum, int newRefNum, Dict *srcNameDict, Dict *mergeNameDict, int numOffset) +{ + for (int i = 0; i < mergeNameDict->getLength(); i++) { + const char *key = mergeNameDict->getKey(i); + Object mergeNameTree = mergeNameDict->lookup(key); + Object srcNameTree = srcNameDict->lookup(key); + if (srcNameTree.isDict() && mergeNameTree.isDict()) { + doMergeNameTree(doc, srcXRef, countRef, oldRefNum, newRefNum, srcNameTree.getDict(), mergeNameTree.getDict(), numOffset); + } else if (srcNameTree.isNull() && mergeNameTree.isDict()) { + Object newNameTree(new Dict(srcXRef)); + doMergeNameTree(doc, srcXRef, countRef, oldRefNum, newRefNum, newNameTree.getDict(), mergeNameTree.getDict(), numOffset); + srcNameDict->add(key, std::move(newNameTree)); + } } - } } -static void doMergeFormDict(Dict *srcFormDict, Dict *mergeFormDict, int numOffset) { - Object srcFields = srcFormDict->lookup("Fields"); - Object mergeFields = mergeFormDict->lookup("Fields"); - if (srcFields.isArray() && mergeFields.isArray()) { - for (int i = 0; i < mergeFields.arrayGetLength(); i++) { - const Object &value = mergeFields.arrayGetNF(i); - srcFields.arrayAdd(Object( { value.getRef().num + numOffset, value.getRef().gen } )); +static void doMergeFormDict(Dict *srcFormDict, Dict *mergeFormDict, int numOffset) +{ + Object srcFields = srcFormDict->lookup("Fields"); + Object mergeFields = mergeFormDict->lookup("Fields"); + if (srcFields.isArray() && mergeFields.isArray()) { + for (int i = 0; i < mergeFields.arrayGetLength(); i++) { + const Object &value = mergeFields.arrayGetNF(i); + srcFields.arrayAdd(Object({ value.getRef().num + numOffset, value.getRef().gen })); + } } - } } /////////////////////////////////////////////////////////////////////////// -int main (int argc, char *argv[]) +int main(int argc, char *argv[]) /////////////////////////////////////////////////////////////////////////// // Merge PDF files given by arguments 1 to argc-2 and write the result // to the file specified by argument argc-1. /////////////////////////////////////////////////////////////////////////// { - int objectsCount = 0; - unsigned int numOffset = 0; - std::vector<Object> pages; - std::vector<unsigned int> offsets; - XRef *yRef, *countRef; - FILE *f; - OutStream *outStr; - int i; - int j, rootNum; - std::vector<PDFDoc *>docs; - int majorVersion = 0; - int minorVersion = 0; - char *fileName = argv[argc - 1]; - int exitCode; + int objectsCount = 0; + unsigned int numOffset = 0; + std::vector<Object> pages; + std::vector<unsigned int> offsets; + XRef *yRef, *countRef; + FILE *f; + OutStream *outStr; + int i; + int j, rootNum; + std::vector<PDFDoc *> docs; + int majorVersion = 0; + int minorVersion = 0; + char *fileName = argv[argc - 1]; + int exitCode; - exitCode = 99; - const bool ok = parseArgs (argDesc, &argc, argv); - if (!ok || argc < 3 || printVersion || printHelp) { - fprintf(stderr, "pdfunite version %s\n", PACKAGE_VERSION); - fprintf(stderr, "%s\n", popplerCopyright); - fprintf(stderr, "%s\n", xpdfCopyright); - if (!printVersion) { - printUsage("pdfunite", "<PDF-sourcefile-1>..<PDF-sourcefile-n> <PDF-destfile>", - argDesc); + exitCode = 99; + const bool ok = parseArgs(argDesc, &argc, argv); + if (!ok || argc < 3 || printVersion || printHelp) { + fprintf(stderr, "pdfunite version %s\n", PACKAGE_VERSION); + fprintf(stderr, "%s\n", popplerCopyright); + fprintf(stderr, "%s\n", xpdfCopyright); + if (!printVersion) { + printUsage("pdfunite", "<PDF-sourcefile-1>..<PDF-sourcefile-n> <PDF-destfile>", argDesc); + } + if (printVersion || printHelp) + exitCode = 0; + return exitCode; } - if (printVersion || printHelp) - exitCode = 0; - return exitCode; - } - exitCode = 0; - globalParams = std::make_unique<GlobalParams>(); + exitCode = 0; + globalParams = std::make_unique<GlobalParams>(); - for (i = 1; i < argc - 1; i++) { - GooString *gfileName = new GooString(argv[i]); - PDFDoc *doc = new PDFDoc(gfileName, nullptr, nullptr, nullptr); - if (doc->isOk() && !doc->isEncrypted() && - doc->getXRef()->getCatalog().isDict()) { - docs.push_back(doc); - if (doc->getPDFMajorVersion() > majorVersion) { - majorVersion = doc->getPDFMajorVersion(); - minorVersion = doc->getPDFMinorVersion(); - } else if (doc->getPDFMajorVersion() == majorVersion) { - if (doc->getPDFMinorVersion() > minorVersion) { - minorVersion = doc->getPDFMinorVersion(); + for (i = 1; i < argc - 1; i++) { + GooString *gfileName = new GooString(argv[i]); + PDFDoc *doc = new PDFDoc(gfileName, nullptr, nullptr, nullptr); + if (doc->isOk() && !doc->isEncrypted() && doc->getXRef()->getCatalog().isDict()) { + docs.push_back(doc); + if (doc->getPDFMajorVersion() > majorVersion) { + majorVersion = doc->getPDFMajorVersion(); + minorVersion = doc->getPDFMinorVersion(); + } else if (doc->getPDFMajorVersion() == majorVersion) { + if (doc->getPDFMinorVersion() > minorVersion) { + minorVersion = doc->getPDFMinorVersion(); + } + } + } else if (doc->isOk()) { + if (doc->isEncrypted()) { + error(errUnimplemented, -1, "Could not merge encrypted files ('{0:s}')", argv[i]); + return -1; + } else if (!doc->getXRef()->getCatalog().isDict()) { + error(errSyntaxError, -1, "XRef's Catalog is not a dictionary ('{0:s}')", argv[i]); + return -1; + } + } else { + error(errSyntaxError, -1, "Could not merge damaged documents ('{0:s}')", argv[i]); + return -1; } - } - } else if (doc->isOk()) { - if (doc->isEncrypted()) { - error(errUnimplemented, -1, "Could not merge encrypted files ('{0:s}')", argv[i]); - return -1; - } else if (!doc->getXRef()->getCatalog().isDict()) { - error(errSyntaxError, -1, "XRef's Catalog is not a dictionary ('{0:s}')", argv[i]); - return -1; - } - } else { - error(errSyntaxError, -1, "Could not merge damaged documents ('{0:s}')", argv[i]); - return -1; } - } - if (!(f = fopen(fileName, "wb"))) { - error(errIO, -1, "Could not open file '{0:s}'", fileName); - return -1; - } - outStr = new FileOutStream(f, 0); + if (!(f = fopen(fileName, "wb"))) { + error(errIO, -1, "Could not open file '{0:s}'", fileName); + return -1; + } + outStr = new FileOutStream(f, 0); - yRef = new XRef(); - countRef = new XRef(); - yRef->add(0, 65535, 0, false); - PDFDoc::writeHeader(outStr, majorVersion, minorVersion); + yRef = new XRef(); + countRef = new XRef(); + yRef->add(0, 65535, 0, false); + PDFDoc::writeHeader(outStr, majorVersion, minorVersion); - // handle OutputIntents, AcroForm, OCProperties & Names - Object intents; - Object names; - Object afObj; - Object ocObj; - if (docs.size() >= 1) { - Object catObj = docs[0]->getXRef()->getCatalog(); - Dict *catDict = catObj.getDict(); - intents = catDict->lookup("OutputIntents"); - afObj = catDict->lookupNF("AcroForm").copy(); - Ref *refPage = docs[0]->getCatalog()->getPageRef(1); - if (!afObj.isNull() && refPage) { - docs[0]->markAcroForm(&afObj, yRef, countRef, 0, refPage->num, refPage->num); - } - ocObj = catDict->lookupNF("OCProperties").copy(); - if (!ocObj.isNull() && ocObj.isDict() && refPage) { - docs[0]->markPageObjects(ocObj.getDict(), yRef, countRef, 0, refPage->num, refPage->num); - } - names = catDict->lookup("Names"); - if (!names.isNull() && names.isDict() && refPage) { - docs[0]->markPageObjects(names.getDict(), yRef, countRef, 0, refPage->num, refPage->num); - } - if (intents.isArray() && intents.arrayGetLength() > 0) { - for (i = 1; i < (int) docs.size(); i++) { - Object pagecatObj = docs[i]->getXRef()->getCatalog(); - Dict *pagecatDict = pagecatObj.getDict(); - Object pageintents = pagecatDict->lookup("OutputIntents"); - if (pageintents.isArray() && pageintents.arrayGetLength() > 0) { - for (j = intents.arrayGetLength() - 1; j >= 0; j--) { - Object intent = intents.arrayGet(j, 0); - if (intent.isDict()) { - Object idf = intent.dictLookup("OutputConditionIdentifier"); - if (idf.isString()) { - const GooString *gidf = idf.getString(); - bool removeIntent = true; - for (int k = 0; k < pageintents.arrayGetLength(); k++) { - Object pgintent = pageintents.arrayGet(k, 0); - if (pgintent.isDict()) { - Object pgidf = pgintent.dictLookup("OutputConditionIdentifier"); - if (pgidf.isString()) { - const GooString *gpgidf = pgidf.getString(); - if (gpgidf->cmp(gidf) == 0) { - removeIntent = false; - break; - } + // handle OutputIntents, AcroForm, OCProperties & Names + Object intents; + Object names; + Object afObj; + Object ocObj; + if (docs.size() >= 1) { + Object catObj = docs[0]->getXRef()->getCatalog(); + Dict *catDict = catObj.getDict(); + intents = catDict->lookup("OutputIntents"); + afObj = catDict->lookupNF("AcroForm").copy(); + Ref *refPage = docs[0]->getCatalog()->getPageRef(1); + if (!afObj.isNull() && refPage) { + docs[0]->markAcroForm(&afObj, yRef, countRef, 0, refPage->num, refPage->num); + } + ocObj = catDict->lookupNF("OCProperties").copy(); + if (!ocObj.isNull() && ocObj.isDict() && refPage) { + docs[0]->markPageObjects(ocObj.getDict(), yRef, countRef, 0, refPage->num, refPage->num); + } + names = catDict->lookup("Names"); + if (!names.isNull() && names.isDict() && refPage) { + docs[0]->markPageObjects(names.getDict(), yRef, countRef, 0, refPage->num, refPage->num); + } + if (intents.isArray() && intents.arrayGetLength() > 0) { + for (i = 1; i < (int)docs.size(); i++) { + Object pagecatObj = docs[i]->getXRef()->getCatalog(); + Dict *pagecatDict = pagecatObj.getDict(); + Object pageintents = pagecatDict->lookup("OutputIntents"); + if (pageintents.isArray() && pageintents.arrayGetLength() > 0) { + for (j = intents.arrayGetLength() - 1; j >= 0; j--) { + Object intent = intents.arrayGet(j, 0); + if (intent.isDict()) { + Object idf = intent.dictLookup("OutputConditionIdentifier"); + if (idf.isString()) { + const GooString *gidf = idf.getString(); + bool removeIntent = true; + for (int k = 0; k < pageintents.arrayGetLength(); k++) { + Object pgintent = pageintents.arrayGet(k, 0); + if (pgintent.isDict()) { + Object pgidf = pgintent.dictLookup("OutputConditionIdentifier"); + if (pgidf.isString()) { + const GooString *gpgidf = pgidf.getString(); + if (gpgidf->cmp(gidf) == 0) { + removeIntent = false; + break; + } + } + } + } + if (removeIntent) { + intents.arrayRemove(j); + error(errSyntaxWarning, -1, "Output intent {0:s} missing in pdf {1:s}, removed", gidf->c_str(), docs[i]->getFileName()->c_str()); + } + } else { + intents.arrayRemove(j); + error(errSyntaxWarning, -1, "Invalid output intent dict, missing required OutputConditionIdentifier"); + } + } else { + intents.arrayRemove(j); + } } - } + } else { + error(errSyntaxWarning, -1, "Output intents differs, remove them all"); + break; } - if (removeIntent) { - intents.arrayRemove(j); - error(errSyntaxWarning, -1, "Output intent {0:s} missing in pdf {1:s}, removed", - gidf->c_str(), docs[i]->getFileName()->c_str()); + } + } + if (intents.isArray() && intents.arrayGetLength() > 0) { + for (j = intents.arrayGetLength() - 1; j >= 0; j--) { + Object intent = intents.arrayGet(j, 0); + if (intent.isDict()) { + docs[0]->markPageObjects(intent.getDict(), yRef, countRef, numOffset, 0, 0); + } else { + intents.arrayRemove(j); } - } else { - intents.arrayRemove(j); - error(errSyntaxWarning, -1, "Invalid output intent dict, missing required OutputConditionIdentifier"); - } - } else { - intents.arrayRemove(j); } - } - } else { - error(errSyntaxWarning, -1, "Output intents differs, remove them all"); - break; } - } } - if (intents.isArray() && intents.arrayGetLength() > 0) { - for (j = intents.arrayGetLength() - 1; j >= 0; j--) { - Object intent = intents.arrayGet(j, 0); - if (intent.isDict()) { - docs[0]->markPageObjects(intent.getDict(), yRef, countRef, numOffset, 0, 0); - } else { - intents.arrayRemove(j); + + for (i = 0; i < (int)docs.size(); i++) { + for (j = 1; j <= docs[i]->getNumPages(); j++) { + if (!docs[i]->getCatalog()->getPage(j)) { + continue; + } + + const PDFRectangle *cropBox = nullptr; + if (docs[i]->getCatalog()->getPage(j)->isCropped()) + cropBox = docs[i]->getCatalog()->getPage(j)->getCropBox(); + docs[i]->replacePageDict(j, docs[i]->getCatalog()->getPage(j)->getRotate(), docs[i]->getCatalog()->getPage(j)->getMediaBox(), cropBox); + Ref *refPage = docs[i]->getCatalog()->getPageRef(j); + Object page = docs[i]->getXRef()->fetch(*refPage); + Dict *pageDict = page.getDict(); + Object *resDict = docs[i]->getCatalog()->getPage(j)->getResourceDictObject(); + if (resDict->isDict()) { + pageDict->set("Resources", resDict->copy()); + } + pages.push_back(std::move(page)); + offsets.push_back(numOffset); + docs[i]->markPageObjects(pageDict, yRef, countRef, numOffset, refPage->num, refPage->num); + Object annotsObj = pageDict->lookupNF("Annots").copy(); + if (!annotsObj.isNull()) { + docs[i]->markAnnotations(&annotsObj, yRef, countRef, numOffset, refPage->num, refPage->num); + } + } + Object pageCatObj = docs[i]->getXRef()->getCatalog(); + Dict *pageCatDict = pageCatObj.getDict(); + Object pageNames = pageCatDict->lookup("Names"); + if (!pageNames.isNull() && pageNames.isDict()) { + if (!names.isDict()) { + names = Object(new Dict(yRef)); + } + doMergeNameDict(docs[i], yRef, countRef, 0, 0, names.getDict(), pageNames.getDict(), numOffset); } - } + Object pageForm = pageCatDict->lookup("AcroForm"); + if (i > 0 && !pageForm.isNull() && pageForm.isDict()) { + if (afObj.isNull()) { + afObj = pageCatDict->lookupNF("AcroForm").copy(); + } else if (afObj.isDict()) { + doMergeFormDict(afObj.getDict(), pageForm.getDict(), numOffset); + } + } + objectsCount += docs[i]->writePageObjects(outStr, yRef, numOffset, true); + numOffset = yRef->getNumObjects() + 1; } - } - - for (i = 0; i < (int) docs.size(); i++) { - for (j = 1; j <= docs[i]->getNumPages(); j++) { - if (!docs[i]->getCatalog()->getPage(j)) { - continue; - } - const PDFRectangle *cropBox = nullptr; - if (docs[i]->getCatalog()->getPage(j)->isCropped()) - cropBox = docs[i]->getCatalog()->getPage(j)->getCropBox(); - docs[i]->replacePageDict(j, - docs[i]->getCatalog()->getPage(j)->getRotate(), - docs[i]->getCatalog()->getPage(j)->getMediaBox(), cropBox); - Ref *refPage = docs[i]->getCatalog()->getPageRef(j); - Object page = docs[i]->getXRef()->fetch(*refPage); - Dict *pageDict = page.getDict(); - Object *resDict = docs[i]->getCatalog()->getPage(j)->getResourceDictObject(); - if (resDict->isDict()) { - pageDict->set("Resources", resDict->copy()); - } - pages.push_back(std::move(page)); - offsets.push_back(numOffset); - docs[i]->markPageObjects(pageDict, yRef, countRef, numOffset, refPage->num, refPage->num); - Object annotsObj = pageDict->lookupNF("Annots").copy(); - if (!annotsObj.isNull()) { - docs[i]->markAnnotations(&annotsObj, yRef, countRef, numOffset, refPage->num, refPage->num); - } + rootNum = yRef->getNumObjects() + 1; + yRef->add(rootNum, 0, outStr->getPos(), true); + outStr->printf("%d 0 obj\n", rootNum); + outStr->printf("<< /Type /Catalog /Pages %d 0 R", rootNum + 1); + // insert OutputIntents + if (intents.isArray() && intents.arrayGetLength() > 0) { + outStr->printf(" /OutputIntents ["); + for (j = 0; j < intents.arrayGetLength(); j++) { + Object intent = intents.arrayGet(j, 0); + if (intent.isDict()) { + PDFDoc::writeObject(&intent, outStr, yRef, 0, nullptr, cryptRC4, 0, 0, 0); + } + } + outStr->printf("]"); } - Object pageCatObj = docs[i]->getXRef()->getCatalog(); - Dict *pageCatDict = pageCatObj.getDict(); - Object pageNames = pageCatDict->lookup("Names"); - if (!pageNames.isNull() && pageNames.isDict()) { - if (!names.isDict()) { - names = Object(new Dict(yRef)); - } - doMergeNameDict(docs[i], yRef, countRef, 0, 0, names.getDict(), pageNames.getDict(), numOffset); + // insert AcroForm + if (!afObj.isNull()) { + outStr->printf(" /AcroForm "); + PDFDoc::writeObject(&afObj, outStr, yRef, 0, nullptr, cryptRC4, 0, 0, 0); } - Object pageForm = pageCatDict->lookup("AcroForm"); - if (i > 0 && !pageForm.isNull() && pageForm.isDict()) { - if (afObj.isNull()) { - afObj = pageCatDict->lookupNF("AcroForm").copy(); - } else if (afObj.isDict()) { - doMergeFormDict(afObj.getDict(), pageForm.getDict(), numOffset); - } + // insert OCProperties + if (!ocObj.isNull() && ocObj.isDict()) { + outStr->printf(" /OCProperties "); + PDFDoc::writeObject(&ocObj, outStr, yRef, 0, nullptr, cryptRC4, 0, 0, 0); } - objectsCount += docs[i]->writePageObjects(outStr, yRef, numOffset, true); - numOffset = yRef->getNumObjects() + 1; - } - - rootNum = yRef->getNumObjects() + 1; - yRef->add(rootNum, 0, outStr->getPos(), true); - outStr->printf("%d 0 obj\n", rootNum); - outStr->printf("<< /Type /Catalog /Pages %d 0 R", rootNum + 1); - // insert OutputIntents - if (intents.isArray() && intents.arrayGetLength() > 0) { - outStr->printf(" /OutputIntents ["); - for (j = 0; j < intents.arrayGetLength(); j++) { - Object intent = intents.arrayGet(j, 0); - if (intent.isDict()) { - PDFDoc::writeObject(&intent, outStr, yRef, 0, nullptr, cryptRC4, 0, 0, 0); - } + // insert Names + if (!names.isNull() && names.isDict()) { + outStr->printf(" /Names "); + PDFDoc::writeObject(&names, outStr, yRef, 0, nullptr, cryptRC4, 0, 0, 0); } - outStr->printf("]"); - } - // insert AcroForm - if (!afObj.isNull()) { - outStr->printf(" /AcroForm "); - PDFDoc::writeObject(&afObj, outStr, yRef, 0, nullptr, cryptRC4, 0, 0, 0); - } - // insert OCProperties - if (!ocObj.isNull() && ocObj.isDict()) { - outStr->printf(" /OCProperties "); - PDFDoc::writeObject(&ocObj, outStr, yRef, 0, nullptr, cryptRC4, 0, 0, 0); - } - // insert Names - if (!names.isNull() && names.isDict()) { - outStr->printf(" /Names "); - PDFDoc::writeObject(&names, outStr, yRef, 0, nullptr, cryptRC4, 0, 0, 0); - } - outStr->printf(">>\nendobj\n"); - objectsCount++; + outStr->printf(">>\nendobj\n"); + objectsCount++; - yRef->add(rootNum + 1, 0, outStr->getPos(), true); - outStr->printf("%d 0 obj\n", rootNum + 1); - outStr->printf("<< /Type /Pages /Kids ["); - for (j = 0; j < (int) pages.size(); j++) - outStr->printf(" %d 0 R", rootNum + j + 2); - outStr->printf(" ] /Count %zd >>\nendobj\n", pages.size()); - objectsCount++; + yRef->add(rootNum + 1, 0, outStr->getPos(), true); + outStr->printf("%d 0 obj\n", rootNum + 1); + outStr->printf("<< /Type /Pages /Kids ["); + for (j = 0; j < (int)pages.size(); j++) + outStr->printf(" %d 0 R", rootNum + j + 2); + outStr->printf(" ] /Count %zd >>\nendobj\n", pages.size()); + objectsCount++; - for (i = 0; i < (int) pages.size(); i++) { - yRef->add(rootNum + i + 2, 0, outStr->getPos(), true); - outStr->printf("%d 0 obj\n", rootNum + i + 2); - outStr->printf("<< "); - Dict *pageDict = pages[i].getDict(); - for (j = 0; j < pageDict->getLength(); j++) { - if (j > 0) - outStr->printf(" "); - const char *key = pageDict->getKey(j); - Object value = pageDict->getValNF(j).copy(); - if (strcmp(key, "Parent") == 0) { - outStr->printf("/Parent %d 0 R", rootNum + 1); - } else { - outStr->printf("/%s ", key); - PDFDoc::writeObject(&value, outStr, yRef, offsets[i], nullptr, cryptRC4, 0, 0, 0); - } + for (i = 0; i < (int)pages.size(); i++) { + yRef->add(rootNum + i + 2, 0, outStr->getPos(), true); + outStr->printf("%d 0 obj\n", rootNum + i + 2); + outStr->printf("<< "); + Dict *pageDict = pages[i].getDict(); + for (j = 0; j < pageDict->getLength(); j++) { + if (j > 0) + outStr->printf(" "); + const char *key = pageDict->getKey(j); + Object value = pageDict->getValNF(j).copy(); + if (strcmp(key, "Parent") == 0) { + outStr->printf("/Parent %d 0 R", rootNum + 1); + } else { + outStr->printf("/%s ", key); + PDFDoc::writeObject(&value, outStr, yRef, offsets[i], nullptr, cryptRC4, 0, 0, 0); + } + } + outStr->printf(" >>\nendobj\n"); + objectsCount++; } - outStr->printf(" >>\nendobj\n"); - objectsCount++; - } - Goffset uxrefOffset = outStr->getPos(); - Ref ref; - ref.num = rootNum; - ref.gen = 0; - Object trailerDict = PDFDoc::createTrailerDict(objectsCount, false, 0, &ref, yRef, - fileName, outStr->getPos()); - PDFDoc::writeXRefTableTrailer(std::move(trailerDict), yRef, true, // write all entries according to ISO 32000-1, 7.5.4 Cross-Reference Table: "For a file that has never been incrementally updated, the cross-reference section shall contain only one subsection, whose object numbering begins at 0." - uxrefOffset, outStr, yRef); + Goffset uxrefOffset = outStr->getPos(); + Ref ref; + ref.num = rootNum; + ref.gen = 0; + Object trailerDict = PDFDoc::createTrailerDict(objectsCount, false, 0, &ref, yRef, fileName, outStr->getPos()); + PDFDoc::writeXRefTableTrailer(std::move(trailerDict), yRef, true, // write all entries according to ISO 32000-1, 7.5.4 Cross-Reference Table: "For a file that has never been incrementally updated, the cross-reference section shall + // contain only one subsection, whose object numbering begins at 0." + uxrefOffset, outStr, yRef); - outStr->close(); - delete outStr; - fclose(f); - delete yRef; - delete countRef; - for (i = 0; i < (int) docs.size (); i++) delete docs[i]; - return exitCode; + outStr->close(); + delete outStr; + fclose(f); + delete yRef; + delete countRef; + for (i = 0; i < (int)docs.size(); i++) + delete docs[i]; + return exitCode; } diff --git a/utils/printencodings.cc b/utils/printencodings.cc index 15580de6..5f561483 100644 --- a/utils/printencodings.cc +++ b/utils/printencodings.cc @@ -28,16 +28,14 @@ void printEncodings() { - std::vector<GooString*> *encNames = globalParams->getEncodingNames(); + std::vector<GooString *> *encNames = globalParams->getEncodingNames(); - std::sort(encNames->begin(), encNames->end(), [](void *lhs, void *rhs) { - return static_cast<GooString *>(lhs)->cmp(static_cast<GooString *>(rhs)) < 0; - }); + std::sort(encNames->begin(), encNames->end(), [](void *lhs, void *rhs) { return static_cast<GooString *>(lhs)->cmp(static_cast<GooString *>(rhs)) < 0; }); - printf("Available encodings are:\n"); - for (const GooString *enc : *encNames) { - printf("%s\n", enc->c_str()); - } + printf("Available encodings are:\n"); + for (const GooString *enc : *encNames) { + printf("%s\n", enc->c_str()); + } - delete encNames; + delete encNames; } |