diff --git a/.gitignore b/.gitignore index 7b811cf..f796ffa 100644 --- a/.gitignore +++ b/.gitignore @@ -3,20 +3,18 @@ CMakeFiles/* cmake_install.cmake CTestTestfile.cmake gmon.out -Makefile install_manifest.txt +Makefile pdf2htmlEX pdf2htmlEX.1 +*.pyc share/base.css +share/base.min.css share/fancy.css -share/js_src/css_class_names.js +share/fancy.min.css share/pdf2htmlEX.js +share/pdf2htmlEX.min.js src/pdf2htmlEX-config.h src/util/css_const.h -test/* +test export-ignore Testing/* -wiki/* -doc/* -/"\\" -/share/base.min.css -/share/fancy.min.css diff --git a/.travis.yml b/.travis.yml index aaaa8cd..ceaba73 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,25 +1,32 @@ language: cpp compiler: gcc +addons: + sauce_connect: true before_install: + - sudo add-apt-repository ppa:fontforge/fontforge --yes - sudo add-apt-repository ppa:coolwanglu/pdf2htmlex --yes - sudo apt-get update -qq - - sudo apt-get install -qq libpoppler-dev libspiro-dev libcairo-dev libfreetype6-dev libltdl-dev -install: - - export LIBRARY_PATH=/usr/local/lib - - export LD_LIBRARY_PATH=/usr/local/lib - - pushd .. - - wget 'https://github.com/coolwanglu/fontforge/archive/pdf2htmlEX.tar.gz' -O - | tar -zxf - - - pushd fontforge-pdf2htmlEX && ./autogen.sh && ./configure && make && sudo make install && popd + - sudo apt-get install -qq libpoppler-dev libpoppler-private-dev libspiro-dev libcairo-dev libpango1.0-dev libfreetype6-dev libltdl-dev libfontforge-dev python-imaging python-pip firefox xvfb + - sudo pip install selenium sauceclient + - export DISPLAY=:99.0 + - test/start_xvfb.sh + - pushd / + - python -m SimpleHTTPServer 8000 >/dev/null 2>&1 & - popd + - sleep 5 before_script: - cmake -DENABLE_SVG=ON . script: - make - - make test + - P2H_TEST_REMOTE=1 ctest --output-on-failure - sudo make install - /usr/local/bin/pdf2htmlEX -v branches: only: - master - incoming - - travis + - wl +env: + global: + - secure: V0yGXROTAsRc3ExcECj7X/CrJLbodUeqZyfQGkA6x0iLV7Lh8/hgTjSsvuj7ef/DIWMqJ5cAIzZuXiF0KIxiVllF1v0I3w+LScxynT7B1NsyH16hvGIc7EvrsRmGVeTv8n9I+cCIwQxjtliNKfeZjV4Rk2+u6LioUzTszmW2etc= + - secure: Q5ZSrdFEgN0JvUp90nY5Wh58iukmGZQ2EW7crOibWH2yuUsxAnMELxpY+9yV3+eA7kbjJf/I0NCa5ZY1gkxK60ugUj+zuUDTL+BV1XCbO37e0uwh3ae99iyQWpXc8e8wBp10sthoX7U6Hvypa5tD9r1JJib8jxJV/MzIFpb7H9s= diff --git a/3rdparty/poppler/0.22.5/CairoFontEngine.cc b/3rdparty/poppler/0.22.5/CairoFontEngine.cc deleted file mode 100644 index 92f2486..0000000 --- a/3rdparty/poppler/0.22.5/CairoFontEngine.cc +++ /dev/null @@ -1,803 +0,0 @@ -//======================================================================== -// -// CairoFontEngine.cc -// -// Copyright 2003 Glyph & Cog, LLC -// Copyright 2004 Red Hat, Inc -// -//======================================================================== - -//======================================================================== -// -// Modified under the Poppler project - http://poppler.freedesktop.org -// -// All changes made under the Poppler project to this file are licensed -// under GPL version 2 or later -// -// Copyright (C) 2005-2007 Jeff Muizelaar -// Copyright (C) 2005, 2006 Kristian Høgsberg -// Copyright (C) 2005 Martin Kretzschmar -// Copyright (C) 2005, 2009, 2012 Albert Astals Cid -// Copyright (C) 2006, 2007, 2010, 2011 Carlos Garcia Campos -// Copyright (C) 2007 Koji Otani -// Copyright (C) 2008, 2009 Chris Wilson -// Copyright (C) 2008, 2012 Adrian Johnson -// Copyright (C) 2009 Darren Kenny -// Copyright (C) 2010 Suzuki Toshiya -// Copyright (C) 2010 Jan Kümmel -// Copyright (C) 2012 Hib Eris -// -// To see a description of the changes please see the Changelog file that -// came with your tarball or type make ChangeLog if you are building from git -// -//======================================================================== - -#include - -#include -#include "CairoFontEngine.h" -#include "CairoOutputDev.h" -#include "GlobalParams.h" -#include -#include -#include "goo/gfile.h" -#include "Error.h" -#include "XRef.h" -#include "Gfx.h" -#include "Page.h" - -#if HAVE_FCNTL_H && HAVE_SYS_MMAN_H && HAVE_SYS_STAT_H -#include -#include -#include -#define CAN_CHECK_OPEN_FACES 1 -#endif - -#ifdef USE_GCC_PRAGMAS -#pragma implementation -#endif - - -//------------------------------------------------------------------------ -// CairoFont -//------------------------------------------------------------------------ - -CairoFont::CairoFont(Ref ref, - cairo_font_face_t *cairo_font_face, - int *codeToGID, - Guint codeToGIDLen, - GBool substitute, - GBool printing) : ref(ref), - cairo_font_face(cairo_font_face), - codeToGID(codeToGID), - codeToGIDLen(codeToGIDLen), - substitute(substitute), - printing(printing) { } - -CairoFont::~CairoFont() { - cairo_font_face_destroy (cairo_font_face); - gfree(codeToGID); -} - -GBool -CairoFont::matches(Ref &other, GBool printingA) { - return (other.num == ref.num && other.gen == ref.gen); -} - -cairo_font_face_t * -CairoFont::getFontFace(void) { - return cairo_font_face; -} - -unsigned long -CairoFont::getGlyph(CharCode code, - Unicode *u, int uLen) { - FT_UInt gid; - - if (codeToGID && code < codeToGIDLen) { - gid = (FT_UInt)codeToGID[code]; - } else { - gid = (FT_UInt)code; - } - return gid; -} - -double -CairoFont::getSubstitutionCorrection(GfxFont *gfxFont) -{ - double w1, w2; - CharCode code; - char *name; - - // for substituted fonts: adjust the font matrix -- compare the - // width of 'm' in the original font and the substituted font - if (isSubstitute() && !gfxFont->isCIDFont()) { - for (code = 0; code < 256; ++code) { - if ((name = ((Gfx8BitFont *)gfxFont)->getCharName(code)) && - name[0] == 'm' && name[1] == '\0') { - break; - } - } - if (code < 256) { - w1 = ((Gfx8BitFont *)gfxFont)->getWidth(code); - { - cairo_matrix_t m; - cairo_matrix_init_identity(&m); - cairo_font_options_t *options = cairo_font_options_create(); - cairo_font_options_set_hint_style(options, CAIRO_HINT_STYLE_NONE); - cairo_font_options_set_hint_metrics(options, CAIRO_HINT_METRICS_OFF); - cairo_scaled_font_t *scaled_font = cairo_scaled_font_create(cairo_font_face, &m, &m, options); - - cairo_text_extents_t extents; - cairo_scaled_font_text_extents(scaled_font, "m", &extents); - - cairo_scaled_font_destroy(scaled_font); - cairo_font_options_destroy(options); - w2 = extents.x_advance; - } - if (!gfxFont->isSymbolic()) { - // if real font is substantially narrower than substituted - // font, reduce the font size accordingly - if (w1 > 0.01 && w1 < 0.9 * w2) { - w1 /= w2; - return w1; - } - } - } - } - return 1.0; -} - -//------------------------------------------------------------------------ -// CairoFreeTypeFont -//------------------------------------------------------------------------ - -static cairo_user_data_key_t _ft_cairo_key; - -static void -_ft_done_face_uncached (void *closure) -{ - FT_Face face = (FT_Face) closure; - FT_Done_Face (face); -} - -static GBool -_ft_new_face_uncached (FT_Library lib, - const char *filename, - char *font_data, - int font_data_len, - FT_Face *face_out, - cairo_font_face_t **font_face_out) -{ - FT_Face face; - cairo_font_face_t *font_face; - - if (font_data == NULL) { - if (FT_New_Face (lib, filename, 0, &face)) - return gFalse; - } else { - if (FT_New_Memory_Face (lib, (unsigned char *)font_data, font_data_len, 0, &face)) - return gFalse; - } - - font_face = cairo_ft_font_face_create_for_ft_face (face, - FT_LOAD_NO_HINTING | - FT_LOAD_NO_BITMAP); - if (cairo_font_face_set_user_data (font_face, - &_ft_cairo_key, - face, - _ft_done_face_uncached)) - { - _ft_done_face_uncached (face); - cairo_font_face_destroy (font_face); - return gFalse; - } - - *face_out = face; - *font_face_out = font_face; - return gTrue; -} - -#if CAN_CHECK_OPEN_FACES -static struct _ft_face_data { - struct _ft_face_data *prev, *next, **head; - - int fd; - unsigned long hash; - size_t size; - unsigned char *bytes; - - FT_Library lib; - FT_Face face; - cairo_font_face_t *font_face; -} *_ft_open_faces; - -static unsigned long -_djb_hash (const unsigned char *bytes, size_t len) -{ - unsigned long hash = 5381; - while (len--) { - unsigned char c = *bytes++; - hash *= 33; - hash ^= c; - } - return hash; -} - -static GBool -_ft_face_data_equal (struct _ft_face_data *a, struct _ft_face_data *b) -{ - if (a->lib != b->lib) - return gFalse; - if (a->size != b->size) - return gFalse; - if (a->hash != b->hash) - return gFalse; - - return memcmp (a->bytes, b->bytes, a->size) == 0; -} - -static void -_ft_done_face (void *closure) -{ - struct _ft_face_data *data = (struct _ft_face_data *) closure; - - if (data->next) - data->next->prev = data->prev; - if (data->prev) - data->prev->next = data->next; - else - _ft_open_faces = data->next; - -#if defined(__SUNPRO_CC) && defined(__sun) && defined(__SVR4) - munmap ((char*)data->bytes, data->size); -#else - munmap (data->bytes, data->size); -#endif - close (data->fd); - - FT_Done_Face (data->face); - gfree (data); -} - -static GBool -_ft_new_face (FT_Library lib, - const char *filename, - char *font_data, - int font_data_len, - FT_Face *face_out, - cairo_font_face_t **font_face_out) -{ - struct _ft_face_data *l; - struct stat st; - struct _ft_face_data tmpl; - - tmpl.fd = -1; - - if (font_data == NULL) { - /* if we fail to mmap the file, just pass it to FreeType instead */ - tmpl.fd = open (filename, O_RDONLY); - if (tmpl.fd == -1) - return _ft_new_face_uncached (lib, filename, font_data, font_data_len, face_out, font_face_out); - - if (fstat (tmpl.fd, &st) == -1) { - close (tmpl.fd); - return _ft_new_face_uncached (lib, filename, font_data, font_data_len, face_out, font_face_out); - } - - tmpl.bytes = (unsigned char *) mmap (NULL, st.st_size, - PROT_READ, MAP_PRIVATE, - tmpl.fd, 0); - if (tmpl.bytes == MAP_FAILED) { - close (tmpl.fd); - return _ft_new_face_uncached (lib, filename, font_data, font_data_len, face_out, font_face_out); - } - tmpl.size = st.st_size; - } else { - tmpl.bytes = (unsigned char*) font_data; - tmpl.size = font_data_len; - } - - /* check to see if this is a duplicate of any of the currently open fonts */ - tmpl.lib = lib; - tmpl.hash = _djb_hash (tmpl.bytes, tmpl.size); - - for (l = _ft_open_faces; l; l = l->next) { - if (_ft_face_data_equal (l, &tmpl)) { - if (tmpl.fd != -1) { -#if defined(__SUNPRO_CC) && defined(__sun) && defined(__SVR4) - munmap ((char*)tmpl.bytes, tmpl.size); -#else - munmap (tmpl.bytes, tmpl.size); -#endif - close (tmpl.fd); - } - *face_out = l->face; - *font_face_out = cairo_font_face_reference (l->font_face); - return gTrue; - } - } - - /* not a dup, open and insert into list */ - if (FT_New_Memory_Face (lib, - (FT_Byte *) tmpl.bytes, tmpl.size, - 0, &tmpl.face)) - { - if (tmpl.fd != -1) { -#if defined(__SUNPRO_CC) && defined(__sun) && defined(__SVR4) - munmap ((char*)tmpl.bytes, tmpl.size); -#else - munmap (tmpl.bytes, tmpl.size); -#endif - - close (tmpl.fd); - } - return gFalse; - } - - l = (struct _ft_face_data *) gmallocn (1, sizeof (struct _ft_face_data)); - *l = tmpl; - l->prev = NULL; - l->next = _ft_open_faces; - if (_ft_open_faces) - _ft_open_faces->prev = l; - _ft_open_faces = l; - - l->font_face = cairo_ft_font_face_create_for_ft_face (tmpl.face, - FT_LOAD_NO_HINTING | - FT_LOAD_NO_BITMAP); - if (cairo_font_face_set_user_data (l->font_face, - &_ft_cairo_key, - l, - _ft_done_face)) - { - cairo_font_face_destroy (l->font_face); - _ft_done_face (l); - return gFalse; - } - - *face_out = l->face; - *font_face_out = l->font_face; - return gTrue; -} -#else -#define _ft_new_face _ft_new_face_uncached -#endif - -CairoFreeTypeFont::CairoFreeTypeFont(Ref ref, - cairo_font_face_t *cairo_font_face, - int *codeToGID, - Guint codeToGIDLen, - GBool substitute) : CairoFont(ref, - cairo_font_face, - codeToGID, - codeToGIDLen, - substitute, - gTrue) { } - -CairoFreeTypeFont::~CairoFreeTypeFont() { } - -CairoFreeTypeFont *CairoFreeTypeFont::create(GfxFont *gfxFont, XRef *xref, - FT_Library lib, GBool useCIDs) { - Object refObj, strObj; - GooString *fileName; - char *fileNameC; - char *font_data; - int font_data_len; - int i, n; - GfxFontType fontType; - GfxFontLoc *fontLoc; - char **enc; - char *name; - FoFiTrueType *ff; - FoFiType1C *ff1c; - Ref ref; - FT_Face face; - cairo_font_face_t *font_face; - - int *codeToGID; - Guint codeToGIDLen; - - codeToGID = NULL; - codeToGIDLen = 0; - font_data = NULL; - font_data_len = 0; - fileName = NULL; - fileNameC = NULL; - - GBool substitute = gFalse; - - ref = *gfxFont->getID(); - fontType = gfxFont->getType(); - - if (!(fontLoc = gfxFont->locateFont(xref, gFalse))) { - error(errSyntaxError, -1, "Couldn't find a font for '{0:s}'", - gfxFont->getName() ? gfxFont->getName()->getCString() - : "(unnamed)"); - goto err2; - } - - // embedded font - if (fontLoc->locType == gfxFontLocEmbedded) { - font_data = gfxFont->readEmbFontFile(xref, &font_data_len); - if (NULL == font_data) - goto err2; - - // external font - } else { // gfxFontLocExternal - fileName = fontLoc->path; - fontType = fontLoc->fontType; - substitute = gTrue; - } - - if (fileName != NULL) { - fileNameC = fileName->getCString(); - } - - switch (fontType) { - case fontType1: - case fontType1C: - case fontType1COT: - if (! _ft_new_face (lib, fileNameC, font_data, font_data_len, &face, &font_face)) { - error(errSyntaxError, -1, "could not create type1 face"); - goto err2; - } - - enc = ((Gfx8BitFont *)gfxFont)->getEncoding(); - - codeToGID = (int *)gmallocn(256, sizeof(int)); - codeToGIDLen = 256; - for (i = 0; i < 256; ++i) { - codeToGID[i] = 0; - if ((name = enc[i])) { - codeToGID[i] = FT_Get_Name_Index(face, name); - } - } - break; - case fontCIDType2: - case fontCIDType2OT: - codeToGID = NULL; - n = 0; - if (((GfxCIDFont *)gfxFont)->getCIDToGID()) { - n = ((GfxCIDFont *)gfxFont)->getCIDToGIDLen(); - if (n) { - codeToGID = (int *)gmallocn(n, sizeof(int)); - memcpy(codeToGID, ((GfxCIDFont *)gfxFont)->getCIDToGID(), - n * sizeof(int)); - } - } else { - if (font_data != NULL) { - ff = FoFiTrueType::make(font_data, font_data_len); - } else { - ff = FoFiTrueType::load(fileNameC); - } - if (! ff) - goto err2; - codeToGID = ((GfxCIDFont *)gfxFont)->getCodeToGIDMap(ff, &n); - delete ff; - } - codeToGIDLen = n; - /* Fall through */ - case fontTrueType: - if (font_data != NULL) { - ff = FoFiTrueType::make(font_data, font_data_len); - } else { - ff = FoFiTrueType::load(fileNameC); - } - if (! ff) { - error(errSyntaxError, -1, "failed to load truetype font\n"); - goto err2; - } - /* This might be set already for the CIDType2 case */ - if (fontType == fontTrueType) { - codeToGID = ((Gfx8BitFont *)gfxFont)->getCodeToGIDMap(ff); - codeToGIDLen = 256; - } - delete ff; - if (! _ft_new_face (lib, fileNameC, font_data, font_data_len, &face, &font_face)) { - error(errSyntaxError, -1, "could not create truetype face\n"); - goto err2; - } - break; - - case fontCIDType0: - case fontCIDType0C: - - codeToGID = NULL; - codeToGIDLen = 0; - - if (!useCIDs) - { - if (font_data != NULL) { - ff1c = FoFiType1C::make(font_data, font_data_len); - } else { - ff1c = FoFiType1C::load(fileNameC); - } - if (ff1c) { - codeToGID = ff1c->getCIDToGIDMap((int *)&codeToGIDLen); - delete ff1c; - } - } - - if (! _ft_new_face (lib, fileNameC, font_data, font_data_len, &face, &font_face)) { - gfree(codeToGID); - codeToGID = NULL; - error(errSyntaxError, -1, "could not create cid face\n"); - goto err2; - } - break; - - default: - fprintf (stderr, "font type %d not handled\n", (int)fontType); - goto err2; - break; - } - - delete fontLoc; - return new CairoFreeTypeFont(ref, - font_face, - codeToGID, codeToGIDLen, - substitute); - - err2: - /* hmm? */ - delete fontLoc; - fprintf (stderr, "some font thing failed\n"); - return NULL; -} - -//------------------------------------------------------------------------ -// CairoType3Font -//------------------------------------------------------------------------ - -static const cairo_user_data_key_t type3_font_key = {0}; - -typedef struct _type3_font_info { - GfxFont *font; - PDFDoc *doc; - CairoFontEngine *fontEngine; - GBool printing; -} type3_font_info_t; - -static void -_free_type3_font_info(void *closure) -{ - type3_font_info_t *info = (type3_font_info_t *) closure; - - info->font->decRefCnt(); - free (info); -} - -static cairo_status_t -_init_type3_glyph (cairo_scaled_font_t *scaled_font, - cairo_t *cr, - cairo_font_extents_t *extents) -{ - type3_font_info_t *info; - GfxFont *font; - double *mat; - - info = (type3_font_info_t *) - cairo_font_face_get_user_data (cairo_scaled_font_get_font_face (scaled_font), - &type3_font_key); - font = info->font; - mat = font->getFontBBox(); - extents->ascent = mat[3]; /* y2 */ - extents->descent = -mat[3]; /* -y1 */ - extents->height = extents->ascent + extents->descent; - extents->max_x_advance = mat[2] - mat[1]; /* x2 - x1 */ - extents->max_y_advance = 0; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_render_type3_glyph (cairo_scaled_font_t *scaled_font, - unsigned long glyph, - cairo_t *cr, - cairo_text_extents_t *metrics) -{ - Dict *charProcs; - Object charProc; - CairoOutputDev *output_dev; - cairo_matrix_t matrix, invert_y_axis; - double *mat; - double wx, wy; - PDFRectangle box; - type3_font_info_t *info; - GfxFont *font; - Dict *resDict; - Gfx *gfx; - - info = (type3_font_info_t *) - cairo_font_face_get_user_data (cairo_scaled_font_get_font_face (scaled_font), - &type3_font_key); - - font = info->font; - resDict = ((Gfx8BitFont *)font)->getResources(); - charProcs = ((Gfx8BitFont *)(info->font))->getCharProcs(); - if (!charProcs) - return CAIRO_STATUS_USER_FONT_ERROR; - - if ((int)glyph >= charProcs->getLength()) - return CAIRO_STATUS_USER_FONT_ERROR; - - mat = font->getFontMatrix(); - matrix.xx = mat[0]; - matrix.yx = mat[1]; - matrix.xy = mat[2]; - matrix.yy = mat[3]; - matrix.x0 = mat[4]; - matrix.y0 = mat[5]; - cairo_matrix_init_scale (&invert_y_axis, 1, -1); - cairo_matrix_multiply (&matrix, &matrix, &invert_y_axis); - cairo_transform (cr, &matrix); - - output_dev = new CairoOutputDev(); - output_dev->setCairo(cr); - output_dev->setPrinting(info->printing); - - mat = font->getFontBBox(); - box.x1 = mat[0]; - box.y1 = mat[1]; - box.x2 = mat[2]; - box.y2 = mat[3]; - gfx = new Gfx(info->doc, output_dev, resDict, &box, NULL); - output_dev->startDoc(info->doc, info->fontEngine); - output_dev->startPage (1, gfx->getState()); - output_dev->setInType3Char(gTrue); - gfx->display(charProcs->getVal(glyph, &charProc)); - - output_dev->getType3GlyphWidth (&wx, &wy); - cairo_matrix_transform_distance (&matrix, &wx, &wy); - metrics->x_advance = wx; - metrics->y_advance = wy; - if (output_dev->hasType3GlyphBBox()) { - double *bbox = output_dev->getType3GlyphBBox(); - - cairo_matrix_transform_point (&matrix, &bbox[0], &bbox[1]); - cairo_matrix_transform_point (&matrix, &bbox[2], &bbox[3]); - metrics->x_bearing = bbox[0]; - metrics->y_bearing = bbox[1]; - metrics->width = bbox[2] - bbox[0]; - metrics->height = bbox[3] - bbox[1]; - } - - delete gfx; - delete output_dev; - charProc.free(); - - return CAIRO_STATUS_SUCCESS; -} - - -CairoType3Font *CairoType3Font::create(GfxFont *gfxFont, PDFDoc *doc, - CairoFontEngine *fontEngine, - GBool printing) { - Object refObj, strObj; - type3_font_info_t *info; - cairo_font_face_t *font_face; - Ref ref; - int *codeToGID; - Guint codeToGIDLen; - int i, j; - char **enc; - Dict *charProcs; - char *name; - - charProcs = ((Gfx8BitFont *)gfxFont)->getCharProcs(); - info = (type3_font_info_t *) malloc(sizeof(*info)); - ref = *gfxFont->getID(); - font_face = cairo_user_font_face_create(); - cairo_user_font_face_set_init_func (font_face, _init_type3_glyph); - cairo_user_font_face_set_render_glyph_func (font_face, _render_type3_glyph); - gfxFont->incRefCnt(); - info->font = gfxFont; - info->doc = doc; - info->fontEngine = fontEngine; - info->printing = printing; - - cairo_font_face_set_user_data (font_face, &type3_font_key, (void *) info, _free_type3_font_info); - - enc = ((Gfx8BitFont *)gfxFont)->getEncoding(); - codeToGID = (int *)gmallocn(256, sizeof(int)); - codeToGIDLen = 256; - for (i = 0; i < 256; ++i) { - codeToGID[i] = 0; - if (charProcs && (name = enc[i])) { - for (j = 0; j < charProcs->getLength(); j++) { - if (strcmp(name, charProcs->getKey(j)) == 0) { - codeToGID[i] = j; - } - } - } - } - - return new CairoType3Font(ref, doc, font_face, codeToGID, codeToGIDLen, printing); -} - -CairoType3Font::CairoType3Font(Ref ref, - PDFDoc *doc, - cairo_font_face_t *cairo_font_face, - int *codeToGID, - Guint codeToGIDLen, - GBool printing) : CairoFont(ref, - cairo_font_face, - codeToGID, - codeToGIDLen, - gFalse, - printing), - doc(doc) { } - -CairoType3Font::~CairoType3Font() { } - -GBool -CairoType3Font::matches(Ref &other, GBool printingA) { - return (other.num == ref.num && other.gen == ref.gen && printing == printingA); -} - - -//------------------------------------------------------------------------ -// CairoFontEngine -//------------------------------------------------------------------------ - -CairoFontEngine::CairoFontEngine(FT_Library libA) { - int i; - - lib = libA; - for (i = 0; i < cairoFontCacheSize; ++i) { - fontCache[i] = NULL; - } - - FT_Int major, minor, patch; - // as of FT 2.1.8, CID fonts are indexed by CID instead of GID - FT_Library_Version(lib, &major, &minor, &patch); - useCIDs = major > 2 || - (major == 2 && (minor > 1 || (minor == 1 && patch > 7))); -} - -CairoFontEngine::~CairoFontEngine() { - int i; - - for (i = 0; i < cairoFontCacheSize; ++i) { - if (fontCache[i]) - delete fontCache[i]; - } -} - -CairoFont * -CairoFontEngine::getFont(GfxFont *gfxFont, PDFDoc *doc, GBool printing) { - int i, j; - Ref ref; - CairoFont *font; - GfxFontType fontType; - - ref = *gfxFont->getID(); - - for (i = 0; i < cairoFontCacheSize; ++i) { - font = fontCache[i]; - if (font && font->matches(ref, printing)) { - for (j = i; j > 0; --j) { - fontCache[j] = fontCache[j-1]; - } - fontCache[0] = font; - return font; - } - } - - fontType = gfxFont->getType(); - if (fontType == fontType3) - font = CairoType3Font::create (gfxFont, doc, this, printing); - else - font = CairoFreeTypeFont::create (gfxFont, doc->getXRef(), lib, useCIDs); - - //XXX: if font is null should we still insert it into the cache? - if (fontCache[cairoFontCacheSize - 1]) { - delete fontCache[cairoFontCacheSize - 1]; - } - for (j = cairoFontCacheSize - 1; j > 0; --j) { - fontCache[j] = fontCache[j-1]; - } - fontCache[0] = font; - return font; -} diff --git a/3rdparty/poppler/0.22.5/CairoFontEngine.h b/3rdparty/poppler/0.22.5/CairoFontEngine.h deleted file mode 100644 index 6335348..0000000 --- a/3rdparty/poppler/0.22.5/CairoFontEngine.h +++ /dev/null @@ -1,124 +0,0 @@ -//======================================================================== -// -// CairoFontEngine.h -// -// Copyright 2003 Glyph & Cog, LLC -// Copyright 2004 Red Hat, Inc -// -//======================================================================== - -//======================================================================== -// -// Modified under the Poppler project - http://poppler.freedesktop.org -// -// All changes made under the Poppler project to this file are licensed -// under GPL version 2 or later -// -// Copyright (C) 2005, 2006 Kristian Høgsberg -// Copyright (C) 2005 Albert Astals Cid -// Copyright (C) 2006, 2007 Jeff Muizelaar -// Copyright (C) 2006, 2010 Carlos Garcia Campos -// Copyright (C) 2008 Adrian Johnson -// -// To see a description of the changes please see the Changelog file that -// came with your tarball or type make ChangeLog if you are building from git -// -//======================================================================== - -#ifndef CAIROFONTENGINE_H -#define CAIROFONTENGINE_H - -#ifdef USE_GCC_PRAGMAS -#pragma interface -#endif - -#include "goo/gtypes.h" -#include - -#include "GfxFont.h" -#include "PDFDoc.h" - -class CairoFontEngine; - -class CairoFont { -public: - CairoFont(Ref ref, - cairo_font_face_t *face, - int *codeToGID, - Guint codeToGIDLen, - GBool substitute, - GBool printing); - virtual ~CairoFont(); - - virtual GBool matches(Ref &other, GBool printing); - cairo_font_face_t *getFontFace(void); - unsigned long getGlyph(CharCode code, Unicode *u, int uLen); - double getSubstitutionCorrection(GfxFont *gfxFont); - - GBool isSubstitute() { return substitute; } -protected: - Ref ref; - cairo_font_face_t *cairo_font_face; - - int *codeToGID; - Guint codeToGIDLen; - - GBool substitute; - GBool printing; -}; - -//------------------------------------------------------------------------ - -class CairoFreeTypeFont : public CairoFont { -public: - static CairoFreeTypeFont *create(GfxFont *gfxFont, XRef *xref, FT_Library lib, GBool useCIDs); - virtual ~CairoFreeTypeFont(); - -private: - CairoFreeTypeFont(Ref ref, cairo_font_face_t *cairo_font_face, - int *codeToGID, Guint codeToGIDLen, GBool substitute); -}; - -//------------------------------------------------------------------------ - -class CairoType3Font : public CairoFont { -public: - static CairoType3Font *create(GfxFont *gfxFont, PDFDoc *doc, - CairoFontEngine *fontEngine, - GBool printing); - virtual ~CairoType3Font(); - - virtual GBool matches(Ref &other, GBool printing); - -private: - CairoType3Font(Ref ref, PDFDoc *doc, - cairo_font_face_t *cairo_font_face, - int *codeToGID, Guint codeToGIDLen, - GBool printing); - PDFDoc *doc; -}; - -//------------------------------------------------------------------------ - -#define cairoFontCacheSize 64 - -//------------------------------------------------------------------------ -// CairoFontEngine -//------------------------------------------------------------------------ - -class CairoFontEngine { -public: - - // Create a font engine. - CairoFontEngine(FT_Library libA); - ~CairoFontEngine(); - - CairoFont *getFont(GfxFont *gfxFont, PDFDoc *doc, GBool printing); - -private: - CairoFont *fontCache[cairoFontCacheSize]; - FT_Library lib; - GBool useCIDs; -}; - -#endif diff --git a/3rdparty/poppler/0.22.5/CairoOutputDev.cc b/3rdparty/poppler/0.22.5/CairoOutputDev.cc deleted file mode 100644 index 5a62fe2..0000000 --- a/3rdparty/poppler/0.22.5/CairoOutputDev.cc +++ /dev/null @@ -1,3200 +0,0 @@ -//======================================================================== -// -// CairoOutputDev.cc -// -// Copyright 2003 Glyph & Cog, LLC -// Copyright 2004 Red Hat, Inc -// -//======================================================================== - -//======================================================================== -// -// Modified under the Poppler project - http://poppler.freedesktop.org -// -// All changes made under the Poppler project to this file are licensed -// under GPL version 2 or later -// -// Copyright (C) 2005-2008 Jeff Muizelaar -// Copyright (C) 2005, 2006 Kristian Høgsberg -// Copyright (C) 2005, 2009, 2012 Albert Astals Cid -// Copyright (C) 2005 Nickolay V. Shmyrev -// Copyright (C) 2006-2011, 2013 Carlos Garcia Campos -// Copyright (C) 2008 Carl Worth -// Copyright (C) 2008-2013 Adrian Johnson -// Copyright (C) 2008 Michael Vrable -// Copyright (C) 2008, 2009 Chris Wilson -// Copyright (C) 2008, 2012 Hib Eris -// Copyright (C) 2009, 2010 David Benjamin -// Copyright (C) 2011, 2012 Thomas Freitag -// Copyright (C) 2012 Patrick Pfeifer -// Copyright (C) 2012 Jason Crain -// -// To see a description of the changes please see the Changelog file that -// came with your tarball or type make ChangeLog if you are building from git -// -//======================================================================== - -#include - -#ifdef USE_GCC_PRAGMAS -#pragma implementation -#endif - -#include -#include -#include -#include -#include - -#include "goo/gfile.h" -#include "GlobalParams.h" -#include "Error.h" -#include "Object.h" -#include "Gfx.h" -#include "GfxState.h" -#include "GfxFont.h" -#include "Page.h" -#include "Link.h" -#include "FontEncodingTables.h" -#include "PDFDocEncoding.h" -#include -#include -#include "CairoOutputDev.h" -#include "CairoFontEngine.h" -#include "CairoRescaleBox.h" -#include "UnicodeMap.h" -//------------------------------------------------------------------------ - -// #define LOG_CAIRO - -#ifdef LOG_CAIRO -#define LOG(x) (x) -#else -#define LOG(x) -#endif - -static inline void printMatrix(cairo_matrix_t *matrix){ - printf("%f %f, %f %f (%f %f)\n", matrix->xx, matrix->yx, - matrix->xy, matrix->yy, - matrix->x0, matrix->y0); -} - - -#define MIN(a,b) (((a) < (b)) ? (a) : (b)) -#define MAX(a,b) (((a) > (b)) ? (a) : (b)) - - -//------------------------------------------------------------------------ -// CairoImage -//------------------------------------------------------------------------ - -CairoImage::CairoImage (double x1, double y1, double x2, double y2) { - this->image = NULL; - this->x1 = x1; - this->y1 = y1; - this->x2 = x2; - this->y2 = y2; -} - -CairoImage::~CairoImage () { - if (image) - cairo_surface_destroy (image); -} - -void CairoImage::setImage (cairo_surface_t *image) { - if (this->image) - cairo_surface_destroy (this->image); - this->image = cairo_surface_reference (image); -} - -//------------------------------------------------------------------------ -// CairoOutputDev -//------------------------------------------------------------------------ - -// We cannot tie the lifetime of an FT_Library object to that of -// CairoOutputDev, since any FT_Faces created with it may end up with a -// reference by Cairo which can be held long after the CairoOutputDev is -// deleted. The simplest way to avoid problems is to never tear down the -// FT_Library instance; to avoid leaks, just use a single global instance -// initialized the first time it is needed. -FT_Library CairoOutputDev::ft_lib; -GBool CairoOutputDev::ft_lib_initialized = gFalse; - -CairoOutputDev::CairoOutputDev() { - doc = NULL; - - if (!ft_lib_initialized) { - FT_Init_FreeType(&ft_lib); - ft_lib_initialized = gTrue; - } - - fontEngine = NULL; - fontEngine_owner = gFalse; - glyphs = NULL; - fill_pattern = NULL; - fill_color.r = fill_color.g = fill_color.b = 0; - stroke_pattern = NULL; - stroke_color.r = stroke_color.g = stroke_color.b = 0; - stroke_opacity = 1.0; - fill_opacity = 1.0; - textClipPath = NULL; - strokePathClip = NULL; - cairo = NULL; - currentFont = NULL; - prescaleImages = gTrue; - printing = gTrue; - use_show_text_glyphs = gFalse; - inType3Char = gFalse; - t3_glyph_has_bbox = gFalse; - - groupColorSpaceStack = NULL; - maskStack = NULL; - group = NULL; - mask = NULL; - shape = NULL; - cairo_shape = NULL; - knockoutCount = 0; - - text = NULL; - actualText = NULL; - - // the SA parameter supposedly defaults to false, but Acrobat - // apparently hardwires it to true - stroke_adjust = globalParams->getStrokeAdjust(); - align_stroke_coords = gFalse; - adjusted_stroke_width = gFalse; -} - -CairoOutputDev::~CairoOutputDev() { - if (fontEngine_owner && fontEngine) { - delete fontEngine; - } - - if (cairo) - cairo_destroy (cairo); - cairo_pattern_destroy (stroke_pattern); - cairo_pattern_destroy (fill_pattern); - if (group) - cairo_pattern_destroy (group); - if (mask) - cairo_pattern_destroy (mask); - if (shape) - cairo_pattern_destroy (shape); - if (text) - text->decRefCnt(); - if (actualText) - delete actualText; -} - -void CairoOutputDev::setCairo(cairo_t *cairo) -{ - if (this->cairo != NULL) { - cairo_status_t status = cairo_status (this->cairo); - if (status) { - error(errInternal, -1, "cairo context error: {0:s}\n", cairo_status_to_string(status)); - } - cairo_destroy (this->cairo); - assert(!cairo_shape); - } - if (cairo != NULL) { - this->cairo = cairo_reference (cairo); - /* save the initial matrix so that we can use it for type3 fonts. */ - //XXX: is this sufficient? could we miss changes to the matrix somehow? - cairo_get_matrix(cairo, &orig_matrix); - } else { - this->cairo = NULL; - this->cairo_shape = NULL; - } -} - -void CairoOutputDev::setTextPage(TextPage *text) -{ - if (this->text) - this->text->decRefCnt(); - if (actualText) - delete actualText; - if (text) { - this->text = text; - this->text->incRefCnt(); - actualText = new ActualText(text); - } else { - this->text = NULL; - actualText = NULL; - } -} - -void CairoOutputDev::startDoc(PDFDoc *docA, - CairoFontEngine *parentFontEngine) { - doc = docA; - if (parentFontEngine) { - fontEngine = parentFontEngine; - } else { - if (fontEngine) { - delete fontEngine; - } - fontEngine = new CairoFontEngine(ft_lib); - fontEngine_owner = gTrue; - } -} - -void CairoOutputDev::startPage(int pageNum, GfxState *state) { - /* set up some per page defaults */ - cairo_pattern_destroy(fill_pattern); - cairo_pattern_destroy(stroke_pattern); - - fill_pattern = cairo_pattern_create_rgb(0., 0., 0.); - fill_color.r = fill_color.g = fill_color.b = 0; - stroke_pattern = cairo_pattern_reference(fill_pattern); - stroke_color.r = stroke_color.g = stroke_color.b = 0; - - if (text) - text->startPage(state); -} - -void CairoOutputDev::endPage() { - if (text) { - text->endPage(); - text->coalesce(gTrue, 0, gFalse); - } -} - -void CairoOutputDev::saveState(GfxState *state) { - LOG(printf ("save\n")); - cairo_save (cairo); - if (cairo_shape) - cairo_save (cairo_shape); - - MaskStack *ms = new MaskStack; - ms->mask = cairo_pattern_reference(mask); - ms->mask_matrix = mask_matrix; - ms->next = maskStack; - maskStack = ms; -} - -void CairoOutputDev::restoreState(GfxState *state) { - LOG(printf ("restore\n")); - cairo_restore (cairo); - if (cairo_shape) - cairo_restore (cairo_shape); - - /* These aren't restored by cairo_restore() since we keep them in - * the output device. */ - updateFillColor(state); - updateStrokeColor(state); - updateFillOpacity(state); - updateStrokeOpacity(state); - updateBlendMode(state); - - MaskStack* ms = maskStack; - if (ms) { - if (mask) - cairo_pattern_destroy(mask); - mask = ms->mask; - mask_matrix = ms->mask_matrix; - maskStack = ms->next; - delete ms; - } -} - -void CairoOutputDev::updateAll(GfxState *state) { - updateLineDash(state); - updateLineJoin(state); - updateLineCap(state); - updateLineWidth(state); - updateFlatness(state); - updateMiterLimit(state); - updateFillColor(state); - updateStrokeColor(state); - updateFillOpacity(state); - updateStrokeOpacity(state); - updateBlendMode(state); - needFontUpdate = gTrue; - if (text) - text->updateFont(state); -} - -void CairoOutputDev::setDefaultCTM(double *ctm) { - cairo_matrix_t matrix; - matrix.xx = ctm[0]; - matrix.yx = ctm[1]; - matrix.xy = ctm[2]; - matrix.yy = ctm[3]; - matrix.x0 = ctm[4]; - matrix.y0 = ctm[5]; - - cairo_transform (cairo, &matrix); - if (cairo_shape) - cairo_transform (cairo_shape, &matrix); - - OutputDev::setDefaultCTM(ctm); -} - -void CairoOutputDev::updateCTM(GfxState *state, double m11, double m12, - double m21, double m22, - double m31, double m32) { - cairo_matrix_t matrix, invert_matrix; - matrix.xx = m11; - matrix.yx = m12; - matrix.xy = m21; - matrix.yy = m22; - matrix.x0 = m31; - matrix.y0 = m32; - - /* Make sure the matrix is invertible before setting it. - * cairo will blow up if we give it a matrix that's not - * invertible, so we need to check before passing it - * to cairo_transform. Ignoring it is likely to give better - * results than not rendering anything at all. See #14398 - * - * Ideally, we could do the cairo_transform - * and then check if anything went wrong and fix it then - * instead of having to invert the matrix. */ - invert_matrix = matrix; - if (cairo_matrix_invert(&invert_matrix)) { - error(errSyntaxWarning, -1, "matrix not invertible\n"); - return; - } - - cairo_transform (cairo, &matrix); - if (cairo_shape) - cairo_transform (cairo_shape, &matrix); - updateLineDash(state); - updateLineJoin(state); - updateLineCap(state); - updateLineWidth(state); -} - -void CairoOutputDev::updateLineDash(GfxState *state) { - double *dashPattern; - int dashLength; - double dashStart; - - state->getLineDash(&dashPattern, &dashLength, &dashStart); - cairo_set_dash (cairo, dashPattern, dashLength, dashStart); - if (cairo_shape) - cairo_set_dash (cairo_shape, dashPattern, dashLength, dashStart); -} - -void CairoOutputDev::updateFlatness(GfxState *state) { - // cairo_set_tolerance (cairo, state->getFlatness()); -} - -void CairoOutputDev::updateLineJoin(GfxState *state) { - switch (state->getLineJoin()) { - case 0: - cairo_set_line_join (cairo, CAIRO_LINE_JOIN_MITER); - break; - case 1: - cairo_set_line_join (cairo, CAIRO_LINE_JOIN_ROUND); - break; - case 2: - cairo_set_line_join (cairo, CAIRO_LINE_JOIN_BEVEL); - break; - } - if (cairo_shape) - cairo_set_line_join (cairo_shape, cairo_get_line_join(cairo)); -} - -void CairoOutputDev::updateLineCap(GfxState *state) { - switch (state->getLineCap()) { - case 0: - cairo_set_line_cap (cairo, CAIRO_LINE_CAP_BUTT); - break; - case 1: - cairo_set_line_cap (cairo, CAIRO_LINE_CAP_ROUND); - break; - case 2: - cairo_set_line_cap (cairo, CAIRO_LINE_CAP_SQUARE); - break; - } - if (cairo_shape) - cairo_set_line_cap (cairo_shape, cairo_get_line_cap(cairo)); -} - -void CairoOutputDev::updateMiterLimit(GfxState *state) { - cairo_set_miter_limit (cairo, state->getMiterLimit()); - if (cairo_shape) - cairo_set_miter_limit (cairo_shape, state->getMiterLimit()); -} - -void CairoOutputDev::updateLineWidth(GfxState *state) { - LOG(printf ("line width: %f\n", state->getLineWidth())); - adjusted_stroke_width = gFalse; - double width = state->getLineWidth(); - if (stroke_adjust && !printing) { - double x, y; - x = y = width; - - /* find out line width in device units */ - cairo_user_to_device_distance(cairo, &x, &y); - if (fabs(x) <= 1.0 && fabs(y) <= 1.0) { - /* adjust width to at least one device pixel */ - x = y = 1.0; - cairo_device_to_user_distance(cairo, &x, &y); - width = MIN(fabs(x),fabs(y)); - adjusted_stroke_width = gTrue; - } - } else if (width == 0.0) { - /* Cairo does not support 0 line width == 1 device pixel. Find out - * how big pixels (device unit) are in the x and y - * directions. Choose the smaller of the two as our line width. - */ - double x = 1.0, y = 1.0; - if (printing) { - // assume printer pixel size is 1/600 inch - x = 72.0/600; - y = 72.0/600; - } - cairo_device_to_user_distance(cairo, &x, &y); - width = MIN(fabs(x),fabs(y)); - } - cairo_set_line_width (cairo, width); - if (cairo_shape) - cairo_set_line_width (cairo_shape, cairo_get_line_width (cairo)); -} - -void CairoOutputDev::updateFillColor(GfxState *state) { - GfxRGB color = fill_color; - - state->getFillRGB(&fill_color); - if (cairo_pattern_get_type (fill_pattern) != CAIRO_PATTERN_TYPE_SOLID || - color.r != fill_color.r || - color.g != fill_color.g || - color.b != fill_color.b) - { - cairo_pattern_destroy(fill_pattern); - fill_pattern = cairo_pattern_create_rgba(colToDbl(fill_color.r), - colToDbl(fill_color.g), - colToDbl(fill_color.b), - fill_opacity); - - LOG(printf ("fill color: %d %d %d\n", - fill_color.r, fill_color.g, fill_color.b)); - } -} - -void CairoOutputDev::updateStrokeColor(GfxState *state) { - GfxRGB color = stroke_color; - - state->getStrokeRGB(&stroke_color); - if (cairo_pattern_get_type (fill_pattern) != CAIRO_PATTERN_TYPE_SOLID || - color.r != stroke_color.r || - color.g != stroke_color.g || - color.b != stroke_color.b) - { - cairo_pattern_destroy(stroke_pattern); - stroke_pattern = cairo_pattern_create_rgba(colToDbl(stroke_color.r), - colToDbl(stroke_color.g), - colToDbl(stroke_color.b), - stroke_opacity); - - LOG(printf ("stroke color: %d %d %d\n", - stroke_color.r, stroke_color.g, stroke_color.b)); - } -} - -void CairoOutputDev::updateFillOpacity(GfxState *state) { - double opacity = fill_opacity; - - fill_opacity = state->getFillOpacity(); - if (opacity != fill_opacity) { - cairo_pattern_destroy(fill_pattern); - fill_pattern = cairo_pattern_create_rgba(colToDbl(fill_color.r), - colToDbl(fill_color.g), - colToDbl(fill_color.b), - fill_opacity); - - LOG(printf ("fill opacity: %f\n", fill_opacity)); - } -} - -void CairoOutputDev::updateStrokeOpacity(GfxState *state) { - double opacity = stroke_opacity; - - stroke_opacity = state->getStrokeOpacity(); - if (opacity != stroke_opacity) { - cairo_pattern_destroy(stroke_pattern); - stroke_pattern = cairo_pattern_create_rgba(colToDbl(stroke_color.r), - colToDbl(stroke_color.g), - colToDbl(stroke_color.b), - stroke_opacity); - - LOG(printf ("stroke opacity: %f\n", stroke_opacity)); - } -} - -void CairoOutputDev::updateFillColorStop(GfxState *state, double offset) { - state->getFillRGB(&fill_color); - - cairo_pattern_add_color_stop_rgba(fill_pattern, offset, - colToDbl(fill_color.r), - colToDbl(fill_color.g), - colToDbl(fill_color.b), - fill_opacity); - LOG(printf ("fill color stop: %f (%d, %d, %d)\n", - offset, fill_color.r, fill_color.g, fill_color.b)); -} - -void CairoOutputDev::updateBlendMode(GfxState *state) { - switch (state->getBlendMode()) { - default: - case gfxBlendNormal: - cairo_set_operator (cairo, CAIRO_OPERATOR_OVER); - break; - case gfxBlendMultiply: - cairo_set_operator (cairo, CAIRO_OPERATOR_MULTIPLY); - break; - case gfxBlendScreen: - cairo_set_operator (cairo, CAIRO_OPERATOR_SCREEN); - break; - case gfxBlendOverlay: - cairo_set_operator (cairo, CAIRO_OPERATOR_OVERLAY); - break; - case gfxBlendDarken: - cairo_set_operator (cairo, CAIRO_OPERATOR_DARKEN); - break; - case gfxBlendLighten: - cairo_set_operator (cairo, CAIRO_OPERATOR_LIGHTEN); - break; - case gfxBlendColorDodge: - cairo_set_operator (cairo, CAIRO_OPERATOR_COLOR_DODGE); - break; - case gfxBlendColorBurn: - cairo_set_operator (cairo, CAIRO_OPERATOR_COLOR_BURN); - break; - case gfxBlendHardLight: - cairo_set_operator (cairo, CAIRO_OPERATOR_HARD_LIGHT); - break; - case gfxBlendSoftLight: - cairo_set_operator (cairo, CAIRO_OPERATOR_SOFT_LIGHT); - break; - case gfxBlendDifference: - cairo_set_operator (cairo, CAIRO_OPERATOR_DIFFERENCE); - break; - case gfxBlendExclusion: - cairo_set_operator (cairo, CAIRO_OPERATOR_EXCLUSION); - break; - case gfxBlendHue: - cairo_set_operator (cairo, CAIRO_OPERATOR_HSL_HUE); - break; - case gfxBlendSaturation: - cairo_set_operator (cairo, CAIRO_OPERATOR_HSL_SATURATION); - break; - case gfxBlendColor: - cairo_set_operator (cairo, CAIRO_OPERATOR_HSL_COLOR); - break; - case gfxBlendLuminosity: - cairo_set_operator (cairo, CAIRO_OPERATOR_HSL_LUMINOSITY); - break; - } - LOG(printf ("blend mode: %d\n", (int)state->getBlendMode())); -} - -void CairoOutputDev::updateFont(GfxState *state) { - cairo_font_face_t *font_face; - cairo_matrix_t matrix, invert_matrix; - - LOG(printf ("updateFont() font=%s\n", state->getFont()->getName()->getCString())); - - needFontUpdate = gFalse; - - //FIXME: use cairo font engine? - if (text) - text->updateFont(state); - - currentFont = fontEngine->getFont (state->getFont(), doc, printing); - - if (!currentFont) - return; - - font_face = currentFont->getFontFace(); - cairo_set_font_face (cairo, font_face); - - use_show_text_glyphs = state->getFont()->hasToUnicodeCMap() && - cairo_surface_has_show_text_glyphs (cairo_get_target (cairo)); - - double fontSize = state->getFontSize(); - double *m = state->getTextMat(); - /* NOTE: adjusting by a constant is hack. The correct solution - * is probably to use user-fonts and compute the scale on a per - * glyph basis instead of for the entire font */ - double w = currentFont->getSubstitutionCorrection(state->getFont()); - matrix.xx = m[0] * fontSize * state->getHorizScaling() * w; - matrix.yx = m[1] * fontSize * state->getHorizScaling() * w; - matrix.xy = -m[2] * fontSize; - matrix.yy = -m[3] * fontSize; - matrix.x0 = 0; - matrix.y0 = 0; - - LOG(printf ("font matrix: %f %f %f %f\n", matrix.xx, matrix.yx, matrix.xy, matrix.yy)); - - /* Make sure the font matrix is invertible before setting it. cairo - * will blow up if we give it a matrix that's not invertible, so we - * need to check before passing it to cairo_set_font_matrix. Ignoring it - * is likely to give better results than not rendering anything at - * all. See #18254. - */ - invert_matrix = matrix; - if (cairo_matrix_invert(&invert_matrix)) { - error(errSyntaxWarning, -1, "font matrix not invertible\n"); - return; - } - - cairo_set_font_matrix (cairo, &matrix); -} - -/* Tolerance in pixels for checking if strokes are horizontal or vertical - * lines in device space */ -#define STROKE_COORD_TOLERANCE 0.5 - -/* Align stroke coordinate i if the point is the start or end of a - * horizontal or vertical line */ -void CairoOutputDev::alignStrokeCoords(GfxSubpath *subpath, int i, double *x, double *y) -{ - double x1, y1, x2, y2; - GBool align = gFalse; - - x1 = subpath->getX(i); - y1 = subpath->getY(i); - cairo_user_to_device (cairo, &x1, &y1); - - // Does the current coord and prev coord form a horiz or vert line? - if (i > 0 && !subpath->getCurve(i - 1)) { - x2 = subpath->getX(i - 1); - y2 = subpath->getY(i - 1); - cairo_user_to_device (cairo, &x2, &y2); - if (fabs(x2 - x1) < STROKE_COORD_TOLERANCE || fabs(y2 - y1) < STROKE_COORD_TOLERANCE) - align = gTrue; - } - - // Does the current coord and next coord form a horiz or vert line? - if (i < subpath->getNumPoints() - 1 && !subpath->getCurve(i + 1)) { - x2 = subpath->getX(i + 1); - y2 = subpath->getY(i + 1); - cairo_user_to_device (cairo, &x2, &y2); - if (fabs(x2 - x1) < STROKE_COORD_TOLERANCE || fabs(y2 - y1) < STROKE_COORD_TOLERANCE) - align = gTrue; - } - - *x = subpath->getX(i); - *y = subpath->getY(i); - if (align) { - /* see http://www.cairographics.org/FAQ/#sharp_lines */ - cairo_user_to_device (cairo, x, y); - *x = floor(*x) + 0.5; - *y = floor(*y) + 0.5; - cairo_device_to_user (cairo, x, y); - } -} - -#undef STROKE_COORD_TOLERANCE - -void CairoOutputDev::doPath(cairo_t *cairo, GfxState *state, GfxPath *path) { - GfxSubpath *subpath; - int i, j; - double x, y; - cairo_new_path (cairo); - for (i = 0; i < path->getNumSubpaths(); ++i) { - subpath = path->getSubpath(i); - if (subpath->getNumPoints() > 0) { - if (align_stroke_coords) { - alignStrokeCoords(subpath, 0, &x, &y); - } else { - x = subpath->getX(0); - y = subpath->getY(0); - } - cairo_move_to (cairo, x, y); - j = 1; - while (j < subpath->getNumPoints()) { - if (subpath->getCurve(j)) { - if (align_stroke_coords) { - alignStrokeCoords(subpath, j + 2, &x, &y); - } else { - x = subpath->getX(j+2); - y = subpath->getY(j+2); - } - cairo_curve_to( cairo, - subpath->getX(j), subpath->getY(j), - subpath->getX(j+1), subpath->getY(j+1), - x, y); - - j += 3; - } else { - if (align_stroke_coords) { - alignStrokeCoords(subpath, j, &x, &y); - } else { - x = subpath->getX(j); - y = subpath->getY(j); - } - cairo_line_to (cairo, x, y); - ++j; - } - } - if (subpath->isClosed()) { - LOG (printf ("close\n")); - cairo_close_path (cairo); - } - } - } -} - -void CairoOutputDev::stroke(GfxState *state) { - if (inType3Char) { - GfxGray gray; - state->getFillGray(&gray); - if (colToDbl(gray) > 0.5) - return; - } - - if (adjusted_stroke_width) - align_stroke_coords = gTrue; - doPath (cairo, state, state->getPath()); - align_stroke_coords = gFalse; - cairo_set_source (cairo, stroke_pattern); - LOG(printf ("stroke\n")); - cairo_stroke (cairo); - if (cairo_shape) { - doPath (cairo_shape, state, state->getPath()); - cairo_stroke (cairo_shape); - } -} - -void CairoOutputDev::fill(GfxState *state) { - if (inType3Char) { - GfxGray gray; - state->getFillGray(&gray); - if (colToDbl(gray) > 0.5) - return; - } - - doPath (cairo, state, state->getPath()); - cairo_set_fill_rule (cairo, CAIRO_FILL_RULE_WINDING); - cairo_set_source (cairo, fill_pattern); - LOG(printf ("fill\n")); - //XXX: how do we get the path - if (mask) { - cairo_save (cairo); - cairo_clip (cairo); - cairo_set_matrix (cairo, &mask_matrix); - cairo_mask (cairo, mask); - cairo_restore (cairo); - } else if (strokePathClip) { - fillToStrokePathClip(state); - } else { - cairo_fill (cairo); - } - if (cairo_shape) { - cairo_set_fill_rule (cairo_shape, CAIRO_FILL_RULE_WINDING); - doPath (cairo_shape, state, state->getPath()); - cairo_fill (cairo_shape); - } -} - -void CairoOutputDev::eoFill(GfxState *state) { - doPath (cairo, state, state->getPath()); - cairo_set_fill_rule (cairo, CAIRO_FILL_RULE_EVEN_ODD); - cairo_set_source (cairo, fill_pattern); - LOG(printf ("fill-eo\n")); - cairo_fill (cairo); - - if (cairo_shape) { - cairo_set_fill_rule (cairo_shape, CAIRO_FILL_RULE_EVEN_ODD); - doPath (cairo_shape, state, state->getPath()); - cairo_fill (cairo_shape); - } - -} - -GBool CairoOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx1, Catalog *cat, Object *str, - double *pmat, int paintType, int /*tilingType*/, Dict *resDict, - double *mat, double *bbox, - int x0, int y0, int x1, int y1, - double xStep, double yStep) -{ - PDFRectangle box; - Gfx *gfx; - cairo_pattern_t *pattern; - cairo_surface_t *surface; - cairo_matrix_t matrix; - cairo_t *old_cairo; - double xMin, yMin, xMax, yMax; - double width, height; - int surface_width, surface_height; - StrokePathClip *strokePathTmp; - - width = bbox[2] - bbox[0]; - height = bbox[3] - bbox[1]; - - if (xStep != width || yStep != height) - return gFalse; - /* TODO: implement the other cases here too */ - - surface_width = (int) ceil (width); - surface_height = (int) ceil (height); - - surface = cairo_surface_create_similar (cairo_get_target (cairo), - CAIRO_CONTENT_COLOR_ALPHA, - surface_width, surface_height); - if (cairo_surface_status (surface)) - return gFalse; - - old_cairo = cairo; - cairo = cairo_create (surface); - cairo_surface_destroy (surface); - - box.x1 = bbox[0]; box.y1 = bbox[1]; - box.x2 = bbox[2]; box.y2 = bbox[3]; - strokePathTmp = strokePathClip; - strokePathClip = NULL; - gfx = new Gfx(doc, this, resDict, &box, NULL); - gfx->display(str); - delete gfx; - strokePathClip = strokePathTmp; - - pattern = cairo_pattern_create_for_surface (cairo_get_target (cairo)); - cairo_destroy (cairo); - cairo = old_cairo; - if (cairo_pattern_status (pattern)) - return gFalse; - - state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax); - cairo_rectangle (cairo, xMin, yMin, xMax - xMin, yMax - yMin); - - cairo_matrix_init_scale (&matrix, surface_width / width, surface_height / height); - cairo_pattern_set_matrix (pattern, &matrix); - - cairo_matrix_init (&matrix, mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]); - cairo_transform (cairo, &matrix); - cairo_set_source (cairo, pattern); - cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT); - if (strokePathClip) { - fillToStrokePathClip(state); - } else { - cairo_fill (cairo); - } - - cairo_pattern_destroy (pattern); - - return gTrue; -} - -GBool CairoOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading, double tMin, double tMax) { - double x0, y0, x1, y1; - double dx, dy; - - shading->getCoords(&x0, &y0, &x1, &y1); - dx = x1 - x0; - dy = y1 - y0; - - cairo_pattern_destroy(fill_pattern); - fill_pattern = cairo_pattern_create_linear (x0 + tMin * dx, y0 + tMin * dy, - x0 + tMax * dx, y0 + tMax * dy); - if (!shading->getExtend0() && !shading->getExtend1()) - cairo_pattern_set_extend (fill_pattern, CAIRO_EXTEND_NONE); - else - cairo_pattern_set_extend (fill_pattern, CAIRO_EXTEND_PAD); - - LOG (printf ("axial-sh\n")); - - // TODO: use the actual stops in the shading in the case - // of linear interpolation (Type 2 Exponential functions with N=1) - return gFalse; -} - -GBool CairoOutputDev::axialShadedSupportExtend(GfxState *state, GfxAxialShading *shading) -{ - return (shading->getExtend0() == shading->getExtend1()); -} - -GBool CairoOutputDev::radialShadedFill(GfxState *state, GfxRadialShading *shading, double sMin, double sMax) { - double x0, y0, r0, x1, y1, r1; - double dx, dy, dr; - - shading->getCoords(&x0, &y0, &r0, &x1, &y1, &r1); - dx = x1 - x0; - dy = y1 - y0; - dr = r1 - r0; - cairo_pattern_destroy(fill_pattern); - fill_pattern = cairo_pattern_create_radial (x0 + sMin * dx, - y0 + sMin * dy, - r0 + sMin * dr, - x0 + sMax * dx, - y0 + sMax * dy, - r0 + sMax * dr); - if (shading->getExtend0() && shading->getExtend1()) - cairo_pattern_set_extend (fill_pattern, CAIRO_EXTEND_PAD); - else - cairo_pattern_set_extend (fill_pattern, CAIRO_EXTEND_NONE); - - LOG (printf ("radial-sh\n")); - - return gFalse; -} - -GBool CairoOutputDev::radialShadedSupportExtend(GfxState *state, GfxRadialShading *shading) -{ - return (shading->getExtend0() == shading->getExtend1()); -} - -#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 12, 0) -GBool CairoOutputDev::gouraudTriangleShadedFill(GfxState *state, GfxGouraudTriangleShading *shading) -{ - double x0, y0, x1, y1, x2, y2; - GfxColor color[3]; - int i, j; - GfxRGB rgb; - - cairo_pattern_destroy(fill_pattern); - fill_pattern = cairo_pattern_create_mesh (); - - for (i = 0; i < shading->getNTriangles(); i++) { - if (shading->isParameterized()) { - double color0, color1, color2; - shading->getTriangle(i, &x0, &y0, &color0, - &x1, &y1, &color1, - &x2, &y2, &color2); - shading->getParameterizedColor(color0, &color[0]); - shading->getParameterizedColor(color1, &color[1]); - shading->getParameterizedColor(color2, &color[2]); - } else { - shading->getTriangle(i, - &x0, &y0, &color[0], - &x1, &y1, &color[1], - &x2, &y2, &color[2]); - - } - - cairo_mesh_pattern_begin_patch (fill_pattern); - - cairo_mesh_pattern_move_to (fill_pattern, x0, y0); - cairo_mesh_pattern_line_to (fill_pattern, x1, y1); - cairo_mesh_pattern_line_to (fill_pattern, x2, y2); - - for (j = 0; j < 3; j++) { - shading->getColorSpace()->getRGB(&color[j], &rgb); - cairo_mesh_pattern_set_corner_color_rgb (fill_pattern, j, - colToDbl(rgb.r), - colToDbl(rgb.g), - colToDbl(rgb.b)); - } - - cairo_mesh_pattern_end_patch (fill_pattern); - } - - double xMin, yMin, xMax, yMax; - // get the clip region bbox - state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax); - state->moveTo(xMin, yMin); - state->lineTo(xMin, yMax); - state->lineTo(xMax, yMax); - state->lineTo(xMax, yMin); - state->closePath(); - fill(state); - state->clearPath(); - - return gTrue; -} - -GBool CairoOutputDev::patchMeshShadedFill(GfxState *state, GfxPatchMeshShading *shading) -{ - int i, j, k; - - cairo_pattern_destroy(fill_pattern); - fill_pattern = cairo_pattern_create_mesh (); - - for (i = 0; i < shading->getNPatches(); i++) { - GfxPatch *patch = shading->getPatch(i); - GfxColor color; - GfxRGB rgb; - - cairo_mesh_pattern_begin_patch (fill_pattern); - - cairo_mesh_pattern_move_to (fill_pattern, patch->x[0][0], patch->y[0][0]); - cairo_mesh_pattern_curve_to (fill_pattern, - patch->x[0][1], patch->y[0][1], - patch->x[0][2], patch->y[0][2], - patch->x[0][3], patch->y[0][3]); - - cairo_mesh_pattern_curve_to (fill_pattern, - patch->x[1][3], patch->y[1][3], - patch->x[2][3], patch->y[2][3], - patch->x[3][3], patch->y[3][3]); - - cairo_mesh_pattern_curve_to (fill_pattern, - patch->x[3][2], patch->y[3][2], - patch->x[3][1], patch->y[3][1], - patch->x[3][0], patch->y[3][0]); - - cairo_mesh_pattern_curve_to (fill_pattern, - patch->x[2][0], patch->y[2][0], - patch->x[1][0], patch->y[1][0], - patch->x[0][0], patch->y[0][0]); - - cairo_mesh_pattern_set_control_point (fill_pattern, 0, patch->x[1][1], patch->y[1][1]); - cairo_mesh_pattern_set_control_point (fill_pattern, 1, patch->x[1][2], patch->y[1][2]); - cairo_mesh_pattern_set_control_point (fill_pattern, 2, patch->x[2][2], patch->y[2][2]); - cairo_mesh_pattern_set_control_point (fill_pattern, 3, patch->x[2][1], patch->y[2][1]); - - for (j = 0; j < 4; j++) { - int u, v; - - switch (j) { - case 0: - u = 0; v = 0; - break; - case 1: - u = 0; v = 1; - break; - case 2: - u = 1; v = 1; - break; - case 3: - u = 1; v = 0; - break; - } - - if (shading->isParameterized()) { - shading->getParameterizedColor (patch->color[u][v].c[0], &color); - } else { - for (k = 0; k < shading->getColorSpace()->getNComps(); k++) { - // simply cast to the desired type; that's all what is needed. - color.c[k] = GfxColorComp (patch->color[u][v].c[k]); - } - } - - shading->getColorSpace()->getRGB(&color, &rgb); - cairo_mesh_pattern_set_corner_color_rgb (fill_pattern, j, - colToDbl(rgb.r), - colToDbl(rgb.g), - colToDbl(rgb.b)); - } - cairo_mesh_pattern_end_patch (fill_pattern); - } - - double xMin, yMin, xMax, yMax; - // get the clip region bbox - state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax); - state->moveTo(xMin, yMin); - state->lineTo(xMin, yMax); - state->lineTo(xMax, yMax); - state->lineTo(xMax, yMin); - state->closePath(); - fill(state); - state->clearPath(); - - return gTrue; -} -#endif /* CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 12, 0) */ - -void CairoOutputDev::clip(GfxState *state) { - doPath (cairo, state, state->getPath()); - cairo_set_fill_rule (cairo, CAIRO_FILL_RULE_WINDING); - cairo_clip (cairo); - LOG (printf ("clip\n")); - if (cairo_shape) { - doPath (cairo_shape, state, state->getPath()); - cairo_set_fill_rule (cairo_shape, CAIRO_FILL_RULE_WINDING); - cairo_clip (cairo_shape); - } -} - -void CairoOutputDev::eoClip(GfxState *state) { - doPath (cairo, state, state->getPath()); - cairo_set_fill_rule (cairo, CAIRO_FILL_RULE_EVEN_ODD); - cairo_clip (cairo); - LOG (printf ("clip-eo\n")); - if (cairo_shape) { - doPath (cairo_shape, state, state->getPath()); - cairo_set_fill_rule (cairo_shape, CAIRO_FILL_RULE_EVEN_ODD); - cairo_clip (cairo_shape); - } - -} - -void CairoOutputDev::clipToStrokePath(GfxState *state) { - LOG(printf("clip-to-stroke-path\n")); - strokePathClip = (StrokePathClip*)gmalloc (sizeof(*strokePathClip)); - strokePathClip->path = state->getPath()->copy(); - cairo_get_matrix (cairo, &strokePathClip->ctm); - strokePathClip->line_width = cairo_get_line_width (cairo); - strokePathClip->dash_count = cairo_get_dash_count (cairo); - if (strokePathClip->dash_count) { - strokePathClip->dashes = (double*) gmallocn (sizeof(double), strokePathClip->dash_count); - cairo_get_dash (cairo, strokePathClip->dashes, &strokePathClip->dash_offset); - } else { - strokePathClip->dashes = NULL; - } - strokePathClip->cap = cairo_get_line_cap (cairo); - strokePathClip->join = cairo_get_line_join (cairo); - strokePathClip->miter = cairo_get_miter_limit (cairo); -} - -void CairoOutputDev::fillToStrokePathClip(GfxState *state) { - cairo_save (cairo); - - cairo_set_matrix (cairo, &strokePathClip->ctm); - cairo_set_line_width (cairo, strokePathClip->line_width); - strokePathClip->dash_count = cairo_get_dash_count (cairo); - cairo_set_dash (cairo, strokePathClip->dashes, strokePathClip->dash_count, strokePathClip->dash_offset); - cairo_set_line_cap (cairo, strokePathClip->cap); - cairo_set_line_join (cairo, strokePathClip->join); - cairo_set_miter_limit (cairo, strokePathClip->miter); - doPath (cairo, state, strokePathClip->path); - cairo_stroke (cairo); - - cairo_restore (cairo); - - delete strokePathClip->path; - if (strokePathClip->dashes) - gfree (strokePathClip->dashes); - gfree (strokePathClip); - strokePathClip = NULL; -} - -void CairoOutputDev::beginString(GfxState *state, GooString *s) -{ - int len = s->getLength(); - - if (needFontUpdate) - updateFont(state); - - if (!currentFont) - return; - - glyphs = (cairo_glyph_t *) gmallocn (len, sizeof (cairo_glyph_t)); - glyphCount = 0; - if (use_show_text_glyphs) { - clusters = (cairo_text_cluster_t *) gmallocn (len, sizeof (cairo_text_cluster_t)); - clusterCount = 0; - utf8Max = len*2; // start with twice the number of glyphs. we will realloc if we need more. - utf8 = (char *) gmalloc (utf8Max); - utf8Count = 0; - } -} - -void CairoOutputDev::drawChar(GfxState *state, double x, double y, - double dx, double dy, - double originX, double originY, - CharCode code, int nBytes, Unicode *u, int uLen) -{ - if (currentFont) { - glyphs[glyphCount].index = currentFont->getGlyph (code, u, uLen); - glyphs[glyphCount].x = x - originX; - glyphs[glyphCount].y = y - originY; - glyphCount++; - if (use_show_text_glyphs) { - GooString enc("UTF-8"); - UnicodeMap *utf8Map = globalParams->getUnicodeMap(&enc); - if (utf8Max - utf8Count < uLen*6) { - // utf8 encoded characters can be up to 6 bytes - if (utf8Max > uLen*6) - utf8Max *= 2; - else - utf8Max += 2*uLen*6; - utf8 = (char *) grealloc (utf8, utf8Max); - } - clusters[clusterCount].num_bytes = 0; - for (int i = 0; i < uLen; i++) { - int size = utf8Map->mapUnicode(u[i], utf8 + utf8Count, utf8Max - utf8Count); - utf8Count += size; - clusters[clusterCount].num_bytes += size; - } - clusters[clusterCount].num_glyphs = 1; - clusterCount++; - } - } - - if (!text) - return; - actualText->addChar (state, x, y, dx, dy, code, nBytes, u, uLen); -} - -void CairoOutputDev::endString(GfxState *state) -{ - int render; - - if (!currentFont) - return; - - // endString can be called without a corresponding beginString. If this - // happens glyphs will be null so don't draw anything, just return. - // XXX: OutputDevs should probably not have to deal with this... - if (!glyphs) - return; - - // ignore empty strings and invisible text -- this is used by - // Acrobat Capture - render = state->getRender(); - if (render == 3 || glyphCount == 0) { - gfree(glyphs); - glyphs = NULL; - return; - } - - if (!(render & 1)) { - LOG (printf ("fill string\n")); - cairo_set_source (cairo, fill_pattern); - if (use_show_text_glyphs) - cairo_show_text_glyphs (cairo, utf8, utf8Count, glyphs, glyphCount, clusters, clusterCount, (cairo_text_cluster_flags_t)0); - else - cairo_show_glyphs (cairo, glyphs, glyphCount); - if (cairo_shape) - cairo_show_glyphs (cairo_shape, glyphs, glyphCount); - } - - // stroke - if ((render & 3) == 1 || (render & 3) == 2) { - LOG (printf ("stroke string\n")); - cairo_set_source (cairo, stroke_pattern); - cairo_glyph_path (cairo, glyphs, glyphCount); - cairo_stroke (cairo); - if (cairo_shape) { - cairo_glyph_path (cairo_shape, glyphs, glyphCount); - cairo_stroke (cairo_shape); - } - } - - // clip - if ((render & 4)) { - LOG (printf ("clip string\n")); - // append the glyph path to textClipPath. - - // set textClipPath as the currentPath - if (textClipPath) { - cairo_append_path (cairo, textClipPath); - if (cairo_shape) { - cairo_append_path (cairo_shape, textClipPath); - } - cairo_path_destroy (textClipPath); - } - - // append the glyph path - cairo_glyph_path (cairo, glyphs, glyphCount); - - // move the path back into textClipPath - // and clear the current path - textClipPath = cairo_copy_path (cairo); - cairo_new_path (cairo); - if (cairo_shape) { - cairo_new_path (cairo_shape); - } - } - - gfree (glyphs); - glyphs = NULL; - if (use_show_text_glyphs) { - gfree (clusters); - clusters = NULL; - gfree (utf8); - utf8 = NULL; - } -} - - -GBool CairoOutputDev::beginType3Char(GfxState *state, double x, double y, - double dx, double dy, - CharCode code, Unicode *u, int uLen) { - - cairo_save (cairo); - double *ctm; - cairo_matrix_t matrix; - - ctm = state->getCTM(); - matrix.xx = ctm[0]; - matrix.yx = ctm[1]; - matrix.xy = ctm[2]; - matrix.yy = ctm[3]; - matrix.x0 = ctm[4]; - matrix.y0 = ctm[5]; - /* Restore the original matrix and then transform to matrix needed for the - * type3 font. This is ugly but seems to work. Perhaps there is a better way to do it?*/ - cairo_set_matrix(cairo, &orig_matrix); - cairo_transform(cairo, &matrix); - if (cairo_shape) { - cairo_save (cairo_shape); - cairo_set_matrix(cairo_shape, &orig_matrix); - cairo_transform(cairo_shape, &matrix); - } - cairo_pattern_destroy(stroke_pattern); - cairo_pattern_reference(fill_pattern); - stroke_pattern = fill_pattern; - return gFalse; -} - -void CairoOutputDev::endType3Char(GfxState *state) { - cairo_restore (cairo); - if (cairo_shape) { - cairo_restore (cairo_shape); - } -} - -void CairoOutputDev::type3D0(GfxState *state, double wx, double wy) { - t3_glyph_wx = wx; - t3_glyph_wy = wy; -} - -void CairoOutputDev::type3D1(GfxState *state, double wx, double wy, - double llx, double lly, double urx, double ury) { - t3_glyph_wx = wx; - t3_glyph_wy = wy; - t3_glyph_bbox[0] = llx; - t3_glyph_bbox[1] = lly; - t3_glyph_bbox[2] = urx; - t3_glyph_bbox[3] = ury; - t3_glyph_has_bbox = gTrue; -} - -void CairoOutputDev::beginTextObject(GfxState *state) { -} - -void CairoOutputDev::endTextObject(GfxState *state) { - if (textClipPath) { - // clip the accumulated text path - cairo_append_path (cairo, textClipPath); - cairo_clip (cairo); - if (cairo_shape) { - cairo_append_path (cairo_shape, textClipPath); - cairo_clip (cairo_shape); - } - cairo_path_destroy (textClipPath); - textClipPath = NULL; - } -} - -void CairoOutputDev::beginActualText(GfxState *state, GooString *text) -{ - if (this->text) - actualText->begin(state, text); -} - -void CairoOutputDev::endActualText(GfxState *state) -{ - if (text) - actualText->end(state); -} - -static inline int splashRound(SplashCoord x) { - return (int)floor(x + 0.5); -} - -static inline int splashCeil(SplashCoord x) { - return (int)ceil(x); -} - -static inline int splashFloor(SplashCoord x) { - return (int)floor(x); -} - -static -cairo_surface_t *cairo_surface_create_similar_clip (cairo_t *cairo, cairo_content_t content) -{ - double x1, y1, x2, y2; - int width, height; - cairo_clip_extents (cairo, &x1, &y1, &x2, &y2); - cairo_matrix_t matrix; - cairo_get_matrix (cairo, &matrix); - //cairo_matrix_transform_point(&matrix, &x1, &y1); - //cairo_matrix_transform_point(&matrix, &x2, &y2);*/ - cairo_user_to_device(cairo, &x1, &y1); - cairo_user_to_device(cairo, &x2, &y2); - width = splashCeil(x2) - splashFloor(x1); - //XXX: negative matrix - ////height = splashCeil(y2) - splashFloor(y1); - height = splashFloor(y1) - splashCeil(y2); - cairo_surface_t *target = cairo_get_target (cairo); - cairo_surface_t *result; - - result = cairo_surface_create_similar (target, content, width, height); - double x_offset, y_offset; - cairo_surface_get_device_offset(target, &x_offset, &y_offset); - cairo_surface_set_device_offset(result, x_offset, y_offset); - - - return result; -} - - - -void CairoOutputDev::beginTransparencyGroup(GfxState * /*state*/, double * /*bbox*/, - GfxColorSpace * blendingColorSpace, - GBool /*isolated*/, GBool knockout, - GBool forSoftMask) { - /* push color space */ - ColorSpaceStack* css = new ColorSpaceStack; - css->cs = blendingColorSpace; - css->knockout = knockout; - cairo_get_matrix(cairo, &css->group_matrix); - css->next = groupColorSpaceStack; - groupColorSpaceStack = css; - - LOG(printf ("begin transparency group. knockout: %s\n", knockout ? "yes":"no")); - - if (knockout) { - knockoutCount++; - if (!cairo_shape) { - /* create a surface for tracking the shape */ - cairo_surface_t *cairo_shape_surface = cairo_surface_create_similar_clip (cairo, CAIRO_CONTENT_ALPHA); - cairo_shape = cairo_create (cairo_shape_surface); - cairo_surface_destroy (cairo_shape_surface); - - /* the color doesn't matter as long as it is opaque */ - cairo_set_source_rgb (cairo_shape, 0, 0, 0); - cairo_matrix_t matrix; - cairo_get_matrix (cairo, &matrix); - //printMatrix(&matrix); - cairo_set_matrix (cairo_shape, &matrix); - } else { - cairo_reference (cairo_shape); - } - } - if (groupColorSpaceStack->next && groupColorSpaceStack->next->knockout) { - /* we need to track the shape */ - cairo_push_group (cairo_shape); - } - if (0 && forSoftMask) - cairo_push_group_with_content (cairo, CAIRO_CONTENT_ALPHA); - else - cairo_push_group (cairo); - - /* push_group has an implicit cairo_save() */ - if (knockout) { - /*XXX: let's hope this matches the semantics needed */ - cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); - } else { - cairo_set_operator(cairo, CAIRO_OPERATOR_OVER); - } -} - -void CairoOutputDev::endTransparencyGroup(GfxState * /*state*/) { - if (group) - cairo_pattern_destroy(group); - group = cairo_pop_group (cairo); - - LOG(printf ("end transparency group\n")); - - if (groupColorSpaceStack->next && groupColorSpaceStack->next->knockout) { - if (shape) - cairo_pattern_destroy(shape); - shape = cairo_pop_group (cairo_shape); - } -} - -void CairoOutputDev::paintTransparencyGroup(GfxState * /*state*/, double * /*bbox*/) { - LOG(printf ("paint transparency group\n")); - - cairo_save (cairo); - cairo_set_matrix (cairo, &groupColorSpaceStack->group_matrix); - cairo_set_source (cairo, group); - - if (!mask) { - //XXX: deal with mask && shape case - if (shape) { - cairo_save (cairo); - - /* OPERATOR_SOURCE w/ a mask is defined as (src IN mask) ADD (dest OUT mask) - * however our source has already been clipped to mask so we only need to - * do ADD and OUT */ - - /* clear the shape mask */ - cairo_set_source (cairo, shape); - cairo_set_operator (cairo, CAIRO_OPERATOR_DEST_OUT); - cairo_paint (cairo); - - cairo_set_operator (cairo, CAIRO_OPERATOR_ADD); - cairo_set_source (cairo, group); - cairo_paint (cairo); - - cairo_restore (cairo); - - cairo_pattern_destroy (shape); - shape = NULL; - } else { - cairo_paint_with_alpha (cairo, fill_opacity); - } - cairo_status_t status = cairo_status(cairo); - if (status) - printf("BAD status: %s\n", cairo_status_to_string(status)); - } else { - if (fill_opacity < 1.0) { - cairo_push_group(cairo); - } - cairo_save(cairo); - cairo_set_matrix(cairo, &mask_matrix); - cairo_mask(cairo, mask); - cairo_restore(cairo); - if (fill_opacity < 1.0) { - cairo_pop_group_to_source(cairo); - cairo_paint_with_alpha (cairo, fill_opacity); - } - cairo_pattern_destroy(mask); - mask = NULL; - } - - popTransparencyGroup(); - cairo_restore(cairo); -} - -static int luminocity(uint32_t x) -{ - int r = (x >> 16) & 0xff; - int g = (x >> 8) & 0xff; - int b = (x >> 0) & 0xff; - // an arbitrary integer approximation of .3*r + .59*g + .11*b - int y = (r*19661+g*38666+b*7209 + 32829)>>16; - return y; -} - - -/* XXX: do we need to deal with shape here? */ -void CairoOutputDev::setSoftMask(GfxState * state, double * bbox, GBool alpha, - Function * transferFunc, GfxColor * backdropColor) { - cairo_pattern_destroy(mask); - - LOG(printf ("set softMask\n")); - - if (!alpha || transferFunc) { - /* We need to mask according to the luminocity of the group. - * So we paint the group to an image surface convert it to a luminocity map - * and then use that as the mask. */ - - /* Get clip extents in device space */ - double x1, y1, x2, y2, x_min, y_min, x_max, y_max; - cairo_clip_extents(cairo, &x1, &y1, &x2, &y2); - cairo_user_to_device(cairo, &x1, &y1); - cairo_user_to_device(cairo, &x2, &y2); - x_min = MIN(x1, x2); - y_min = MIN(y1, y2); - x_max = MAX(x1, x2); - y_max = MAX(y1, y2); - cairo_clip_extents(cairo, &x1, &y1, &x2, &y2); - cairo_user_to_device(cairo, &x1, &y2); - cairo_user_to_device(cairo, &x2, &y1); - x_min = MIN(x_min,MIN(x1, x2)); - y_min = MIN(y_min,MIN(y1, y2)); - x_max = MAX(x_max,MAX(x1, x2)); - y_max = MAX(y_max,MAX(y1, y2)); - - int width = (int)(ceil(x_max) - floor(x_min)); - int height = (int)(ceil(y_max) - floor(y_min)); - - /* Get group device offset */ - double x_offset, y_offset; - if (cairo_get_group_target(cairo) == cairo_get_target(cairo)) { - cairo_surface_get_device_offset(cairo_get_group_target(cairo), &x_offset, &y_offset); - } else { - cairo_surface_t *pats; - cairo_pattern_get_surface(group, &pats); - cairo_surface_get_device_offset(pats, &x_offset, &y_offset); - } - - /* Adjust extents by group offset */ - x_min += x_offset; - y_min += y_offset; - - cairo_surface_t *source = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); - cairo_t *maskCtx = cairo_create(source); - - //XXX: hopefully this uses the correct color space */ - if (!alpha) { - GfxRGB backdropColorRGB; - groupColorSpaceStack->cs->getRGB(backdropColor, &backdropColorRGB); - /* paint the backdrop */ - cairo_set_source_rgb(maskCtx, - colToDbl(backdropColorRGB.r), - colToDbl(backdropColorRGB.g), - colToDbl(backdropColorRGB.b)); - } - cairo_paint(maskCtx); - - /* Copy source ctm to mask ctm and translate origin so that the - * mask appears it the same location on the source surface. */ - cairo_matrix_t mat, tmat; - cairo_matrix_init_translate(&tmat, -x_min, -y_min); - cairo_get_matrix(cairo, &mat); - cairo_matrix_multiply(&mat, &mat, &tmat); - cairo_set_matrix(maskCtx, &mat); - - /* make the device offset of the new mask match that of the group */ - cairo_surface_set_device_offset(source, x_offset, y_offset); - - /* paint the group */ - cairo_set_source(maskCtx, group); - cairo_paint(maskCtx); - - /* XXX status = cairo_status(maskCtx); */ - cairo_destroy(maskCtx); - - /* convert to a luminocity map */ - uint32_t *source_data = (uint32_t*)cairo_image_surface_get_data(source); - /* get stride in units of 32 bits */ - int stride = cairo_image_surface_get_stride(source)/4; - for (int y=0; ytransform(&lum_in, &lum_out); - lum = (int)(lum_out * 255.0 + 0.5); - } - source_data[y*stride + x] = lum << 24; - } - } - cairo_surface_mark_dirty (source); - - /* setup the new mask pattern */ - mask = cairo_pattern_create_for_surface(source); - cairo_get_matrix(cairo, &mask_matrix); - - if (cairo_get_group_target(cairo) == cairo_get_target(cairo)) { - cairo_pattern_set_matrix(mask, &mat); - } else { - cairo_matrix_t patMatrix; - cairo_pattern_get_matrix(group, &patMatrix); - /* Apply x_min, y_min offset to it appears in the same location as source. */ - cairo_matrix_multiply(&patMatrix, &patMatrix, &tmat); - cairo_pattern_set_matrix(mask, &patMatrix); - } - - cairo_surface_destroy(source); - } else if (alpha) { - mask = cairo_pattern_reference(group); - cairo_get_matrix(cairo, &mask_matrix); - } - - popTransparencyGroup(); -} - -void CairoOutputDev::popTransparencyGroup() { - /* pop color space */ - ColorSpaceStack *css = groupColorSpaceStack; - if (css->knockout) { - knockoutCount--; - if (!knockoutCount) { - /* we don't need to track the shape anymore because - * we are not above any knockout groups */ - cairo_destroy(cairo_shape); - cairo_shape = NULL; - } - } - groupColorSpaceStack = css->next; - delete css; -} - - -void CairoOutputDev::clearSoftMask(GfxState * /*state*/) { - if (mask) - cairo_pattern_destroy(mask); - mask = NULL; -} - -/* Taken from cairo/doc/tutorial/src/singular.c */ -static void -get_singular_values (const cairo_matrix_t *matrix, - double *major, - double *minor) -{ - double xx = matrix->xx, xy = matrix->xy; - double yx = matrix->yx, yy = matrix->yy; - - double a = xx*xx+yx*yx; - double b = xy*xy+yy*yy; - double k = xx*xy+yx*yy; - - double f = (a+b) * .5; - double g = (a-b) * .5; - double delta = sqrt (g*g + k*k); - - if (major) - *major = sqrt (f + delta); - if (minor) - *minor = sqrt (f - delta); -} - -void CairoOutputDev::getScaledSize(int orig_width, - int orig_height, - int *scaledWidth, - int *scaledHeight) { - cairo_matrix_t matrix; - cairo_get_matrix(cairo, &matrix); - - double xScale; - double yScale; - if (orig_width > orig_height) - get_singular_values (&matrix, &xScale, &yScale); - else - get_singular_values (&matrix, &yScale, &xScale); - - int tx, tx2, ty, ty2; /* the integer co-oridinates of the resulting image */ - if (xScale >= 0) { - tx = splashRound(matrix.x0 - 0.01); - tx2 = splashRound(matrix.x0 + xScale + 0.01) - 1; - } else { - tx = splashRound(matrix.x0 + 0.01) - 1; - tx2 = splashRound(matrix.x0 + xScale - 0.01); - } - *scaledWidth = abs(tx2 - tx) + 1; - //scaledWidth = splashRound(fabs(xScale)); - if (*scaledWidth == 0) { - // technically, this should draw nothing, but it generally seems - // better to draw a one-pixel-wide stripe rather than throwing it - // away - *scaledWidth = 1; - } - if (yScale >= 0) { - ty = splashFloor(matrix.y0 + 0.01); - ty2 = splashCeil(matrix.y0 + yScale - 0.01); - } else { - ty = splashCeil(matrix.y0 - 0.01); - ty2 = splashFloor(matrix.y0 + yScale + 0.01); - } - *scaledHeight = abs(ty2 - ty); - if (*scaledHeight == 0) { - *scaledHeight = 1; - } -} - -cairo_filter_t -CairoOutputDev::getFilterForSurface(cairo_surface_t *image, - GBool interpolate) -{ - if (interpolate) - return CAIRO_FILTER_BILINEAR; - - int orig_width = cairo_image_surface_get_width (image); - int orig_height = cairo_image_surface_get_height (image); - if (orig_width == 0 || orig_height == 0) - return CAIRO_FILTER_NEAREST; - - /* When printing, don't change the interpolation. */ - if (printing) - return CAIRO_FILTER_NEAREST; - - int scaled_width, scaled_height; - getScaledSize (orig_width, orig_height, &scaled_width, &scaled_height); - - /* When scale factor is >= 400% we don't interpolate. See bugs #25268, #9860 */ - if (scaled_width / orig_width >= 4 || scaled_height / orig_height >= 4) - return CAIRO_FILTER_NEAREST; - - return CAIRO_FILTER_BILINEAR; -} - -void CairoOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, - int width, int height, GBool invert, - GBool interpolate, GBool inlineImg) { - - /* FIXME: Doesn't the image mask support any colorspace? */ - cairo_set_source (cairo, fill_pattern); - - /* work around a cairo bug when scaling 1x1 surfaces */ - if (width == 1 && height == 1) { - ImageStream *imgStr; - Guchar pix; - int invert_bit; - - imgStr = new ImageStream(str, width, 1, 1); - imgStr->reset(); - imgStr->getPixel(&pix); - imgStr->close(); - delete imgStr; - - invert_bit = invert ? 1 : 0; - if (pix ^ invert_bit) - return; - - cairo_save (cairo); - cairo_rectangle (cairo, 0., 0., width, height); - cairo_fill (cairo); - cairo_restore (cairo); - if (cairo_shape) { - cairo_save (cairo_shape); - cairo_rectangle (cairo_shape, 0., 0., width, height); - cairo_fill (cairo_shape); - cairo_restore (cairo_shape); - } - return; - } - - /* shape is 1.0 for painted areas, 0.0 for unpainted ones */ - - cairo_matrix_t matrix; - cairo_get_matrix (cairo, &matrix); - //XXX: it is possible that we should only do sub pixel positioning if - // we are rendering fonts */ - if (!printing && prescaleImages - /* not rotated */ - && matrix.xy == 0 && matrix.yx == 0 - /* axes not flipped / not 180 deg rotated */ - && matrix.xx > 0 && (upsideDown() ? -1 : 1) * matrix.yy > 0) { - drawImageMaskPrescaled(state, ref, str, width, height, invert, interpolate, inlineImg); - } else { - drawImageMaskRegular(state, ref, str, width, height, invert, interpolate, inlineImg); - } - -} - -void CairoOutputDev::setSoftMaskFromImageMask(GfxState *state, Object *ref, Stream *str, - int width, int height, GBool invert, - GBool inlineImg, double *baseMatrix) { - - /* FIXME: Doesn't the image mask support any colorspace? */ - cairo_set_source (cairo, fill_pattern); - - /* work around a cairo bug when scaling 1x1 surfaces */ - if (width == 1 && height == 1) { - ImageStream *imgStr; - Guchar pix; - int invert_bit; - - imgStr = new ImageStream(str, width, 1, 1); - imgStr->reset(); - imgStr->getPixel(&pix); - imgStr->close(); - delete imgStr; - - invert_bit = invert ? 1 : 0; - if (pix ^ invert_bit) - return; - - cairo_save (cairo); - cairo_rectangle (cairo, 0., 0., width, height); - cairo_fill (cairo); - cairo_restore (cairo); - if (cairo_shape) { - cairo_save (cairo_shape); - cairo_rectangle (cairo_shape, 0., 0., width, height); - cairo_fill (cairo_shape); - cairo_restore (cairo_shape); - } - return; - } - - cairo_push_group_with_content (cairo, CAIRO_CONTENT_ALPHA); - - /* shape is 1.0 for painted areas, 0.0 for unpainted ones */ - - cairo_matrix_t matrix; - cairo_get_matrix (cairo, &matrix); - //XXX: it is possible that we should only do sub pixel positioning if - // we are rendering fonts */ - if (!printing && prescaleImages && matrix.xy == 0.0 && matrix.yx == 0.0) { - drawImageMaskPrescaled(state, ref, str, width, height, invert, gFalse, inlineImg); - } else { - drawImageMaskRegular(state, ref, str, width, height, invert, gFalse, inlineImg); - } - - if (state->getFillColorSpace()->getMode() == csPattern) { - cairo_set_source_rgb (cairo, 1, 1, 1); - cairo_set_matrix (cairo, &mask_matrix); - cairo_mask (cairo, mask); - } - - if (mask) - cairo_pattern_destroy (mask); - mask = cairo_pop_group (cairo); - - saveState(state); - double bbox[4] = {0,0,1,1}; // dummy - beginTransparencyGroup(state, bbox, state->getFillColorSpace(), - gTrue, gFalse, gFalse); -} - -void CairoOutputDev::unsetSoftMaskFromImageMask(GfxState *state, double *baseMatrix) { - double bbox[4] = {0,0,1,1}; // dummy - - endTransparencyGroup(state); - restoreState(state); - paintTransparencyGroup(state, bbox); - clearSoftMask(state); -} - -void CairoOutputDev::drawImageMaskRegular(GfxState *state, Object *ref, Stream *str, - int width, int height, GBool invert, - GBool interpolate, GBool inlineImg) { - unsigned char *buffer; - unsigned char *dest; - cairo_surface_t *image; - cairo_pattern_t *pattern; - int x, y, i, bit; - ImageStream *imgStr; - Guchar *pix; - cairo_matrix_t matrix; - int invert_bit; - int row_stride; - cairo_filter_t filter; - - /* TODO: Do we want to cache these? */ - imgStr = new ImageStream(str, width, 1, 1); - imgStr->reset(); - - image = cairo_image_surface_create (CAIRO_FORMAT_A1, width, height); - if (cairo_surface_status (image)) - goto cleanup; - - buffer = cairo_image_surface_get_data (image); - row_stride = cairo_image_surface_get_stride (image); - - invert_bit = invert ? 1 : 0; - - for (y = 0; y < height; y++) { - pix = imgStr->getLine(); - dest = buffer + y * row_stride; - i = 0; - bit = 0; - for (x = 0; x < width; x++) { - if (bit == 0) - dest[i] = 0; - if (!(pix[x] ^ invert_bit)) { -#ifdef WORDS_BIGENDIAN - dest[i] |= (1 << (7 - bit)); -#else - dest[i] |= (1 << bit); -#endif - } - bit++; - if (bit > 7) { - bit = 0; - i++; - } - } - } - - filter = getFilterForSurface (image, interpolate); - - cairo_surface_mark_dirty (image); - pattern = cairo_pattern_create_for_surface (image); - cairo_surface_destroy (image); - if (cairo_pattern_status (pattern)) - goto cleanup; - - LOG (printf ("drawImageMask %dx%d\n", width, height)); - - cairo_pattern_set_filter (pattern, filter); - - if (!printing) - cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD); - - cairo_matrix_init_translate (&matrix, 0, height); - cairo_matrix_scale (&matrix, width, -height); - cairo_pattern_set_matrix (pattern, &matrix); - if (cairo_pattern_status (pattern)) { - cairo_pattern_destroy (pattern); - goto cleanup; - } - - if (state->getFillColorSpace()->getMode() == csPattern) { - mask = cairo_pattern_reference (pattern); - cairo_get_matrix (cairo, &mask_matrix); - } else if (!printing) { - cairo_save (cairo); - cairo_rectangle (cairo, 0., 0., 1., 1.); - cairo_clip (cairo); - cairo_mask (cairo, pattern); - cairo_restore (cairo); - } else { - cairo_mask (cairo, pattern); - } - - if (cairo_shape) { - cairo_save (cairo_shape); - cairo_set_source (cairo_shape, pattern); - if (!printing) { - cairo_rectangle (cairo_shape, 0., 0., 1., 1.); - cairo_fill (cairo_shape); - } else { - cairo_mask (cairo_shape, pattern); - } - cairo_restore (cairo_shape); - } - - cairo_pattern_destroy (pattern); - -cleanup: - imgStr->close(); - delete imgStr; -} - - -void CairoOutputDev::drawImageMaskPrescaled(GfxState *state, Object *ref, Stream *str, - int width, int height, GBool invert, - GBool interpolate, GBool inlineImg) { - unsigned char *buffer; - cairo_surface_t *image; - cairo_pattern_t *pattern; - ImageStream *imgStr; - Guchar *pix; - cairo_matrix_t matrix; - int invert_bit; - int row_stride; - - /* cairo does a very poor job of scaling down images so we scale them ourselves */ - - LOG (printf ("drawImageMaskPrescaled %dx%d\n", width, height)); - - /* this scaling code is adopted from the splash image scaling code */ - cairo_get_matrix(cairo, &matrix); -#if 0 - printf("[%f %f], [%f %f], %f %f\n", matrix.xx, matrix.xy, matrix.yx, matrix.yy, matrix.x0, matrix.y0); -#endif - /* this whole computation should be factored out */ - double xScale = matrix.xx; - double yScale = matrix.yy; - int tx, tx2, ty, ty2; /* the integer co-oridinates of the resulting image */ - int scaledHeight; - int scaledWidth; - if (xScale >= 0) { - tx = splashRound(matrix.x0 - 0.01); - tx2 = splashRound(matrix.x0 + xScale + 0.01) - 1; - } else { - tx = splashRound(matrix.x0 + 0.01) - 1; - tx2 = splashRound(matrix.x0 + xScale - 0.01); - } - scaledWidth = abs(tx2 - tx) + 1; - //scaledWidth = splashRound(fabs(xScale)); - if (scaledWidth == 0) { - // technically, this should draw nothing, but it generally seems - // better to draw a one-pixel-wide stripe rather than throwing it - // away - scaledWidth = 1; - } - if (yScale >= 0) { - ty = splashFloor(matrix.y0 + 0.01); - ty2 = splashCeil(matrix.y0 + yScale - 0.01); - } else { - ty = splashCeil(matrix.y0 - 0.01); - ty2 = splashFloor(matrix.y0 + yScale + 0.01); - } - scaledHeight = abs(ty2 - ty); - if (scaledHeight == 0) { - scaledHeight = 1; - } -#if 0 - printf("xscale: %g, yscale: %g\n", xScale, yScale); - printf("width: %d, height: %d\n", width, height); - printf("scaledWidth: %d, scaledHeight: %d\n", scaledWidth, scaledHeight); -#endif - - /* compute the required padding */ - /* Padding is used to preserve the aspect ratio. - We compute total_pad to make (height+total_pad)/scaledHeight as close to height/yScale as possible */ - int head_pad = 0; - int tail_pad = 0; - int total_pad = splashRound(height*(scaledHeight/fabs(yScale)) - height); - - /* compute the two pieces of padding */ - if (total_pad > 0) { - //XXX: i'm not positive fabs() is correct - float tail_error = fabs(matrix.y0 - ty); - float head_error = fabs(ty2 - (matrix.y0 + yScale)); - float tail_fraction = tail_error/(tail_error + head_error); - tail_pad = splashRound(total_pad*tail_fraction); - head_pad = total_pad - tail_pad; - } else { - tail_pad = 0; - head_pad = 0; - } - int origHeight = height; - height += tail_pad; - height += head_pad; -#if 0 - printf("head_pad: %d tail_pad: %d\n", head_pad, tail_pad); - printf("origHeight: %d height: %d\n", origHeight, height); - printf("ty: %d, ty2: %d\n", ty, ty2); -#endif - - /* TODO: Do we want to cache these? */ - imgStr = new ImageStream(str, width, 1, 1); - imgStr->reset(); - - invert_bit = invert ? 1 : 0; - - image = cairo_image_surface_create (CAIRO_FORMAT_A8, scaledWidth, scaledHeight); - if (cairo_surface_status (image)) { - imgStr->close(); - delete imgStr; - return; - } - - buffer = cairo_image_surface_get_data (image); - row_stride = cairo_image_surface_get_stride (image); - - int yp = height / scaledHeight; - int yq = height % scaledHeight; - int xp = width / scaledWidth; - int xq = width % scaledWidth; - int yt = 0; - int origHeight_c = origHeight; - /* use MIN() because yp might be > origHeight because of padding */ - unsigned char *pixBuf = (unsigned char *)malloc(MIN(yp+1, origHeight)*width); - int lastYStep = 1; - int total = 0; - for (int y = 0; y < scaledHeight; y++) { - // y scale Bresenham - int yStep = yp; - yt += yq; - - if (yt >= scaledHeight) { - yt -= scaledHeight; - ++yStep; - } - - // read row (s) from image ignoring the padding as appropriate - { - int n = (yp > 0) ? yStep : lastYStep; - total += n; - if (n > 0) { - unsigned char *p = pixBuf; - int head_pad_count = head_pad; - int origHeight_count = origHeight; - int tail_pad_count = tail_pad; - for (int i=0; igetLine(); - for (int j=0; j 0 ? yStep : 1; - int origN = n; - - /* compute the size of padding and pixels that will be used for this row */ - int head_pad_size = MIN(n, head_pad); - n -= head_pad_size; - head_pad -= MIN(head_pad_size, yStep); - - int pix_size = MIN(n, origHeight); - n -= pix_size; - origHeight -= MIN(pix_size, yStep); - - int tail_pad_size = MIN(n, tail_pad); - n -= tail_pad_size; - tail_pad -= MIN(tail_pad_size, yStep); - if (n != 0) { - printf("n = %d (%d %d %d)\n", n, head_pad_size, pix_size, tail_pad_size); - assert(n == 0); - } - - for (int x = 0; x < scaledWidth; ++x) { - int xStep = xp; - xt += xq; - if (xt >= scaledWidth) { - xt -= scaledWidth; - ++xStep; - } - int m = xStep > 0 ? xStep : 1; - float pixAcc0 = 0; - /* could m * head_pad_size * tail_pad_size overflow? */ - if (invert_bit) { - pixAcc0 += m * head_pad_size * tail_pad_size * 255; - } else { - pixAcc0 += m * head_pad_size * tail_pad_size * 0; - } - /* Accumulate all of the source pixels for the destination pixel */ - for (int i = 0; i < pix_size; ++i) { - for (int j = 0; j< m; ++j) { - if (xSrc + i*width + j > MIN(yp + 1, origHeight_c)*width) { - printf("%d > %d (%d %d %d %d) (%d %d %d)\n", xSrc + i*width + j, MIN(yp + 1, origHeight_c)*width, xSrc, i , width, j, yp, origHeight_c, width); - printf("%d %d %d\n", head_pad_size, pix_size, tail_pad_size); - assert(0 && "bad access\n"); - } - pixAcc0 += pixBuf[xSrc + i*width + j]; - } - } - buffer[y * row_stride + x] = splashFloor(pixAcc0 / (origN*m)); - xSrc += xStep; - x1 += 1; - } - - } - free(pixBuf); - - cairo_surface_mark_dirty (image); - pattern = cairo_pattern_create_for_surface (image); - cairo_surface_destroy (image); - if (cairo_pattern_status (pattern)) { - imgStr->close(); - delete imgStr; - return; - } - - /* we should actually be using CAIRO_FILTER_NEAREST here. However, - * cairo doesn't yet do minifaction filtering causing scaled down - * images with CAIRO_FILTER_NEAREST to look really bad */ - cairo_pattern_set_filter (pattern, - interpolate ? CAIRO_FILTER_BEST : CAIRO_FILTER_FAST); - cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD); - - if (state->getFillColorSpace()->getMode() == csPattern) { - cairo_matrix_init_translate (&matrix, 0, scaledHeight); - cairo_matrix_scale (&matrix, scaledWidth, -scaledHeight); - cairo_pattern_set_matrix (pattern, &matrix); - if (cairo_pattern_status (pattern)) { - cairo_pattern_destroy (pattern); - imgStr->close(); - delete imgStr; - return; - } - - mask = cairo_pattern_reference (pattern); - cairo_get_matrix (cairo, &mask_matrix); - } else { - cairo_save (cairo); - - /* modify our current transformation so that the prescaled image - * goes where it is supposed to */ - cairo_get_matrix(cairo, &matrix); - cairo_scale(cairo, 1.0/matrix.xx, 1.0/matrix.yy); - // get integer co-ords - cairo_translate (cairo, tx - matrix.x0, ty2 - matrix.y0); - if (yScale > 0) - cairo_scale(cairo, 1, -1); - - cairo_rectangle (cairo, 0., 0., scaledWidth, scaledHeight); - cairo_clip (cairo); - cairo_mask (cairo, pattern); - - //cairo_get_matrix(cairo, &matrix); - //printf("mask at: [%f %f], [%f %f], %f %f\n\n", matrix.xx, matrix.xy, matrix.yx, matrix.yy, matrix.x0, matrix.y0); - cairo_restore(cairo); - } - - if (cairo_shape) { - cairo_save (cairo_shape); - - /* modify our current transformation so that the prescaled image - * goes where it is supposed to */ - cairo_get_matrix(cairo_shape, &matrix); - cairo_scale(cairo_shape, 1.0/matrix.xx, 1.0/matrix.yy); - // get integer co-ords - cairo_translate (cairo_shape, tx - matrix.x0, ty2 - matrix.y0); - if (yScale > 0) - cairo_scale(cairo_shape, 1, -1); - - cairo_rectangle (cairo_shape, 0., 0., scaledWidth, scaledHeight); - cairo_fill (cairo_shape); - - cairo_restore(cairo_shape); - } - - cairo_pattern_destroy (pattern); - - imgStr->close(); - delete imgStr; -} - -void CairoOutputDev::drawMaskedImage(GfxState *state, Object *ref, - Stream *str, int width, int height, - GfxImageColorMap *colorMap, - GBool interpolate, - Stream *maskStr, int maskWidth, - int maskHeight, GBool maskInvert, - GBool maskInterpolate) -{ - ImageStream *maskImgStr, *imgStr; - int row_stride; - unsigned char *maskBuffer, *buffer; - unsigned char *maskDest; - unsigned int *dest; - cairo_surface_t *maskImage, *image; - cairo_pattern_t *maskPattern, *pattern; - cairo_matrix_t matrix; - cairo_matrix_t maskMatrix; - Guchar *pix; - int x, y; - int invert_bit; - cairo_filter_t filter; - cairo_filter_t maskFilter; - - maskImgStr = new ImageStream(maskStr, maskWidth, 1, 1); - maskImgStr->reset(); - - maskImage = cairo_image_surface_create (CAIRO_FORMAT_A8, maskWidth, maskHeight); - if (cairo_surface_status (maskImage)) { - maskImgStr->close(); - delete maskImgStr; - return; - } - - maskBuffer = cairo_image_surface_get_data (maskImage); - row_stride = cairo_image_surface_get_stride (maskImage); - - invert_bit = maskInvert ? 1 : 0; - - for (y = 0; y < maskHeight; y++) { - pix = maskImgStr->getLine(); - maskDest = maskBuffer + y * row_stride; - for (x = 0; x < maskWidth; x++) { - if (pix[x] ^ invert_bit) - *maskDest++ = 0; - else - *maskDest++ = 255; - } - } - - maskImgStr->close(); - delete maskImgStr; - - maskFilter = getFilterForSurface (maskImage, maskInterpolate); - - cairo_surface_mark_dirty (maskImage); - maskPattern = cairo_pattern_create_for_surface (maskImage); - cairo_surface_destroy (maskImage); - if (cairo_pattern_status (maskPattern)) - return; - -#if 0 - /* ICCBased color space doesn't do any color correction - * so check its underlying color space as well */ - int is_identity_transform; - is_identity_transform = colorMap->getColorSpace()->getMode() == csDeviceRGB || - (colorMap->getColorSpace()->getMode() == csICCBased && - ((GfxICCBasedColorSpace*)colorMap->getColorSpace())->getAlt()->getMode() == csDeviceRGB); -#endif - - /* TODO: Do we want to cache these? */ - imgStr = new ImageStream(str, width, - colorMap->getNumPixelComps(), - colorMap->getBits()); - imgStr->reset(); - - image = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height); - if (cairo_surface_status (image)) - goto cleanup; - - buffer = cairo_image_surface_get_data (image); - row_stride = cairo_image_surface_get_stride (image); - for (y = 0; y < height; y++) { - dest = (unsigned int *) (buffer + y * row_stride); - pix = imgStr->getLine(); - colorMap->getRGBLine (pix, dest, width); - } - - filter = getFilterForSurface (image, interpolate); - - cairo_surface_mark_dirty (image); - pattern = cairo_pattern_create_for_surface (image); - cairo_surface_destroy (image); - if (cairo_pattern_status (pattern)) - goto cleanup; - - LOG (printf ("drawMaskedImage %dx%d\n", width, height)); - - cairo_pattern_set_filter (pattern, filter); - cairo_pattern_set_filter (maskPattern, maskFilter); - - if (!printing) { - cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD); - cairo_pattern_set_extend (maskPattern, CAIRO_EXTEND_PAD); - } - - cairo_matrix_init_translate (&matrix, 0, height); - cairo_matrix_scale (&matrix, width, -height); - cairo_pattern_set_matrix (pattern, &matrix); - if (cairo_pattern_status (pattern)) { - cairo_pattern_destroy (pattern); - cairo_pattern_destroy (maskPattern); - goto cleanup; - } - - cairo_matrix_init_translate (&maskMatrix, 0, maskHeight); - cairo_matrix_scale (&maskMatrix, maskWidth, -maskHeight); - cairo_pattern_set_matrix (maskPattern, &maskMatrix); - if (cairo_pattern_status (maskPattern)) { - cairo_pattern_destroy (maskPattern); - cairo_pattern_destroy (pattern); - goto cleanup; - } - - if (!printing) { - cairo_save (cairo); - cairo_set_source (cairo, pattern); - cairo_rectangle (cairo, 0., 0., 1., 1.); - cairo_clip (cairo); - cairo_mask (cairo, maskPattern); - cairo_restore (cairo); - } else { - cairo_set_source (cairo, pattern); - cairo_mask (cairo, maskPattern); - } - - if (cairo_shape) { - cairo_save (cairo_shape); - cairo_set_source (cairo_shape, pattern); - if (!printing) { - cairo_rectangle (cairo_shape, 0., 0., 1., 1.); - cairo_fill (cairo_shape); - } else { - cairo_mask (cairo_shape, pattern); - } - cairo_restore (cairo_shape); - } - - cairo_pattern_destroy (maskPattern); - cairo_pattern_destroy (pattern); - -cleanup: - imgStr->close(); - delete imgStr; -} - - -//XXX: is this affect by AIS(alpha is shape)? -void CairoOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str, - int width, int height, - GfxImageColorMap *colorMap, - GBool interpolate, - Stream *maskStr, - int maskWidth, int maskHeight, - GfxImageColorMap *maskColorMap, - GBool maskInterpolate) -{ - ImageStream *maskImgStr, *imgStr; - int row_stride; - unsigned char *maskBuffer, *buffer; - unsigned char *maskDest; - unsigned int *dest; - cairo_surface_t *maskImage, *image; - cairo_pattern_t *maskPattern, *pattern; - cairo_matrix_t maskMatrix, matrix; - Guchar *pix; - int y; - cairo_filter_t filter; - cairo_filter_t maskFilter; - - maskImgStr = new ImageStream(maskStr, maskWidth, - maskColorMap->getNumPixelComps(), - maskColorMap->getBits()); - maskImgStr->reset(); - - maskImage = cairo_image_surface_create (CAIRO_FORMAT_A8, maskWidth, maskHeight); - if (cairo_surface_status (maskImage)) { - maskImgStr->close(); - delete maskImgStr; - return; - } - - maskBuffer = cairo_image_surface_get_data (maskImage); - row_stride = cairo_image_surface_get_stride (maskImage); - for (y = 0; y < maskHeight; y++) { - maskDest = (unsigned char *) (maskBuffer + y * row_stride); - pix = maskImgStr->getLine(); - maskColorMap->getGrayLine (pix, maskDest, maskWidth); - } - - maskImgStr->close(); - delete maskImgStr; - - maskFilter = getFilterForSurface (maskImage, maskInterpolate); - - cairo_surface_mark_dirty (maskImage); - maskPattern = cairo_pattern_create_for_surface (maskImage); - cairo_surface_destroy (maskImage); - if (cairo_pattern_status (maskPattern)) - return; - -#if 0 - /* ICCBased color space doesn't do any color correction - * so check its underlying color space as well */ - int is_identity_transform; - is_identity_transform = colorMap->getColorSpace()->getMode() == csDeviceRGB || - (colorMap->getColorSpace()->getMode() == csICCBased && - ((GfxICCBasedColorSpace*)colorMap->getColorSpace())->getAlt()->getMode() == csDeviceRGB); -#endif - - /* TODO: Do we want to cache these? */ - imgStr = new ImageStream(str, width, - colorMap->getNumPixelComps(), - colorMap->getBits()); - imgStr->reset(); - - image = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height); - if (cairo_surface_status (image)) - goto cleanup; - - buffer = cairo_image_surface_get_data (image); - row_stride = cairo_image_surface_get_stride (image); - for (y = 0; y < height; y++) { - dest = (unsigned int *) (buffer + y * row_stride); - pix = imgStr->getLine(); - colorMap->getRGBLine (pix, dest, width); - } - - filter = getFilterForSurface (image, interpolate); - - cairo_surface_mark_dirty (image); - - setMimeData(str, ref, image); - - pattern = cairo_pattern_create_for_surface (image); - cairo_surface_destroy (image); - if (cairo_pattern_status (pattern)) - goto cleanup; - - LOG (printf ("drawSoftMaskedImage %dx%d\n", width, height)); - - cairo_pattern_set_filter (pattern, filter); - cairo_pattern_set_filter (maskPattern, maskFilter); - - if (!printing) { - cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD); - cairo_pattern_set_extend (maskPattern, CAIRO_EXTEND_PAD); - } - - cairo_matrix_init_translate (&matrix, 0, height); - cairo_matrix_scale (&matrix, width, -height); - cairo_pattern_set_matrix (pattern, &matrix); - if (cairo_pattern_status (pattern)) { - cairo_pattern_destroy (pattern); - cairo_pattern_destroy (maskPattern); - goto cleanup; - } - - cairo_matrix_init_translate (&maskMatrix, 0, maskHeight); - cairo_matrix_scale (&maskMatrix, maskWidth, -maskHeight); - cairo_pattern_set_matrix (maskPattern, &maskMatrix); - if (cairo_pattern_status (maskPattern)) { - cairo_pattern_destroy (maskPattern); - cairo_pattern_destroy (pattern); - goto cleanup; - } - - if (fill_opacity != 1.0) - cairo_push_group (cairo); - else - cairo_save (cairo); - - cairo_set_source (cairo, pattern); - if (!printing) { - cairo_rectangle (cairo, 0., 0., 1., 1.); - cairo_clip (cairo); - } - cairo_mask (cairo, maskPattern); - - if (fill_opacity != 1.0) { - cairo_pop_group_to_source (cairo); - cairo_save (cairo); - if (!printing) { - cairo_rectangle (cairo, 0., 0., 1., 1.); - cairo_clip (cairo); - } - cairo_paint_with_alpha (cairo, fill_opacity); - } - cairo_restore (cairo); - - if (cairo_shape) { - cairo_save (cairo_shape); - cairo_set_source (cairo_shape, pattern); - if (!printing) { - cairo_rectangle (cairo_shape, 0., 0., 1., 1.); - cairo_fill (cairo_shape); - } else { - cairo_mask (cairo_shape, pattern); - } - cairo_restore (cairo_shape); - } - - cairo_pattern_destroy (maskPattern); - cairo_pattern_destroy (pattern); - -cleanup: - imgStr->close(); - delete imgStr; -} - -GBool CairoOutputDev::getStreamData (Stream *str, char **buffer, int *length) -{ - int len, i; - char *strBuffer; - - len = 0; - str->close(); - str->reset(); - while (str->getChar() != EOF) len++; - if (len == 0) - return gFalse; - - strBuffer = (char *)gmalloc (len); - - str->close(); - str->reset(); - for (i = 0; i < len; ++i) - strBuffer[i] = str->getChar(); - - *buffer = strBuffer; - *length = len; - - return gTrue; -} - -void CairoOutputDev::setMimeData(Stream *str, Object *ref, cairo_surface_t *image) -{ - char *strBuffer; - int len; - Object obj; - - if (!printing || !(str->getKind() == strDCT || str->getKind() == strJPX)) - return; - - // colorspace in stream dict may be different from colorspace in jpx - // data - if (str->getKind() == strJPX) { - GBool hasColorSpace = !str->getDict()->lookup("ColorSpace", &obj)->isNull(); - obj.free(); - if (hasColorSpace) - return; - } - - if (getStreamData (str->getNextStream(), &strBuffer, &len)) { - cairo_status_t st; - -#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 11, 2) - if (ref && ref->isRef()) { - Ref imgRef = ref->getRef(); - GooString *surfaceId = new GooString("poppler-surface-"); - surfaceId->appendf("{0:d}-{1:d}", imgRef.gen, imgRef.num); - char *idBuffer = copyString(surfaceId->getCString()); - st = cairo_surface_set_mime_data (image, CAIRO_MIME_TYPE_UNIQUE_ID, - (const unsigned char *)idBuffer, - surfaceId->getLength(), - gfree, idBuffer); - if (st) - gfree(idBuffer); - delete surfaceId; - } -#endif - - st = cairo_surface_set_mime_data (image, - str->getKind() == strDCT ? - CAIRO_MIME_TYPE_JPEG : CAIRO_MIME_TYPE_JP2, - (const unsigned char *)strBuffer, len, - gfree, strBuffer); - if (st) - gfree (strBuffer); - } -} - -class RescaleDrawImage : public CairoRescaleBox { -private: - ImageStream *imgStr; - GfxRGB *lookup; - int width; - GfxImageColorMap *colorMap; - int *maskColors; - int current_row; - -public: - cairo_surface_t *getSourceImage(Stream *str, - int widthA, int height, - int scaledWidth, int scaledHeight, - GBool printing, - GfxImageColorMap *colorMapA, - int *maskColorsA) { - cairo_surface_t *image = NULL; - int i; - - lookup = NULL; - colorMap = colorMapA; - maskColors = maskColorsA; - width = widthA; - current_row = -1; - - /* TODO: Do we want to cache these? */ - imgStr = new ImageStream(str, width, - colorMap->getNumPixelComps(), - colorMap->getBits()); - imgStr->reset(); - -#if 0 - /* ICCBased color space doesn't do any color correction - * so check its underlying color space as well */ - int is_identity_transform; - is_identity_transform = colorMap->getColorSpace()->getMode() == csDeviceRGB || - (colorMap->getColorSpace()->getMode() == csICCBased && - ((GfxICCBasedColorSpace*)colorMap->getColorSpace())->getAlt()->getMode() == csDeviceRGB); -#endif - - // special case for one-channel (monochrome/gray/separation) images: - // build a lookup table here - if (colorMap->getNumPixelComps() == 1) { - int n; - Guchar pix; - - n = 1 << colorMap->getBits(); - lookup = (GfxRGB *)gmallocn(n, sizeof(GfxRGB)); - for (i = 0; i < n; ++i) { - pix = (Guchar)i; - - colorMap->getRGB(&pix, &lookup[i]); - } - } - - if (printing || scaledWidth >= width || scaledHeight >= height) { - // No downscaling. Create cairo image containing the source image data. - unsigned char *buffer; - int stride; - - image = cairo_image_surface_create (maskColors ? - CAIRO_FORMAT_ARGB32 : - CAIRO_FORMAT_RGB24, - width, height); - if (cairo_surface_status (image)) - goto cleanup; - - buffer = cairo_image_surface_get_data (image); - stride = cairo_image_surface_get_stride (image); - for (int y = 0; y < height; y++) { - uint32_t *dest = (uint32_t *) (buffer + y * stride); - getRow(y, dest); - } - } else { - // // Downscaling required. Create cairo image the size of the - // rescaled image and // downscale the source image data into - // the cairo image. downScaleImage() will call getRow() to read - // source image data from the image stream. This avoids having - // to create an image the size of the source image which may - // exceed cairo's 32676x32767 image size limit (and also saves a - // lot of memory). - image = cairo_image_surface_create (maskColors ? - CAIRO_FORMAT_ARGB32 : - CAIRO_FORMAT_RGB24, - scaledWidth, scaledHeight); - if (cairo_surface_status (image)) - goto cleanup; - - downScaleImage(width, height, - scaledWidth, scaledHeight, - 0, 0, scaledWidth, scaledHeight, - image); - } - cairo_surface_mark_dirty (image); - - cleanup: - gfree(lookup); - imgStr->close(); - delete imgStr; - return image; - } - - void getRow(int row_num, uint32_t *row_data) { - int i; - Guchar *pix; - - if (row_num <= current_row) - return; - - while (current_row < row_num) { - pix = imgStr->getLine(); - current_row++; - } - - if (lookup) { - Guchar *p = pix; - GfxRGB rgb; - - for (i = 0; i < width; i++) { - rgb = lookup[*p]; - row_data[i] = - ((int) colToByte(rgb.r) << 16) | - ((int) colToByte(rgb.g) << 8) | - ((int) colToByte(rgb.b) << 0); - p++; - } - } else { - colorMap->getRGBLine (pix, row_data, width); - } - - if (maskColors) { - for (int x = 0; x < width; x++) { - bool is_opaque = false; - for (int i = 0; i < colorMap->getNumPixelComps(); ++i) { - if (pix[i] < maskColors[2*i] || - pix[i] > maskColors[2*i+1]) { - is_opaque = true; - break; - } - } - if (is_opaque) - *row_data |= 0xff000000; - else - *row_data = 0; - row_data++; - pix += colorMap->getNumPixelComps(); - } - } - } - -}; - -void CairoOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, - int widthA, int heightA, - GfxImageColorMap *colorMap, - GBool interpolate, - int *maskColors, GBool inlineImg) -{ - cairo_surface_t *image; - cairo_pattern_t *pattern, *maskPattern; - cairo_matrix_t matrix; - int width, height; - int scaledWidth, scaledHeight; - cairo_filter_t filter = CAIRO_FILTER_BILINEAR; - RescaleDrawImage rescale; - - LOG (printf ("drawImage %dx%d\n", widthA, heightA)); - - getScaledSize (widthA, heightA, &scaledWidth, &scaledHeight); - image = rescale.getSourceImage(str, widthA, heightA, scaledWidth, scaledHeight, printing, colorMap, maskColors); - if (!image) - return; - - width = cairo_image_surface_get_width (image); - height = cairo_image_surface_get_height (image); - if (width == widthA && height == heightA) - filter = getFilterForSurface (image, interpolate); - - if (!inlineImg) /* don't read stream twice if it is an inline image */ - setMimeData(str, ref, image); - - pattern = cairo_pattern_create_for_surface (image); - cairo_surface_destroy (image); - if (cairo_pattern_status (pattern)) - return; - - cairo_pattern_set_filter (pattern, filter); - - if (!printing) - cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD); - - cairo_matrix_init_translate (&matrix, 0, height); - cairo_matrix_scale (&matrix, width, -height); - cairo_pattern_set_matrix (pattern, &matrix); - if (cairo_pattern_status (pattern)) { - cairo_pattern_destroy (pattern); - return; - } - - if (!mask && fill_opacity != 1.0) { - maskPattern = cairo_pattern_create_rgba (1., 1., 1., fill_opacity); - } else if (mask) { - maskPattern = cairo_pattern_reference (mask); - } else { - maskPattern = NULL; - } - - cairo_save (cairo); - cairo_set_source (cairo, pattern); - if (!printing) - cairo_rectangle (cairo, 0., 0., 1., 1.); - if (maskPattern) { - if (!printing) - cairo_clip (cairo); - cairo_set_matrix (cairo, &mask_matrix); - cairo_mask (cairo, maskPattern); - } else { - if (printing) - cairo_paint (cairo); - else - cairo_fill (cairo); - } - cairo_restore (cairo); - - cairo_pattern_destroy (maskPattern); - - if (cairo_shape) { - cairo_save (cairo_shape); - cairo_set_source (cairo_shape, pattern); - if (printing) { - cairo_paint (cairo_shape); - } else { - cairo_rectangle (cairo_shape, 0., 0., 1., 1.); - cairo_fill (cairo_shape); - } - cairo_restore (cairo_shape); - } - - cairo_pattern_destroy (pattern); -} - - -//------------------------------------------------------------------------ -// ImageOutputDev -//------------------------------------------------------------------------ - -CairoImageOutputDev::CairoImageOutputDev() -{ - images = NULL; - numImages = 0; - size = 0; - imgDrawCbk = NULL; - imgDrawCbkData = NULL; -} - -CairoImageOutputDev::~CairoImageOutputDev() -{ - int i; - - for (i = 0; i < numImages; i++) - delete images[i]; - gfree (images); -} - -void CairoImageOutputDev::saveImage(CairoImage *image) -{ - if (numImages >= size) { - size += 16; - images = (CairoImage **) greallocn (images, size, sizeof (CairoImage *)); - } - images[numImages++] = image; -} - -void CairoImageOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, - int width, int height, GBool invert, - GBool interpolate, GBool inlineImg) -{ - cairo_t *cr; - cairo_surface_t *surface; - double x1, y1, x2, y2; - double *ctm; - double mat[6]; - CairoImage *image; - - ctm = state->getCTM(); - - mat[0] = ctm[0]; - mat[1] = ctm[1]; - mat[2] = -ctm[2]; - mat[3] = -ctm[3]; - mat[4] = ctm[2] + ctm[4]; - mat[5] = ctm[3] + ctm[5]; - x1 = mat[4]; - y1 = mat[5]; - x2 = x1 + width; - y2 = y1 + height; - - image = new CairoImage (x1, y1, x2, y2); - saveImage (image); - - if (imgDrawCbk && imgDrawCbk (numImages - 1, imgDrawCbkData)) { - surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); - cr = cairo_create (surface); - setCairo (cr); - cairo_translate (cr, 0, height); - cairo_scale (cr, width, -height); - - CairoOutputDev::drawImageMask(state, ref, str, width, height, invert, interpolate, inlineImg); - image->setImage (surface); - - setCairo (NULL); - cairo_surface_destroy (surface); - cairo_destroy (cr); - } -} - -void CairoImageOutputDev::setSoftMaskFromImageMask(GfxState *state, Object *ref, Stream *str, - int width, int height, GBool invert, - GBool inlineImg, double *baseMatrix) -{ - cairo_t *cr; - cairo_surface_t *surface; - double x1, y1, x2, y2; - double *ctm; - double mat[6]; - CairoImage *image; - - ctm = state->getCTM(); - - mat[0] = ctm[0]; - mat[1] = ctm[1]; - mat[2] = -ctm[2]; - mat[3] = -ctm[3]; - mat[4] = ctm[2] + ctm[4]; - mat[5] = ctm[3] + ctm[5]; - x1 = mat[4]; - y1 = mat[5]; - x2 = x1 + width; - y2 = y1 + height; - - image = new CairoImage (x1, y1, x2, y2); - saveImage (image); - - if (imgDrawCbk && imgDrawCbk (numImages - 1, imgDrawCbkData)) { - surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); - cr = cairo_create (surface); - setCairo (cr); - cairo_translate (cr, 0, height); - cairo_scale (cr, width, -height); - - CairoOutputDev::drawImageMask(state, ref, str, width, height, invert, inlineImg, gFalse); - if (state->getFillColorSpace()->getMode() == csPattern) { - cairo_mask (cairo, mask); - } - image->setImage (surface); - - setCairo (NULL); - cairo_surface_destroy (surface); - cairo_destroy (cr); - } -} - -void CairoImageOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, - int width, int height, GfxImageColorMap *colorMap, - GBool interpolate, int *maskColors, GBool inlineImg) -{ - cairo_t *cr; - cairo_surface_t *surface; - double x1, y1, x2, y2; - double *ctm; - double mat[6]; - CairoImage *image; - - ctm = state->getCTM(); - - mat[0] = ctm[0]; - mat[1] = ctm[1]; - mat[2] = -ctm[2]; - mat[3] = -ctm[3]; - mat[4] = ctm[2] + ctm[4]; - mat[5] = ctm[3] + ctm[5]; - x1 = mat[4]; - y1 = mat[5]; - x2 = x1 + width; - y2 = y1 + height; - - image = new CairoImage (x1, y1, x2, y2); - saveImage (image); - - if (imgDrawCbk && imgDrawCbk (numImages - 1, imgDrawCbkData)) { - surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); - cr = cairo_create (surface); - setCairo (cr); - cairo_translate (cr, 0, height); - cairo_scale (cr, width, -height); - - CairoOutputDev::drawImage(state, ref, str, width, height, colorMap, interpolate, maskColors, inlineImg); - image->setImage (surface); - - setCairo (NULL); - cairo_surface_destroy (surface); - cairo_destroy (cr); - } -} - -void CairoImageOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str, - int width, int height, - GfxImageColorMap *colorMap, - GBool interpolate, - Stream *maskStr, - int maskWidth, int maskHeight, - GfxImageColorMap *maskColorMap, - GBool maskInterpolate) -{ - cairo_t *cr; - cairo_surface_t *surface; - double x1, y1, x2, y2; - double *ctm; - double mat[6]; - CairoImage *image; - - ctm = state->getCTM(); - - mat[0] = ctm[0]; - mat[1] = ctm[1]; - mat[2] = -ctm[2]; - mat[3] = -ctm[3]; - mat[4] = ctm[2] + ctm[4]; - mat[5] = ctm[3] + ctm[5]; - x1 = mat[4]; - y1 = mat[5]; - x2 = x1 + width; - y2 = y1 + height; - - image = new CairoImage (x1, y1, x2, y2); - saveImage (image); - - if (imgDrawCbk && imgDrawCbk (numImages - 1, imgDrawCbkData)) { - surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); - cr = cairo_create (surface); - setCairo (cr); - cairo_translate (cr, 0, height); - cairo_scale (cr, width, -height); - - CairoOutputDev::drawSoftMaskedImage(state, ref, str, width, height, colorMap, interpolate, - maskStr, maskWidth, maskHeight, maskColorMap, maskInterpolate); - image->setImage (surface); - - setCairo (NULL); - cairo_surface_destroy (surface); - cairo_destroy (cr); - } -} - -void CairoImageOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str, - int width, int height, - GfxImageColorMap *colorMap, - GBool interpolate, - Stream *maskStr, - int maskWidth, int maskHeight, - GBool maskInvert, GBool maskInterpolate) -{ - cairo_t *cr; - cairo_surface_t *surface; - double x1, y1, x2, y2; - double *ctm; - double mat[6]; - CairoImage *image; - - ctm = state->getCTM(); - - mat[0] = ctm[0]; - mat[1] = ctm[1]; - mat[2] = -ctm[2]; - mat[3] = -ctm[3]; - mat[4] = ctm[2] + ctm[4]; - mat[5] = ctm[3] + ctm[5]; - x1 = mat[4]; - y1 = mat[5]; - x2 = x1 + width; - y2 = y1 + height; - - image = new CairoImage (x1, y1, x2, y2); - saveImage (image); - - if (imgDrawCbk && imgDrawCbk (numImages - 1, imgDrawCbkData)) { - surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); - cr = cairo_create (surface); - setCairo (cr); - cairo_translate (cr, 0, height); - cairo_scale (cr, width, -height); - - CairoOutputDev::drawMaskedImage(state, ref, str, width, height, colorMap, interpolate, - maskStr, maskWidth, maskHeight, maskInvert, maskInterpolate); - image->setImage (surface); - - setCairo (NULL); - cairo_surface_destroy (surface); - cairo_destroy (cr); - } -} diff --git a/3rdparty/poppler/0.22.5/CairoOutputDev.h b/3rdparty/poppler/0.22.5/CairoOutputDev.h deleted file mode 100644 index a699a7b..0000000 --- a/3rdparty/poppler/0.22.5/CairoOutputDev.h +++ /dev/null @@ -1,503 +0,0 @@ -//======================================================================== -// -// CairoOutputDev.h -// -// Copyright 2003 Glyph & Cog, LLC -// Copyright 2004 Red Hat, INC -// -//======================================================================== - -//======================================================================== -// -// Modified under the Poppler project - http://poppler.freedesktop.org -// -// All changes made under the Poppler project to this file are licensed -// under GPL version 2 or later -// -// Copyright (C) 2005-2008 Jeff Muizelaar -// Copyright (C) 2005, 2006 Kristian Høgsberg -// Copyright (C) 2005 Nickolay V. Shmyrev -// Copyright (C) 2006-2011 Carlos Garcia Campos -// Copyright (C) 2008, 2009, 2011, 2012 Adrian Johnson -// Copyright (C) 2008 Michael Vrable -// Copyright (C) 2010-2012 Thomas Freitag -// -// To see a description of the changes please see the Changelog file that -// came with your tarball or type make ChangeLog if you are building from git -// -//======================================================================== - -#ifndef CAIROOUTPUTDEV_H -#define CAIROOUTPUTDEV_H - -#ifdef USE_GCC_PRAGMAS -#pragma interface -#endif - -#include "goo/gtypes.h" -#include -#include "OutputDev.h" -#include "TextOutputDev.h" -#include "GfxState.h" - -class PDFDoc; -class GfxState; -class GfxPath; -class Gfx8BitFont; -struct GfxRGB; -class CairoFontEngine; -class CairoFont; - -//------------------------------------------------------------------------ - -//------------------------------------------------------------------------ -// CairoImage -//------------------------------------------------------------------------ -class CairoImage { -public: - // Constructor. - CairoImage (double x1, double y1, double x2, double y2); - - // Destructor. - ~CairoImage (); - - // Set the image cairo surface - void setImage (cairo_surface_t *image); - - // Get the image cairo surface - cairo_surface_t *getImage () const { return image; } - - // Get the image rectangle - void getRect (double *xa1, double *ya1, double *xa2, double *ya2) - { *xa1 = x1; *ya1 = y1; *xa2 = x2; *ya2 = y2; } - -private: - cairo_surface_t *image; // image cairo surface - double x1, y1; // upper left corner - double x2, y2; // lower right corner -}; - - -//------------------------------------------------------------------------ -// CairoOutputDev -//------------------------------------------------------------------------ - -class CairoOutputDev: public OutputDev { -public: - - // Constructor. - CairoOutputDev(); - - // Destructor. - virtual ~CairoOutputDev(); - - //----- 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.) - virtual GBool upsideDown() { return gTrue; } - - // Does this device use drawChar() or drawString()? - virtual GBool useDrawChar() { return gTrue; } - - // Does this device use tilingPatternFill()? If this returns false, - // tiling pattern fills will be reduced to a series of other drawing - // operations. - virtual GBool useTilingPatternFill() { return gTrue; } - - // Does this device use functionShadedFill(), axialShadedFill(), and - // radialShadedFill()? If this returns false, these shaded fills - // will be reduced to a series of other drawing operations. -#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 12, 0) - virtual GBool useShadedFills(int type) { return type <= 7; } -#else - virtual GBool useShadedFills(int type) { return type < 4; } -#endif - - // Does this device use FillColorStop()? - virtual GBool useFillColorStop() { return gTrue; } - - // Does this device use beginType3Char/endType3Char? Otherwise, - // text in Type 3 fonts will be drawn with drawChar/drawString. - virtual GBool interpretType3Chars() { return gFalse; } - - //----- initialization and control - - // Start a page. - virtual void startPage(int pageNum, GfxState *state); - - // End a page. - virtual void endPage(); - - //----- save/restore graphics state - virtual void saveState(GfxState *state); - virtual void restoreState(GfxState *state); - - //----- update graphics state - virtual void updateAll(GfxState *state); - virtual void setDefaultCTM(double *ctm); - virtual void updateCTM(GfxState *state, double m11, double m12, - double m21, double m22, double m31, double m32); - virtual void updateLineDash(GfxState *state); - virtual void updateFlatness(GfxState *state); - virtual void updateLineJoin(GfxState *state); - virtual void updateLineCap(GfxState *state); - virtual void updateMiterLimit(GfxState *state); - virtual void updateLineWidth(GfxState *state); - virtual void updateFillColor(GfxState *state); - virtual void updateStrokeColor(GfxState *state); - virtual void updateFillOpacity(GfxState *state); - virtual void updateStrokeOpacity(GfxState *state); - virtual void updateFillColorStop(GfxState *state, double offset); - virtual void updateBlendMode(GfxState *state); - - //----- update text state - virtual void updateFont(GfxState *state); - - //----- path painting - virtual void stroke(GfxState *state); - virtual void fill(GfxState *state); - virtual void eoFill(GfxState *state); - virtual void clipToStrokePath(GfxState *state); - virtual GBool tilingPatternFill(GfxState *state, Gfx *gfx, Catalog *cat, Object *str, - double *pmat, int paintType, int tilingType, Dict *resDict, - double *mat, double *bbox, - int x0, int y0, int x1, int y1, - double xStep, double yStep); - virtual GBool axialShadedFill(GfxState *state, GfxAxialShading *shading, double tMin, double tMax); - virtual GBool axialShadedSupportExtend(GfxState *state, GfxAxialShading *shading); - virtual GBool radialShadedFill(GfxState *state, GfxRadialShading *shading, double sMin, double sMax); - virtual GBool radialShadedSupportExtend(GfxState *state, GfxRadialShading *shading); -#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 12, 0) - virtual GBool gouraudTriangleShadedFill(GfxState *state, GfxGouraudTriangleShading *shading); - virtual GBool patchMeshShadedFill(GfxState *state, GfxPatchMeshShading *shading); -#endif - - //----- path clipping - virtual void clip(GfxState *state); - virtual void eoClip(GfxState *state); - - //----- text drawing - void beginString(GfxState *state, GooString *s); - void endString(GfxState *state); - void drawChar(GfxState *state, double x, double y, - double dx, double dy, - double originX, double originY, - CharCode code, int nBytes, Unicode *u, int uLen); - void beginActualText(GfxState *state, GooString *text); - void endActualText(GfxState *state); - - virtual GBool beginType3Char(GfxState *state, double x, double y, - double dx, double dy, - CharCode code, Unicode *u, int uLen); - virtual void endType3Char(GfxState *state); - virtual void beginTextObject(GfxState *state); - virtual GBool deviceHasTextClip(GfxState *state) { return textClipPath; } - virtual void endTextObject(GfxState *state); - - //----- image drawing - virtual void drawImageMask(GfxState *state, Object *ref, Stream *str, - int width, int height, GBool invert, GBool interpolate, - GBool inlineImg); - virtual void setSoftMaskFromImageMask(GfxState *state, - Object *ref, Stream *str, - int width, int height, GBool invert, - GBool inlineImg, double *baseMatrix); - virtual void unsetSoftMaskFromImageMask(GfxState *state, double *baseMatrix); - void drawImageMaskPrescaled(GfxState *state, Object *ref, Stream *str, - int width, int height, GBool invert, GBool interpolate, - GBool inlineImg); - void drawImageMaskRegular(GfxState *state, Object *ref, Stream *str, - int width, int height, GBool invert, GBool interpolate, - GBool inlineImg); - - virtual void drawImage(GfxState *state, Object *ref, Stream *str, - int width, int height, GfxImageColorMap *colorMap, - GBool interpolate, int *maskColors, GBool inlineImg); - virtual void drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str, - int width, int height, - GfxImageColorMap *colorMap, - GBool interpolate, - Stream *maskStr, - int maskWidth, int maskHeight, - GfxImageColorMap *maskColorMap, - GBool maskInterpolate); - - virtual void drawMaskedImage(GfxState *state, Object *ref, Stream *str, - int width, int height, - GfxImageColorMap *colorMap, - GBool interpolate, - Stream *maskStr, - int maskWidth, int maskHeight, - GBool maskInvert, GBool maskInterpolate); - - //----- transparency groups and soft masks - virtual void beginTransparencyGroup(GfxState * /*state*/, double * /*bbox*/, - GfxColorSpace * /*blendingColorSpace*/, - GBool /*isolated*/, GBool /*knockout*/, - GBool /*forSoftMask*/); - virtual void endTransparencyGroup(GfxState * /*state*/); - void popTransparencyGroup(); - virtual void paintTransparencyGroup(GfxState * /*state*/, double * /*bbox*/); - virtual void setSoftMask(GfxState * /*state*/, double * /*bbox*/, GBool /*alpha*/, - Function * /*transferFunc*/, GfxColor * /*backdropColor*/); - virtual void clearSoftMask(GfxState * /*state*/); - - //----- Type 3 font operators - virtual void type3D0(GfxState *state, double wx, double wy); - virtual void type3D1(GfxState *state, double wx, double wy, - double llx, double lly, double urx, double ury); - - //----- special access - - // Called to indicate that a new PDF document has been loaded. - void startDoc(PDFDoc *docA, CairoFontEngine *fontEngine = NULL); - - GBool isReverseVideo() { return gFalse; } - - void setCairo (cairo_t *cr); - void setTextPage (TextPage *text); - void setPrinting (GBool printing) { this->printing = printing; needFontUpdate = gTrue; } - - void setInType3Char(GBool inType3Char) { this->inType3Char = inType3Char; } - void getType3GlyphWidth (double *wx, double *wy) { *wx = t3_glyph_wx; *wy = t3_glyph_wy; } - GBool hasType3GlyphBBox () { return t3_glyph_has_bbox; } - double *getType3GlyphBBox () { return t3_glyph_bbox; } - -protected: - void doPath(cairo_t *cairo, GfxState *state, GfxPath *path); - cairo_surface_t *downscaleSurface(cairo_surface_t *orig_surface); - void getScaledSize(int orig_width, int orig_height, - int *scaledWidth, int *scaledHeight); - cairo_filter_t getFilterForSurface(cairo_surface_t *image, - GBool interpolate); - GBool getStreamData (Stream *str, char **buffer, int *length); - void setMimeData(Stream *str, Object *ref, cairo_surface_t *image); - void fillToStrokePathClip(GfxState *state); - void alignStrokeCoords(GfxSubpath *subpath, int i, double *x, double *y); - - GfxRGB fill_color, stroke_color; - cairo_pattern_t *fill_pattern, *stroke_pattern; - double fill_opacity; - double stroke_opacity; - GBool stroke_adjust; - GBool adjusted_stroke_width; - GBool align_stroke_coords; - CairoFont *currentFont; - - struct StrokePathClip { - GfxPath *path; - cairo_matrix_t ctm; - double line_width; - double *dashes; - int dash_count; - double dash_offset; - cairo_line_cap_t cap; - cairo_line_join_t join; - double miter; - } *strokePathClip; - - PDFDoc *doc; // the current document - - static FT_Library ft_lib; - static GBool ft_lib_initialized; - - CairoFontEngine *fontEngine; - GBool fontEngine_owner; - - cairo_t *cairo; - cairo_matrix_t orig_matrix; - GBool needFontUpdate; // set when the font needs to be updated - GBool printing; - GBool use_show_text_glyphs; - cairo_surface_t *surface; - cairo_glyph_t *glyphs; - int glyphCount; - cairo_text_cluster_t *clusters; - int clusterCount; - char *utf8; - int utf8Count; - int utf8Max; - cairo_path_t *textClipPath; - GBool inType3Char; // inside a Type 3 CharProc - double t3_glyph_wx, t3_glyph_wy; - GBool t3_glyph_has_bbox; - double t3_glyph_bbox[4]; - - GBool prescaleImages; - - TextPage *text; // text for the current page - ActualText *actualText; - - cairo_pattern_t *group; - cairo_pattern_t *shape; - cairo_pattern_t *mask; - cairo_matrix_t mask_matrix; - cairo_surface_t *cairo_shape_surface; - cairo_t *cairo_shape; - int knockoutCount; - struct ColorSpaceStack { - GBool knockout; - GfxColorSpace *cs; - cairo_matrix_t group_matrix; - struct ColorSpaceStack *next; - } * groupColorSpaceStack; - - struct MaskStack { - cairo_pattern_t *mask; - cairo_matrix_t mask_matrix; - struct MaskStack *next; - } *maskStack; - -}; - -//------------------------------------------------------------------------ -// CairoImageOutputDev -//------------------------------------------------------------------------ - -//XXX: this should ideally not inherit from CairoOutputDev but use it instead perhaps -class CairoImageOutputDev: public CairoOutputDev { -public: - - // Constructor. - CairoImageOutputDev(); - - // Destructor. - virtual ~CairoImageOutputDev(); - - //----- 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.) - virtual GBool upsideDown() { return gTrue; } - - // Does this device use drawChar() or drawString()? - virtual GBool useDrawChar() { return gFalse; } - - // Does this device use tilingPatternFill()? If this returns false, - // tiling pattern fills will be reduced to a series of other drawing - // operations. - virtual GBool useTilingPatternFill() { return gTrue; } - - // Does this device use functionShadedFill(), axialShadedFill(), and - // radialShadedFill()? If this returns false, these shaded fills - // will be reduced to a series of other drawing operations. -#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 11, 2) - virtual GBool useShadedFills(int type) { return type <= 7; } -#else - virtual GBool useShadedFills(int type) { return type < 4; } -#endif - - // Does this device use FillColorStop()? - virtual GBool useFillColorStop() { return gFalse; } - - // Does this device use beginType3Char/endType3Char? Otherwise, - // text in Type 3 fonts will be drawn with drawChar/drawString. - virtual GBool interpretType3Chars() { return gFalse; } - - // Does this device need non-text content? - virtual GBool needNonText() { return gTrue; } - - //----- save/restore graphics state - virtual void saveState(GfxState *state) { } - virtual void restoreState(GfxState *state) { } - - //----- update graphics state - virtual void updateAll(GfxState *state) { } - virtual void setDefaultCTM(double *ctm) { } - virtual void updateCTM(GfxState *state, double m11, double m12, - double m21, double m22, double m31, double m32) { } - virtual void updateLineDash(GfxState *state) { } - virtual void updateFlatness(GfxState *state) { } - virtual void updateLineJoin(GfxState *state) { } - virtual void updateLineCap(GfxState *state) { } - virtual void updateMiterLimit(GfxState *state) { } - virtual void updateLineWidth(GfxState *state) { } - virtual void updateFillColor(GfxState *state) { } - virtual void updateStrokeColor(GfxState *state) { } - virtual void updateFillOpacity(GfxState *state) { } - virtual void updateStrokeOpacity(GfxState *state) { } - virtual void updateBlendMode(GfxState *state) { } - - //----- update text state - virtual void updateFont(GfxState *state) { } - - //----- path painting - virtual void stroke(GfxState *state) { } - virtual void fill(GfxState *state) { } - virtual void eoFill(GfxState *state) { } - virtual void clipToStrokePath(GfxState *state) { } - virtual GBool tilingPatternFill(GfxState *state, Gfx *gfx, Catalog *cat, Object *str, - double *pmat, int paintType, int tilingType, Dict *resDict, - double *mat, double *bbox, - int x0, int y0, int x1, int y1, - double xStep, double yStep) { return gTrue; } - virtual GBool axialShadedFill(GfxState *state, - GfxAxialShading *shading, - double tMin, double tMax) { return gTrue; } - virtual GBool radialShadedFill(GfxState *state, - GfxRadialShading *shading, - double sMin, double sMax) { return gTrue; } - - //----- path clipping - virtual void clip(GfxState *state) { } - virtual void eoClip(GfxState *state) { } - - //----- image drawing - virtual void drawImageMask(GfxState *state, Object *ref, Stream *str, - int width, int height, GBool invert, - GBool interpolate, GBool inlineImg); - virtual void drawImage(GfxState *state, Object *ref, Stream *str, - int width, int height, GfxImageColorMap *colorMap, - GBool interpolate, int *maskColors, GBool inlineImg); - virtual void drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str, - int width, int height, - GfxImageColorMap *colorMap, - GBool interpolate, - Stream *maskStr, - int maskWidth, int maskHeight, - GfxImageColorMap *maskColorMap, - GBool maskInterpolate); - virtual void drawMaskedImage(GfxState *state, Object *ref, Stream *str, - int width, int height, - GfxImageColorMap *colorMap, - GBool interpolate, - Stream *maskStr, - int maskWidth, int maskHeight, - GBool maskInvert, GBool maskInterpolate); - virtual void setSoftMaskFromImageMask(GfxState *state, Object *ref, Stream *str, - int width, int height, GBool invert, - GBool inlineImg, double *baseMatrix); - virtual void unsetSoftMaskFromImageMask(GfxState *state, double *baseMatrix) {} - - - //----- transparency groups and soft masks - virtual void beginTransparencyGroup(GfxState * /*state*/, double * /*bbox*/, - GfxColorSpace * /*blendingColorSpace*/, - GBool /*isolated*/, GBool /*knockout*/, - GBool /*forSoftMask*/) {} - virtual void endTransparencyGroup(GfxState * /*state*/) {} - virtual void paintTransparencyGroup(GfxState * /*state*/, double * /*bbox*/) {} - virtual void setSoftMask(GfxState * /*state*/, double * /*bbox*/, GBool /*alpha*/, - Function * /*transferFunc*/, GfxColor * /*backdropColor*/) {} - virtual void clearSoftMask(GfxState * /*state*/) {} - - //----- Image list - // By default images are not rendred - void setImageDrawDecideCbk(GBool (*cbk)(int img_id, void *data), - void *data) { imgDrawCbk = cbk; imgDrawCbkData = data; } - // Iterate through list of images. - int getNumImages() const { return numImages; } - CairoImage *getImage(int i) const { return images[i]; } - -private: - void saveImage(CairoImage *image); - - CairoImage **images; - int numImages; - int size; - GBool (*imgDrawCbk)(int img_id, void *data); - void *imgDrawCbkData; -}; - -#endif diff --git a/3rdparty/poppler/0.22.5/CairoRescaleBox.cc b/3rdparty/poppler/0.22.5/CairoRescaleBox.cc deleted file mode 100644 index 3d19689..0000000 --- a/3rdparty/poppler/0.22.5/CairoRescaleBox.cc +++ /dev/null @@ -1,377 +0,0 @@ -/* -*- Mode: c; c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t; -*- */ -/* - * Copyright © 2009 Mozilla Corporation - * - * Permission to use, copy, modify, distribute, and sell this software and its - * documentation for any purpose is hereby granted without fee, provided that - * the above copyright notice appear in all copies and that both that - * copyright notice and this permission notice appear in supporting - * documentation, and that the name of Mozilla Corporation not be used in - * advertising or publicity pertaining to distribution of the software without - * specific, written prior permission. Mozilla Corporation makes no - * representations about the suitability of this software for any purpose. It - * is provided "as is" without express or implied warranty. - * - * MOZILLA CORPORATION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, - * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT - * SHALL MOZILLA CORPORATION BE LIABLE FOR ANY SPECIAL, INDIRECT OR - * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, - * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER - * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE - * OF THIS SOFTWARE. - * - * Author: Jeff Muizelaar, Mozilla Corp. - */ - -//======================================================================== -// -// Modified under the Poppler project - http://poppler.freedesktop.org -// -// All changes made under the Poppler project to this file are licensed -// under GPL version 2 or later -// -// Copyright (C) 2012 Hib Eris -// Copyright (C) 2012 Adrian Johnson -// -// To see a description of the changes please see the Changelog file that -// came with your tarball or type make ChangeLog if you are building from git -// -//======================================================================== - - -/* This implements a box filter that supports non-integer box sizes */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include -#include -#include "goo/gmem.h" -#include "CairoRescaleBox.h" - - -/* we work in fixed point where 1. == 1 << 24 */ -#define FIXED_SHIFT 24 - - -static void downsample_row_box_filter ( - int start, int width, - uint32_t *src, uint32_t *dest, - int coverage[], int pixel_coverage) -{ - /* we need an array of the pixel contribution of each destination pixel on the boundaries. - * we invert the value to get the value on the other size of the box */ - /* - - value = a * contribution * 1/box_size - value += a * 1/box_size - value += a * 1/box_size - value += a * 1/box_size - value += a * (1 - contribution) * 1/box_size - a * (1/box_size - contribution * 1/box_size) - - box size is constant - - - value = a * contribtion_a * 1/box_size + b * contribution_b * 1/box_size - contribution_b = (1 - contribution_a) - = (1 - contribution_a_next) - */ - - /* box size = ceil(src_width/dest_width) */ - int x = 0; - - /* skip to start */ - /* XXX: it might be possible to do this directly instead of iteratively, however - * the iterative solution is simple */ - while (x < start) - { - int box = 1 << FIXED_SHIFT; - int start_coverage = coverage[x]; - box -= start_coverage; - src++; - while (box >= pixel_coverage) - { - src++; - box -= pixel_coverage; - } - x++; - } - - while (x < start + width) - { - uint32_t a = 0; - uint32_t r = 0; - uint32_t g = 0; - uint32_t b = 0; - int box = 1 << FIXED_SHIFT; - int start_coverage = coverage[x]; - - a = ((*src >> 24) & 0xff) * start_coverage; - r = ((*src >> 16) & 0xff) * start_coverage; - g = ((*src >> 8) & 0xff) * start_coverage; - b = ((*src >> 0) & 0xff) * start_coverage; - src++; - x++; - box -= start_coverage; - - while (box >= pixel_coverage) - { - a += ((*src >> 24) & 0xff) * pixel_coverage; - r += ((*src >> 16) & 0xff) * pixel_coverage; - g += ((*src >> 8) & 0xff) * pixel_coverage; - b += ((*src >> 0) & 0xff) * pixel_coverage; - src++; - - box -= pixel_coverage; - } - - /* multiply by whatever is leftover - * this ensures that we don't bias down. - * i.e. start_coverage + n*pixel_coverage + box == 1 << 24 */ - if (box > 0) - { - a += ((*src >> 24) & 0xff) * box; - r += ((*src >> 16) & 0xff) * box; - g += ((*src >> 8) & 0xff) * box; - b += ((*src >> 0) & 0xff) * box; - } - - a >>= FIXED_SHIFT; - r >>= FIXED_SHIFT; - g >>= FIXED_SHIFT; - b >>= FIXED_SHIFT; - - *dest = (a << 24) | (r << 16) | (g << 8) | b; - dest++; - } -} - -static void downsample_columns_box_filter ( - int n, - int start_coverage, - int pixel_coverage, - uint32_t *src, uint32_t *dest) -{ - int stride = n; - while (n--) { - uint32_t a = 0; - uint32_t r = 0; - uint32_t g = 0; - uint32_t b = 0; - uint32_t *column_src = src; - int box = 1 << FIXED_SHIFT; - - a = ((*column_src >> 24) & 0xff) * start_coverage; - r = ((*column_src >> 16) & 0xff) * start_coverage; - g = ((*column_src >> 8) & 0xff) * start_coverage; - b = ((*column_src >> 0) & 0xff) * start_coverage; - column_src += stride; - box -= start_coverage; - - while (box >= pixel_coverage) - { - a += ((*column_src >> 24) & 0xff) * pixel_coverage; - r += ((*column_src >> 16) & 0xff) * pixel_coverage; - g += ((*column_src >> 8) & 0xff) * pixel_coverage; - b += ((*column_src >> 0) & 0xff) * pixel_coverage; - column_src += stride; - box -= pixel_coverage; - } - - if (box > 0) { - a += ((*column_src >> 24) & 0xff) * box; - r += ((*column_src >> 16) & 0xff) * box; - g += ((*column_src >> 8) & 0xff) * box; - b += ((*column_src >> 0) & 0xff) * box; - } - - a >>= FIXED_SHIFT; - r >>= FIXED_SHIFT; - g >>= FIXED_SHIFT; - b >>= FIXED_SHIFT; - - *dest = (a << 24) | (r << 16) | (g << 8) | b; - dest++; - src++; - } -} - -static int compute_coverage (int coverage[], int src_length, int dest_length) -{ - int i; - /* num = src_length/dest_length - total = sum(pixel) / num - - pixel * 1/num == pixel * dest_length / src_length - */ - /* the average contribution of each source pixel */ - int ratio = ((1 << 24)*(long long int)dest_length)/src_length; - /* because ((1 << 24)*(long long int)dest_length) won't always be divisible by src_length - * we'll need someplace to put the other bits. - * - * We want to ensure a + n*ratio < 1<<24 - * - * 1<<24 - * */ - - double scale = (double)src_length/dest_length; - - /* for each destination pixel compute the coverage of the left most pixel included in the box */ - /* I have a proof of this, which this margin is too narrow to contain */ - for (i=0; i= i*scale - - floor((i+1)*scale) - ceil(i*scale) <= scale - - further since: floor((i+1)*scale) - ceil(i*scale) is an integer - - therefore: - floor((i+1)*scale) - ceil(i*scale) <= floor(scale) - */ - - if (left_fract == 0.) - count--; - - /* compute how much the right-most pixel contributes */ - overage = ratio*(right_fract); - - /* the remainder is the the amount that the left-most pixel - * contributes */ - coverage[i] = (1<<24) - (count * ratio + overage); - } - - return ratio; -} - - -GBool CairoRescaleBox::downScaleImage(unsigned orig_width, unsigned orig_height, - signed scaled_width, signed scaled_height, - unsigned short int start_column, unsigned short int start_row, - unsigned short int width, unsigned short int height, - cairo_surface_t *dest_surface) { - int pixel_coverage_x, pixel_coverage_y; - int dest_y; - int src_y = 0; - uint32_t *scanline; - int *x_coverage = NULL; - int *y_coverage = NULL; - uint32_t *temp_buf = NULL; - GBool retval = gFalse; - unsigned int *dest; - int dst_stride; - - dest = (unsigned int *)cairo_image_surface_get_data (dest_surface); - dst_stride = cairo_image_surface_get_stride (dest_surface); - - scanline = (uint32_t*)gmallocn3 (orig_width, 1, sizeof(int)); - - x_coverage = (int *)gmallocn3 (orig_width, 1, sizeof(int)); - y_coverage = (int *)gmallocn3 (orig_height, 1, sizeof(int)); - - /* we need to allocate enough room for ceil(src_height/dest_height)+1 - Example: - src_height = 140 - dest_height = 50 - src_height/dest_height = 2.8 - - |-------------| 2.8 pixels - |----|----|----|----| 4 pixels - need to sample 3 pixels - - |-------------| 2.8 pixels - |----|----|----|----| 4 pixels - need to sample 4 pixels - */ - - temp_buf = (uint32_t *)gmallocn3 ((orig_height + scaled_height-1)/scaled_height+1, scaled_width, sizeof(uint32_t)); - - if (!x_coverage || !y_coverage || !scanline || !temp_buf) - goto cleanup; - - pixel_coverage_x = compute_coverage (x_coverage, orig_width, scaled_width); - pixel_coverage_y = compute_coverage (y_coverage, orig_height, scaled_height); - - assert (width + start_column <= scaled_width); - - - - /* skip the rows at the beginning */ - for (dest_y = 0; dest_y < start_row; dest_y++) - { - int box = 1 << FIXED_SHIFT; - int start_coverage_y = y_coverage[dest_y]; - box -= start_coverage_y; - src_y++; - while (box >= pixel_coverage_y) - { - box -= pixel_coverage_y; - src_y++; - } - } - - for (; dest_y < start_row + height; dest_y++) - { - int columns = 0; - int box = 1 << FIXED_SHIFT; - int start_coverage_y = y_coverage[dest_y]; - - getRow(src_y, scanline); - downsample_row_box_filter (start_column, width, scanline, temp_buf + width * columns, x_coverage, pixel_coverage_x); - columns++; - src_y++; - box -= start_coverage_y; - - while (box >= pixel_coverage_y) - { - getRow(src_y, scanline); - downsample_row_box_filter (start_column, width, scanline, temp_buf + width * columns, x_coverage, pixel_coverage_x); - columns++; - src_y++; - box -= pixel_coverage_y; - } - - /* downsample any leftovers */ - if (box > 0) - { - getRow(src_y, scanline); - downsample_row_box_filter (start_column, width, scanline, temp_buf + width * columns, x_coverage, pixel_coverage_x); - columns++; - } - - /* now scale the rows we just downsampled in the y direction */ - downsample_columns_box_filter (width, start_coverage_y, pixel_coverage_y, temp_buf, dest); - dest += dst_stride / 4; - -// assert(width*columns <= ((orig_height + scaled_height-1)/scaled_height+1) * width); - } -// assert (src_y<=orig_height); - - retval = gTrue; - -cleanup: - free (x_coverage); - free (y_coverage); - free (temp_buf); - free (scanline); - - return retval; -} diff --git a/3rdparty/poppler/0.22.5/CairoRescaleBox.h b/3rdparty/poppler/0.22.5/CairoRescaleBox.h deleted file mode 100644 index 072e8a9..0000000 --- a/3rdparty/poppler/0.22.5/CairoRescaleBox.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright © 2009 Mozilla Corporation - * - * Permission to use, copy, modify, distribute, and sell this software and its - * documentation for any purpose is hereby granted without fee, provided that - * the above copyright notice appear in all copies and that both that - * copyright notice and this permission notice appear in supporting - * documentation, and that the name of Mozilla Corporation not be used in - * advertising or publicity pertaining to distribution of the software without - * specific, written prior permission. Mozilla Corporation makes no - * representations about the suitability of this software for any purpose. It - * is provided "as is" without express or implied warranty. - * - * MOZILLA CORPORATION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, - * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT - * SHALL MOZILLA CORPORATION BE LIABLE FOR ANY SPECIAL, INDIRECT OR - * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, - * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER - * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE - * OF THIS SOFTWARE. - * - * Author: Jeff Muizelaar, Mozilla Corp. - */ - -//======================================================================== -// -// Modified under the Poppler project - http://poppler.freedesktop.org -// -// All changes made under the Poppler project to this file are licensed -// under GPL version 2 or later -// -// Copyright (C) 2012 Adrian Johnson -// -// To see a description of the changes please see the Changelog file that -// came with your tarball or type make ChangeLog if you are building from git -// -//======================================================================== - -#ifndef CAIRO_RESCALE_BOX_H -#define CAIRO_RESCALE_BOX_H - -#include "goo/gtypes.h" -#include - -class CairoRescaleBox { -public: - - CairoRescaleBox() {}; - virtual ~CairoRescaleBox() {}; - - virtual GBool downScaleImage(unsigned orig_width, unsigned orig_height, - signed scaled_width, signed scaled_height, - unsigned short int start_column, unsigned short int start_row, - unsigned short int width, unsigned short int height, - cairo_surface_t *dest_surface); - - virtual void getRow(int row_num, uint32_t *row_data) = 0; - -}; - -#endif /* CAIRO_RESCALE_BOX_H */ diff --git a/AUTHORS b/AUTHORS index efd9552..7b8a72c 100644 --- a/AUTHORS +++ b/AUTHORS @@ -18,6 +18,7 @@ Marc Sanfacon Michele Redolfi Mick Giles Ryan Morlok +Simon Chenard Wanmin Liu Packagers: diff --git a/CMakeLists.txt b/CMakeLists.txt index 8f0aad4..9db270a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,11 +5,11 @@ set(CMAKE_BUILD_TYPE Release CACHE STRING "Build configuration (Debug, Release, project(pdf2htmlEX) cmake_minimum_required(VERSION 2.6.0 FATAL_ERROR) -option(ENABLE_SVG "Enable SVG support, for generating SVG background images and converting Type 3 fonts" OFF) +option(ENABLE_SVG "Enable SVG support, for generating SVG background images and converting Type 3 fonts" ON) include_directories(${CMAKE_SOURCE_DIR}/src) -set(PDF2HTMLEX_VERSION "0.12") +set(PDF2HTMLEX_VERSION "0.13.6") set(ARCHIVE_NAME pdf2htmlex-${PDF2HTMLEX_VERSION}) add_custom_target(dist COMMAND git archive --prefix=${ARCHIVE_NAME}/ HEAD @@ -19,21 +19,7 @@ add_custom_target(dist find_package(PkgConfig) -pkg_check_modules(POPPLER poppler>=0.25.0) -if(POPPLER_FOUND) - set(POPPLER_OLDER_THAN_0_25_0 0) - set(POPPLER_OLDER_THAN_0_23_0 0) -else() - set(POPPLER_OLDER_THAN_0_25_0 1) - pkg_check_modules(POPPLER poppler>=0.23.0) - if(POPPLER_FOUND) - set(POPPLER_OLDER_THAN_0_23_0 0) - else() - set(POPPLER_OLDER_THAN_0_23_0 1) - pkg_check_modules(POPPLER REQUIRED poppler>=0.20.0) - endif() -endif() - +pkg_check_modules(POPPLER REQUIRED poppler>=0.25.0) include_directories(${POPPLER_INCLUDE_DIRS}) link_directories(${POPPLER_LIBRARY_DIRS}) set(PDF2HTMLEX_LIBS ${PDF2HTMLEX_LIBS} ${POPPLER_LIBRARIES}) @@ -47,11 +33,7 @@ if(ENABLE_SVG) link_directories(${CAIRO_LIBRARY_DIRS}) set(PDF2HTMLEX_LIBS ${PDF2HTMLEX_LIBS} ${CAIRO_LIBRARIES}) set(ENABLE_SVG 1) - if(POPPLER_OLDER_THAN_0_23_0) - set(CAIROOUTPUTDEV_PATH 3rdparty/poppler/0.22.5) - else() - set(CAIROOUTPUTDEV_PATH 3rdparty/poppler/git) - endif() + set(CAIROOUTPUTDEV_PATH 3rdparty/poppler/git) include_directories(${CAIROOUTPUTDEV_PATH}) set(PDF2HTMLEX_SRC ${PDF2HTMLEX_SRC} ${CAIROOUTPUTDEV_PATH}/CairoFontEngine.h @@ -102,10 +84,20 @@ else() set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") endif() -include(CheckCXXCompilerFlag) -check_cxx_compiler_flag("${CMAKE_CXX_FLAGS}" CXX0X_SUPPORT) +# check the C++11 features we need +include(CheckCXXSourceCompiles) +check_cxx_source_compiles(" +#include +int main() +{ + char * ptr = nullptr; + std::vector v; + auto f = [&](){ for(auto & i : v) ++i; }; + f(); +} +" CXX0X_SUPPORT) if(NOT CXX0X_SUPPORT) - message(FATAL_ERROR "Error: your compiler does not support C++0x, please update it.") + message(FATAL_ERROR "Error: your compiler does not support C++0x/C++11, please update it.") endif() @@ -127,6 +119,7 @@ set(PDF2HTMLEX_SRC ${PDF2HTMLEX_SRC} src/HTMLRenderer/general.cc src/HTMLRenderer/image.cc src/HTMLRenderer/font.cc + src/HTMLRenderer/form.cc src/HTMLRenderer/link.cc src/HTMLRenderer/outline.cc src/HTMLRenderer/state.cc @@ -221,5 +214,4 @@ install (FILES ${PDF2HTMLEX_RESOURCE} DESTINATION share/pdf2htmlEX) install (FILES pdf2htmlEX.1 DESTINATION share/man/man1) enable_testing() -add_test(test_naming - python ${CMAKE_SOURCE_DIR}/test/test_naming.py) +add_test(test python ${CMAKE_SOURCE_DIR}/test/test.py) diff --git a/ChangeLog b/ChangeLog index 858e6a8..e468f70 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +Developing + +* Do not support Poppler < 0.25.0 any more +* ENABLE_SVG is enabled by default +* Improved DrawingTracer +* Workarounds for chrome/webkit + v0.12 2014.07.24 diff --git a/README.md b/README.md index c3fa145..3b49cb7 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Learn more about [who](https://github.com/coolwanglu/pdf2htmlEX/wiki/Use-Cases) * Native HTML text with precise font and location. * Flexible output: all-in-one HTML or on demand page loading (needs JavaScript). * Moderate file size, sometimes even smaller than PDF. -* Support for links, outlines (bookmarks), printing, SVG background, Type 3 fonts and [more...](https://github.com/coolwanglu/pdf2htmlEX/wiki/Feature-List) +* Supporting links, outlines (bookmarks), printing, SVG background, Type 3 fonts and [more...](https://github.com/coolwanglu/pdf2htmlEX/wiki/Feature-List) [Compare to others](https://github.com/coolwanglu/pdf2htmlEX/wiki/Comparison) @@ -52,7 +52,7 @@ Chat with the main author: 王璐 (Lu Wang) * * [@coolwanglu](https://twitter.com/coolwanglu) * :bangbang:Questions about pdf2htmlEX? Use the mailling list instead.:bangbang: - * Accepting messages in :cn::us::gb::jp:. + * Accepting messages in :cn::us::gb::jp: (languages). Want to help without coding? Thank you! * [:moneybag:Make a donation](http://coolwanglu.github.io/pdf2htmlEX/donate.html) diff --git a/TODO b/TODO index f227c9d..928f339 100644 --- a/TODO +++ b/TODO @@ -1,3 +1,5 @@ +Redesign textline + save width in Textline, create new
in the middle link in outline: dest-detail vs hashtag @@ -16,7 +18,6 @@ more information on demo page: - browser requirements pdf:miui -tmp dir: use pid view hash - dots issue diff --git a/debian/control b/debian/control index 1399f61..c06b370 100644 --- a/debian/control +++ b/debian/control @@ -6,11 +6,11 @@ Build-Depends: cmake (>= 2.6.0), debhelper (>= 8), default-jre-headless (>= 1.6), libcairo2-dev, - libfontforge-dev, + libfontforge-dev (>= 20140000), libjpeg-dev, libpango1.0-dev, libpng12-dev, - libpoppler-dev (>= 0.20.3), + libpoppler-dev (>= 0.25.0), libspiro-dev, pkg-config, python-dev diff --git a/pdf2htmlEX.1.in b/pdf2htmlEX.1.in index 1f44984..9c9976d 100644 --- a/pdf2htmlEX.1.in +++ b/pdf2htmlEX.1.in @@ -17,7 +17,7 @@ pdf2htmlEX is a utility that converts PDF files to HTML files. pdf2htmlEX tries its best to render the PDF precisely, maintain proper styling, while retaining text and optimizing for Web. -Fonts are extracted form PDF and then embedded into HTML (Type 3 fonts are not supported). Text in the converted HTML file is usually selectable and copyable. +Fonts are extracted form PDF and then embedded into HTML, text in the converted HTML file is usually selectable and copyable. Other objects are rendered as images and also embedded. @@ -138,6 +138,10 @@ Whether to show outline in the generated HTML .B \-\-process-annotation <0|1> (Default: 0) Whether to show annotation in the generated HTML +.TP +.B \-\-process-form <0|1> (Default: 0) +Whether to include text fields and radio buttons in the generated HTML + .TP .B \-\-printing <0|1> (Default: 1) Enable printing support. Disabling this option may reduce the size of CSS. @@ -261,7 +265,11 @@ This option is only useful when '\-\-bg\-format svg' is specified. Note that nod .TP .B \-\-svg\-embed\-bitmap <0|1> (Default: 1) Whether embed bitmaps in svg background image. 1: embed bitmaps in svg background; 0: dump bitmaps to external files if possible. -JPEG images in a PDF are most possibly dumped. This option is only useful when '\-\-bg\-format svg' is specified. + +This option is only useful when '\-\-bg\-format svg' is specified and '\-\-embed\-image' is off. + +Currently, RGB or Gray JPEG bitmaps in a PDF can be dumped, while those in other formats or colorspaces are still embedded. +If bitmaps are not dumped as expected, try pre-processing your PDF by ghostscript or acrobat and make sure bitmaps in it are converted to RGB/Gray JPEG format. See the project wiki for more details. .SS PDF Protection diff --git a/share/base.css.in b/share/base.css.in index 7f61507..515134d 100644 --- a/share/base.css.in +++ b/share/base.css.in @@ -157,19 +157,23 @@ unicode-bidi:bidi-override;/* For rtl languages, e.g. Hebrew, we don't want the default Unicode behaviour */ -moz-font-feature-settings:"liga" 0;/* We don't want Firefox to recognize ligatures */ } -.@CSS_LINE_CN@:after { - /* Workaround for https://bugs.webkit.org/show_bug.cgi?id=35443 */ +.@CSS_LINE_CN@:after { /* webkit #35443 */ content: ''; } +.@CSS_LINE_CN@:before { /* Workaround Blink(up to 41)/Webkit bug of word-spacing with leading spaces (chromium #404444 and pdf2htmlEX #412) */ + content: ''; + display: inline-block; +} .@CSS_LINE_CN@ span { /* text blocks within a line */ + /* Blink(up to 41)/Webkit have bug with negative word-spacing and inline-block (pdf2htmlEX #416), so keep normal span inline. */ position:relative; - /* _ for spaces may need display:inline, which will override this */ - display:inline-block; unicode-bidi:bidi-override; /* For rtl languages, e.g. Hebrew, we don't want the default Unicode behaviour */ } .@CSS_WHITESPACE_CN@ { /* text shift */ - color:transparent; - z-index:-1; + /* Blink(up to 41)/Webkit have bug with inline element, continuous spaces and word-spacing. Workaround by inline-block. */ + display: inline-block; + color: transparent; + z-index: -1; } /* selection background should not be opaque, for fallback mode */ ::selection{ @@ -190,4 +194,14 @@ -ms-transform-origin:0% 100%; -webkit-transform-origin:0% 100%; } +/* for the forms */ +.@CSS_INPUT_TEXT_CN@ { + border: none; + background-color: rgba(255, 255, 255, 0.0); +} + +.@CSS_INPUT_RADIO_CN@:hover { + cursor: pointer; +} + /* Base CSS END */ diff --git a/share/fancy.css.in b/share/fancy.css.in index b8d9dc5..bdba3f6 100644 --- a/share/fancy.css.in +++ b/share/fancy.css.in @@ -81,5 +81,8 @@ -webkit-animation: swing 1.5s ease-in-out 0.01s infinite alternate none; animation: swing 1.5s ease-in-out 0.01s infinite alternate none; } + .@CSS_RADIO_CHECKED_CN@ { + background: no-repeat url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3goQDSYgDiGofgAAAslJREFUOMvtlM9LFGEYx7/vvOPM6ywuuyPFihWFBUsdNnA6KLIh+QPx4KWExULdHQ/9A9EfUodYmATDYg/iRewQzklFWxcEBcGgEplDkDtI6sw4PzrIbrOuedBb9MALD7zv+3m+z4/3Bf7bZS2bzQIAcrmcMDExcTeXy10DAFVVAQDksgFUVZ1ljD3yfd+0LOuFpmnvVVW9GHhkZAQcxwkNDQ2FSCQyRMgJxnVdy7KstKZpn7nwha6urqqfTqfPBAJAuVymlNLXoigOhfd5nmeiKL5TVTV+lmIKwAOA7u5u6Lped2BsbOwjY6yf4zgQQkAIAcedaPR9H67r3uYBQFEUFItFtLe332lpaVkUBOHK3t5eRtf1DwAwODiIubk5DA8PM8bYW1EU+wEgCIJqsCAIQAiB7/u253k2BQDDMJBKpa4mEon5eDx+UxAESJL0uK2t7XosFlvSdf0QAEmlUnlRFJ9Waho2Qghc1/U9z3uWz+eX+Wr+lL6SZfleEAQIggA8z6OpqSknimIvYyybSCReMsZ6TislhCAIAti2Dc/zejVNWwCAavN8339j27YbTg0AGGM3WltbP4WhlRWq6Q/btrs1TVsYHx+vNgqKoqBUKn2NRqPFxsbGJzzP05puUlpt0ukyOI6z7zjOwNTU1OLo6CgmJyf/gA3DgKIoWF1d/cIY24/FYgOU0pp0z/Ityzo8Pj5OTk9PbwHA+vp6zWghDC+VSiuRSOQgGo32UErJ38CO42wdHR09LBQK3zKZDDY2NupmFmF4R0cHVlZWlmRZ/iVJUn9FeWWcCCE4ODjYtG27Z2Zm5juAOmgdGAB2d3cBADs7O8uSJN2SZfl+WKlpmpumaT6Yn58vn/fs6XmbhmHMNjc3tzDGFI7jYJrm5vb29sDa2trPC/9aiqJUy5pOp4f6+vqeJ5PJBAB0dnZe/t8NBajx/z37Df5OGX8d13xzAAAAAElFTkSuQmCC); + } } /* Fancy CSS END */ diff --git a/share/manifest b/share/manifest index 74088d1..b03560f 100644 --- a/share/manifest +++ b/share/manifest @@ -11,6 +11,7 @@ # # Special # If a line contains """ only, all text until next """ will be included +# #TEST_IGNORE_BEGIN & #TEST_IGNORE_END are used for unittest ############# # Declaration - Do not modify @@ -28,8 +29,10 @@ # Styles # base CSS styles - Do not modify @base.min.css + # fancy CSS styles - Optional @fancy.min.css + # PDF specific CSS styles - Do not modify $css @@ -39,7 +42,9 @@ $css # compatibility.min.js, extracted from PDF.js # To support old browsers like IE9 +#TEST_IGNORE_BEGIN @compatibility.min.js +#TEST_IGNORE_END # entry point of pdf2htmlEX.Viewer # You can override default configuration by passing an object to the constructor of Viewer @@ -48,6 +53,7 @@ $css # pdf2htmlEX.defaultViewer = new pdf2htmlEX.Viewer({ # 'key_handler' : false # }); +#TEST_IGNORE_BEGIN @pdf2htmlEX.min.js """ """ +#TEST_IGNORE_END + ############# # Do not modify @@ -71,6 +79,8 @@ try{ # You can add a class 'opened' here if you want it always opened or you don't use pdf2htmlEX.js # e.g. # """ +#TEST_IGNORE_END ############# # The container of PDF pages @@ -100,6 +111,8 @@ $pages # shown when loading a page via ajax # The default appearance should be invisible # The 'active' class will be added when it is used + +#TEST_IGNORE_BEGIN """
""" @@ -107,6 +120,7 @@ $pages """
""" +#TEST_IGNORE_END ############# # Do not modify diff --git a/share/pdf2htmlEX.js.in b/share/pdf2htmlEX.js.in index ae6a688..20d4c70 100644 --- a/share/pdf2htmlEX.js.in +++ b/share/pdf2htmlEX.js.in @@ -260,6 +260,18 @@ Viewer.prototype = { this.pre_hide_pages(); }, + initialize_radio_button : function() { + var elements = document.getElementsByClassName('ir'); + + for(var i = 0; i < elements.length; i++) { + var r = elements[i]; + + r.addEventListener('click', function() { + this.classList.toggle("checked"); + }); + } + }, + init_after_loading_content : function() { this.sidebar = document.getElementById(this.config['sidebar_id']); this.outline = document.getElementById(this.config['outline_id']); @@ -312,6 +324,7 @@ Viewer.prototype = { ele.addEventListener('click', self.link_handler.bind(self), false); }); + this.initialize_radio_button(); this.render(); }, diff --git a/src/ArgParser.cc b/src/ArgParser.cc index 0c37f74..19dcf32 100644 --- a/src/ArgParser.cc +++ b/src/ArgParser.cc @@ -143,9 +143,9 @@ void ArgParser::parse(int argc, char ** argv) const void ArgParser::show_usage(ostream & out) const { - for(auto iter = arg_entries.begin(); iter != arg_entries.end(); ++iter) + for(auto & entry : arg_entries) { - (*iter)->show_usage(out); + entry->show_usage(out); } } diff --git a/src/BackgroundRenderer/BackgroundRenderer.cc b/src/BackgroundRenderer/BackgroundRenderer.cc index c5c5601..dbd7137 100644 --- a/src/BackgroundRenderer/BackgroundRenderer.cc +++ b/src/BackgroundRenderer/BackgroundRenderer.cc @@ -18,34 +18,34 @@ namespace pdf2htmlEX { -BackgroundRenderer * BackgroundRenderer::getBackgroundRenderer(const std::string & format, HTMLRenderer * html_renderer, const Param & param) +std::unique_ptr BackgroundRenderer::getBackgroundRenderer(const std::string & format, HTMLRenderer * html_renderer, const Param & param) { #ifdef ENABLE_LIBPNG if(format == "png") { - return new SplashBackgroundRenderer(format, html_renderer, param); + return std::unique_ptr(new SplashBackgroundRenderer(format, html_renderer, param)); } #endif #ifdef ENABLE_LIBJPEG if(format == "jpg") { - return new SplashBackgroundRenderer(format, html_renderer, param); + return std::unique_ptr(new SplashBackgroundRenderer(format, html_renderer, param)); } #endif #if ENABLE_SVG if (format == "svg") { - return new CairoBackgroundRenderer(html_renderer, param); + return std::unique_ptr(new CairoBackgroundRenderer(html_renderer, param)); } #endif return nullptr; } -BackgroundRenderer * BackgroundRenderer::getFallbackBackgroundRenderer(HTMLRenderer * html_renderer, const Param & param) +std::unique_ptr BackgroundRenderer::getFallbackBackgroundRenderer(HTMLRenderer * html_renderer, const Param & param) { if (param.bg_format == "svg" && param.svg_node_count_limit >= 0) - return new SplashBackgroundRenderer("", html_renderer, param); + return std::unique_ptr(new SplashBackgroundRenderer("", html_renderer, param)); return nullptr; } diff --git a/src/BackgroundRenderer/BackgroundRenderer.h b/src/BackgroundRenderer/BackgroundRenderer.h index 01675be..2927484 100644 --- a/src/BackgroundRenderer/BackgroundRenderer.h +++ b/src/BackgroundRenderer/BackgroundRenderer.h @@ -24,10 +24,10 @@ class BackgroundRenderer { public: // return nullptr upon failure - static BackgroundRenderer * getBackgroundRenderer(const std::string & format, HTMLRenderer * html_renderer, const Param & param); + static std::unique_ptr getBackgroundRenderer(const std::string & format, HTMLRenderer * html_renderer, const Param & param); // Return a fallback bg renderer according to param.bg_format. // Currently only svg bg format might need a bitmap fallback. - static BackgroundRenderer * getFallbackBackgroundRenderer(HTMLRenderer * html_renderer, const Param & param); + static std::unique_ptr getFallbackBackgroundRenderer(HTMLRenderer * html_renderer, const Param & param); BackgroundRenderer() {} virtual ~BackgroundRenderer() {} diff --git a/src/BackgroundRenderer/CairoBackgroundRenderer.cc b/src/BackgroundRenderer/CairoBackgroundRenderer.cc index d7c48b9..64a2654 100644 --- a/src/BackgroundRenderer/CairoBackgroundRenderer.cc +++ b/src/BackgroundRenderer/CairoBackgroundRenderer.cc @@ -34,11 +34,11 @@ CairoBackgroundRenderer::CairoBackgroundRenderer(HTMLRenderer * html_renderer, c CairoBackgroundRenderer::~CairoBackgroundRenderer() { - for(auto itr = bitmaps_ref_count.begin(); itr != bitmaps_ref_count.end(); ++itr) + for(auto const& p : bitmaps_ref_count) { - if (itr->second == 0) + if (p.second == 0) { - html_renderer->tmp_files.add(this->build_bitmap_path(itr->first)); + html_renderer->tmp_files.add(this->build_bitmap_path(p.first)); } } } @@ -53,10 +53,12 @@ void CairoBackgroundRenderer::drawChar(GfxState *state, double x, double y, // - OR there is special filling method // - OR using a writing mode font // - OR using a Type 3 font while param.process_type3 is not enabled + // - OR the text is used as path if((param.fallback || param.proof) || ( (state->getFont()) && ( (state->getFont()->getWMode()) || ((state->getFont()->getType() == fontType3) && (!param.process_type3)) + || (state->getRender() >= 4) ) ) ) @@ -184,8 +186,8 @@ bool CairoBackgroundRenderer::render_page(PDFDoc * doc, int pageno) } // the svg file is actually used, so add its bitmaps' ref count. - for (auto itr = bitmaps_in_current_page.begin(); itr != bitmaps_in_current_page.end(); itr++) - ++bitmaps_ref_count[*itr]; + for (auto id : bitmaps_in_current_page) + ++bitmaps_ref_count[id]; return true; } @@ -246,6 +248,34 @@ void CairoBackgroundRenderer::setMimeData(Stream *str, Object *ref, cairo_surfac if (ref == nullptr || !ref->isRef()) return; + // We only dump rgb or gray jpeg without /Decode array. + // + // Although jpeg support CMYK, PDF readers do color conversion incompatibly with most other + // programs (including browsers): other programs invert CMYK color if 'Adobe' marker (app14) presents + // in a jpeg file; while PDF readers don't, they solely rely on /Decode array to invert color. + // It's a bit complicated to decide whether a CMYK jpeg is safe to dump, so we don't dump at all. + // See also: + // JPEG file embedded in PDF (CMYK) https://forums.adobe.com/thread/975777 + // http://stackoverflow.com/questions/3123574/how-to-convert-from-cmyk-to-rgb-in-java-correctly + // + // In PDF, jpeg stream objects can also specify other color spaces like DeviceN and Separation, + // It is also not safe to dump them directly. + Object obj; + str->getDict()->lookup("ColorSpace", &obj); + if (!obj.isName() || (strcmp(obj.getName(), "DeviceRGB") && strcmp(obj.getName(), "DeviceGray")) ) + { + obj.free(); + return; + } + obj.free(); + str->getDict()->lookup("Decode", &obj); + if (obj.isArray()) + { + obj.free(); + return; + } + obj.free(); + int imgId = ref->getRef().num; auto uri = strdup((char*) html_renderer->str_fmt("o%d.jpg", imgId)); auto st = cairo_surface_set_mime_data(image, CAIRO_MIME_TYPE_URI, diff --git a/src/BackgroundRenderer/SplashBackgroundRenderer.cc b/src/BackgroundRenderer/SplashBackgroundRenderer.cc index 3217535..b2d167e 100644 --- a/src/BackgroundRenderer/SplashBackgroundRenderer.cc +++ b/src/BackgroundRenderer/SplashBackgroundRenderer.cc @@ -29,7 +29,7 @@ using std::unique_ptr; const SplashColor SplashBackgroundRenderer::white = {255,255,255}; SplashBackgroundRenderer::SplashBackgroundRenderer(const string & imgFormat, HTMLRenderer * html_renderer, const Param & param) - : SplashOutputDev(splashModeRGB8, 4, gFalse, (SplashColorPtr)(&white), gTrue, gTrue) + : SplashOutputDev(splashModeRGB8, 4, gFalse, (SplashColorPtr)(&white), gTrue) , html_renderer(html_renderer) , param(param) , format(imgFormat) @@ -56,15 +56,9 @@ SplashBackgroundRenderer::SplashBackgroundRenderer(const string & imgFormat, HTM * And thus have modified region set to the whole page area * We do not want that. */ -#if POPPLER_OLDER_THAN_0_23_0 -void SplashBackgroundRenderer::startPage(int pageNum, GfxState *state) -{ - SplashOutputDev::startPage(pageNum, state); -#else void SplashBackgroundRenderer::startPage(int pageNum, GfxState *state, XRef *xrefA) { SplashOutputDev::startPage(pageNum, state, xrefA); -#endif clearModRegion(); } @@ -78,10 +72,12 @@ void SplashBackgroundRenderer::drawChar(GfxState *state, double x, double y, // - OR there is special filling method // - OR using a writing mode font // - OR using a Type 3 font while param.process_type3 is not enabled + // - OR the text is used as path if((param.fallback || param.proof) || ( (state->getFont()) && ( (state->getFont()->getWMode()) || ((state->getFont()->getType() == fontType3) && (!param.process_type3)) + || (state->getRender() >= 4) ) ) ) diff --git a/src/BackgroundRenderer/SplashBackgroundRenderer.h b/src/BackgroundRenderer/SplashBackgroundRenderer.h index 6f664ff..067de28 100644 --- a/src/BackgroundRenderer/SplashBackgroundRenderer.h +++ b/src/BackgroundRenderer/SplashBackgroundRenderer.h @@ -39,11 +39,7 @@ public: // text in Type 3 fonts will be drawn with drawChar/drawString. virtual GBool interpretType3Chars() { return !param.process_type3; } -#if POPPLER_OLDER_THAN_0_23_0 - virtual void startPage(int pageNum, GfxState *state); -#else virtual void startPage(int pageNum, GfxState *state, XRef *xrefA); -#endif virtual void drawChar(GfxState *state, double x, double y, double dx, double dy, diff --git a/src/DrawingTracer.cc b/src/DrawingTracer.cc index 8db1437..ffabad0 100644 --- a/src/DrawingTracer.cc +++ b/src/DrawingTracer.cc @@ -10,13 +10,12 @@ #include "util/math.h" #include "DrawingTracer.h" -//#define DT_DEBUG(x) (x) -#define DT_DEBUG(x) - #if !ENABLE_SVG #warning "Cairo is disabled because ENABLE_SVG is off, --correct-text-visibility has limited functionality." #endif +static constexpr bool DT_DEBUG = false; + namespace pdf2htmlEX { @@ -39,9 +38,18 @@ void DrawingTracer::reset(GfxState *state) finish(); #if ENABLE_SVG - cairo_rectangle_t page_box {0, 0, width:state->getPageWidth(), height:state->getPageHeight()}; + // pbox is defined in device space, which is affected by zooming; + // We want to trace in page space which is stable, so invert pbox by ctm. + double pbox[] { 0, 0, state->getPageWidth(), state->getPageHeight() }; + Matrix ctm, ictm; + state->getCTM(&ctm); + ctm.invertTo(&ictm); + tm_transform_bbox(ictm.m, pbox); + cairo_rectangle_t page_box { pbox[0], pbox[1], pbox[2] - pbox[0], pbox[3] - pbox[1] }; cairo_surface_t * surface = cairo_recording_surface_create(CAIRO_CONTENT_COLOR_ALPHA, &page_box); cairo = cairo_create(surface); + if (DT_DEBUG) + printf("DrawingTracer::reset:page bbox:[%f,%f,%f,%f]\n",pbox[0], pbox[1], pbox[2], pbox[3]); #endif } @@ -73,6 +81,13 @@ void DrawingTracer::update_ctm(GfxState *state, double m11, double m12, double m matrix.x0 = m31; matrix.y0 = m32; cairo_transform(cairo, &matrix); + + if (DT_DEBUG) + { + cairo_matrix_t mat; + cairo_get_matrix(cairo, &mat); + printf("DrawingTracer::update_ctm:ctm:[%f,%f,%f,%f,%f,%f]\n", mat.xx, mat.yx, mat.xy, mat.yy, mat.x0, mat.y0); + } #endif } @@ -84,6 +99,13 @@ void DrawingTracer::clip(GfxState * state, bool even_odd) do_path(state, state->getPath()); cairo_set_fill_rule(cairo, even_odd? CAIRO_FILL_RULE_EVEN_ODD : CAIRO_FILL_RULE_WINDING); cairo_clip (cairo); + + if (DT_DEBUG) + { + double cbox[4]; + cairo_clip_extents(cairo, cbox, cbox + 1, cbox + 2, cbox + 3); + printf("DrawingTracer::clip:extents:[%f,%f,%f,%f]\n", cbox[0],cbox[1],cbox[2],cbox[3]); + } #endif } @@ -100,6 +122,8 @@ void DrawingTracer::save() return; #if ENABLE_SVG cairo_save(cairo); + if (DT_DEBUG) + printf("DrawingTracer::save\n"); #endif } void DrawingTracer::restore() @@ -108,6 +132,8 @@ void DrawingTracer::restore() return; #if ENABLE_SVG cairo_restore(cairo); + if (DT_DEBUG) + printf("DrawingTracer::restore\n"); #endif } @@ -119,12 +145,16 @@ void DrawingTracer::do_path(GfxState * state, GfxPath * path) int i, j; double x, y; cairo_new_path(cairo); + if (DT_DEBUG) + printf("DrawingTracer::do_path:new_path\n"); for (i = 0; i < path->getNumSubpaths(); ++i) { subpath = path->getSubpath(i); if (subpath->getNumPoints() > 0) { x = subpath->getX(0); y = subpath->getY(0); cairo_move_to(cairo, x, y); + if (DT_DEBUG) + printf("DrawingTracer::do_path:move_to[%f,%f]\n",x,y); j = 1; while (j < subpath->getNumPoints()) { if (subpath->getCurve(j)) { @@ -134,16 +164,22 @@ void DrawingTracer::do_path(GfxState * state, GfxPath * path) subpath->getX(j), subpath->getY(j), subpath->getX(j+1), subpath->getY(j+1), x, y); + if (DT_DEBUG) + printf("DrawingTracer::do_path:curve_to[%f,%f]\n",x,y); j += 3; } else { x = subpath->getX(j); y = subpath->getY(j); cairo_line_to(cairo, x, y); + if (DT_DEBUG) + printf("DrawingTracer::do_path:line_to[%f,%f]\n",x,y); ++j; } } if (subpath->isClosed()) { cairo_close_path (cairo); + if (DT_DEBUG) + printf("DrawingTracer::do_path:close\n"); } } } @@ -156,7 +192,8 @@ void DrawingTracer::stroke(GfxState * state) if (!param.correct_text_visibility) return; - DT_DEBUG(printf("DrawingTracer::stroke\n")); + if (DT_DEBUG) + printf("DrawingTracer::stroke\n"); cairo_set_line_width(cairo, state->getLineWidth()); @@ -197,13 +234,14 @@ void DrawingTracer::stroke(GfxState * state) ++p; } - DT_DEBUG(printf("DrawingTracer::stroke:new box:\n")); + if (DT_DEBUG) + printf("DrawingTracer::stroke:new box:\n"); double sbox[4]; cairo_stroke_extents(cairo, sbox, sbox + 1, sbox + 2, sbox + 3); if (sbox[0] != sbox[2] && sbox[1] != sbox[3]) draw_non_char_bbox(state, sbox); - else - DT_DEBUG(printf("DrawingTracer::stroke:zero box!\n")); + else if (DT_DEBUG) + printf("DrawingTracer::stroke:zero box!\n"); if (p == n) { @@ -243,7 +281,8 @@ void DrawingTracer::draw_non_char_bbox(GfxState * state, double * bbox) #endif { transform_bbox_by_ctm(bbox, state); - DT_DEBUG(printf("DrawingTracer::draw_non_char_bbox:[%f,%f,%f,%f]\n", bbox[0],bbox[1],bbox[2],bbox[3])); + if (DT_DEBUG) + printf("DrawingTracer::draw_non_char_bbox:[%f,%f,%f,%f]\n", bbox[0],bbox[1],bbox[2],bbox[3]); if (on_non_char_drawn) on_non_char_drawn(bbox); } @@ -296,7 +335,8 @@ void DrawingTracer::draw_char_bbox(GfxState * state, double * bbox) if (on_char_drawn) on_char_drawn(bbox); #endif - DT_DEBUG(printf("DrawingTracer::draw_char_bbox:[%f,%f,%f,%f]\n",bbox[0],bbox[1],bbox[2],bbox[3])); + if (DT_DEBUG) + printf("DrawingTracer::draw_char_bbox:[%f,%f,%f,%f]\n",bbox[0],bbox[1],bbox[2],bbox[3]); } void DrawingTracer::draw_image(GfxState *state) diff --git a/src/HTMLRenderer/HTMLRenderer.h b/src/HTMLRenderer/HTMLRenderer.h index 80ef25d..18e395d 100644 --- a/src/HTMLRenderer/HTMLRenderer.h +++ b/src/HTMLRenderer/HTMLRenderer.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -20,6 +21,10 @@ #include #include +// for form.cc +#include +#include + #include "pdf2htmlEX-config.h" #include "Param.h" @@ -40,9 +45,8 @@ namespace pdf2htmlEX { -class HTMLRenderer : public OutputDev +struct HTMLRenderer : OutputDev { -public: HTMLRenderer(const Param & param); virtual ~HTMLRenderer(); @@ -78,11 +82,7 @@ public: virtual void setDefaultCTM(double *ctm); // Start a page. -#if POPPLER_OLDER_THAN_0_23_0 - virtual void startPage(int pageNum, GfxState *state); -#else virtual void startPage(int pageNum, GfxState *state, XRef * xref); -#endif // End a page. virtual void endPage(); @@ -153,7 +153,7 @@ public: // Does not fail on out-of-bound conditions, but return false. bool is_char_covered(int index); // Currently drawn char (glyph) count in current page. - int get_char_count() { return (int)covered_text_detecor.get_chars_covered().size(); } + int get_char_count() { return (int)covered_text_detector.get_chars_covered().size(); } protected: //////////////////////////////////////////////////// @@ -165,6 +165,8 @@ protected: void process_outline(void); void process_outline_items(GooList * items); + void process_form(std::ofstream & out); + void set_stream_flags (std::ostream & out); void dump_css(void); @@ -308,9 +310,9 @@ protected: } new_line_state; // for font reencoding - int32_t * cur_mapping; - char ** cur_mapping2; - int * width_list; + std::vector cur_mapping; + std::vector cur_mapping2; + std::vector width_list; // width of each char Preprocessor preprocessor; @@ -325,8 +327,8 @@ protected: #if ENABLE_SVG friend class CairoBackgroundRenderer; // ugly! #endif - BackgroundRenderer * bg_renderer; - BackgroundRenderer * fallback_bg_renderer; + + std::unique_ptr bg_renderer, fallback_bg_renderer; struct { std::ofstream fs; @@ -337,7 +339,7 @@ protected: static const std::string MANIFEST_FILENAME; - CoveredTextDetector covered_text_detecor; + CoveredTextDetector covered_text_detector; DrawingTracer tracer; }; diff --git a/src/HTMLRenderer/font.cc b/src/HTMLRenderer/font.cc index c7a77ce..2ca9fa3 100644 --- a/src/HTMLRenderer/font.cc +++ b/src/HTMLRenderer/font.cc @@ -192,11 +192,7 @@ string HTMLRenderer::dump_type3_font (GfxFont * font, FontInfo & info) FT_Library ft_lib; FT_Init_FreeType(&ft_lib); CairoFontEngine font_engine(ft_lib); -#if POPPLER_OLDER_THAN_0_23_0 - auto * cur_font = font_engine.getFont(font, cur_doc, true); -#else auto * cur_font = font_engine.getFont(font, cur_doc, true, xref); -#endif auto used_map = preprocessor.get_code_map(hash_ref(font->getID())); //calculate transformed metrics @@ -398,8 +394,8 @@ void HTMLRenderer::embed_font(const string & filepath, GfxFont * font, FontInfo GfxCIDFont * font_cid = nullptr; string suffix = get_suffix(filepath); - for(auto iter = suffix.begin(); iter != suffix.end(); ++iter) - *iter = tolower(*iter); + for(auto & c : suffix) + c = tolower(c); /* * if parm->tounicode is 0, try the provided tounicode map first @@ -487,7 +483,7 @@ void HTMLRenderer::embed_font(const string & filepath, GfxFont * font, FontInfo unordered_set nameset; bool name_conflict_warned = false; - memset(cur_mapping2, 0, 0x100 * sizeof(char*)); + std::fill(cur_mapping2.begin(), cur_mapping2.end(), (char*)nullptr); for(int i = 0; i < 256; ++i) { @@ -516,7 +512,7 @@ void HTMLRenderer::embed_font(const string & filepath, GfxFont * font, FontInfo } } - ffw_reencode_raw2(cur_mapping2, 256, 0); + ffw_reencode_raw2(cur_mapping2.data(), 256, 0); } } else @@ -580,8 +576,8 @@ void HTMLRenderer::embed_font(const string & filepath, GfxFont * font, FontInfo bool name_conflict_warned = false; auto ctu = font->getToUnicode(); - memset(cur_mapping, -1, 0x10000 * sizeof(*cur_mapping)); - memset(width_list, -1, 0x10000 * sizeof(*width_list)); + std::fill(cur_mapping.begin(), cur_mapping.end(), -1); + std::fill(width_list.begin(), width_list.end(), -1); if(code2GID) maxcode = min(maxcode, code2GID_len - 1); @@ -643,9 +639,8 @@ void HTMLRenderer::embed_font(const string & filepath, GfxFont * font, FontInfo retried = true; codeset.clear(); info.use_tounicode = false; - //TODO: constant for the length - memset(cur_mapping, -1, 0x10000 * sizeof(*cur_mapping)); - memset(width_list, -1, 0x10000 * sizeof(*width_list)); + std::fill(cur_mapping.begin(), cur_mapping.end(), -1); + std::fill(width_list.begin(), width_list.end(), -1); cur_code = -1; if(param.debug) { @@ -704,9 +699,9 @@ void HTMLRenderer::embed_font(const string & filepath, GfxFont * font, FontInfo } } - ffw_set_widths(width_list, max_key + 1, param.stretch_narrow_glyph, param.squeeze_wide_glyph); + ffw_set_widths(width_list.data(), max_key + 1, param.stretch_narrow_glyph, param.squeeze_wide_glyph); - ffw_reencode_raw(cur_mapping, max_key + 1, 1); + ffw_reencode_raw(cur_mapping.data(), max_key + 1, 1); // In some space offsets in HTML, we insert a ' ' there in order to improve text copy&paste // We need to make sure that ' ' is in the font, otherwise it would be very ugly if you select the text @@ -887,7 +882,7 @@ const FontInfo * HTMLRenderer::install_font(GfxFont * font) * which does not make much sense in our case * If we specify gFalse here, font_loc->locaType cannot be gfxFontLocResident */ - if(auto * font_loc = font->locateFont(xref, gFalse)) + if(auto * font_loc = font->locateFont(xref, nullptr)) { switch(font_loc -> locType) { @@ -942,7 +937,7 @@ void HTMLRenderer::install_external_font(GfxFont * font, FontInfo & info) cerr << "Warning: workaround for font names in bad encodings." << endl; } - GfxFontLoc * localfontloc = font->locateFont(xref, gFalse); + GfxFontLoc * localfontloc = font->locateFont(xref, nullptr); if(param.embed_external_font) { @@ -1065,8 +1060,8 @@ void HTMLRenderer::export_local_font(const FontInfo & info, GfxFont * font, cons f_css.fs << "font-family:" << ((cssfont == "") ? (original_font_name + "," + general_font_family(font)) : cssfont) << ";"; string fn = original_font_name; - for(auto iter = fn.begin(); iter != fn.end(); ++iter) - *iter = tolower(*iter); + for(auto & c : fn) + c = tolower(c); if(font->isBold() || (fn.find("bold") != string::npos)) f_css.fs << "font-weight:bold;"; diff --git a/src/HTMLRenderer/form.cc b/src/HTMLRenderer/form.cc new file mode 100644 index 0000000..6b51622 --- /dev/null +++ b/src/HTMLRenderer/form.cc @@ -0,0 +1,76 @@ +/* + * form.cc + * + * Handling Forms + * + * by Simon Chenard + * 2014.07.25 + */ + +#include +#include +#include + +#include "HTMLRenderer.h" +#include "util/namespace.h" +#include "util/misc.h" + +namespace pdf2htmlEX { + +using std::ofstream; +using std::cerr; + +void HTMLRenderer::process_form(ofstream & out) +{ + FormPageWidgets * widgets = cur_catalog->getPage(pageNum)->getFormWidgets(); + int num = widgets->getNumWidgets(); + + for(int i = 0; i < num; i++) + { + FormWidget * w = widgets->getWidget(i); + double x1, y1, x2, y2; + + w->getRect(&x1, &y1, &x2, &y2); + x1 = x1 * param.zoom; + x2 = x2 * param.zoom; + y1 = y1 * param.zoom; + y2 = y2 * param.zoom; + + double width = x2 - x1; + double height = y2 - y1; + + if(w->getType() == formText) + { + double font_size = height / 2; + + out << "" << endl; + } + else if(w->getType() == formButton) + { + //Ideally would check w->getButtonType() + //for more specific rendering + width += 3; + height += 3; + + out << "
" << endl; + } + else + { + cerr << "Unsupported form field detected" << endl; + } + } +} + +} diff --git a/src/HTMLRenderer/general.cc b/src/HTMLRenderer/general.cc index a7d4e4e..6a54194 100644 --- a/src/HTMLRenderer/general.cc +++ b/src/HTMLRenderer/general.cc @@ -3,7 +3,7 @@ * * Handling general stuffs * - * Copyright (C) 2012,2013 Lu Wang + * Copyright (C) 2012,2013,2014 Lu Wang */ #include @@ -56,9 +56,10 @@ HTMLRenderer::HTMLRenderer(const Param & param) } ffw_init(param.debug); - cur_mapping = new int32_t [0x10000]; - cur_mapping2 = new char* [0x100]; - width_list = new int [0x10000]; + + cur_mapping.resize(0x10000); + cur_mapping2.resize(0x100); + width_list.resize(0x10000); /* * For these states, usually the error will not be accumulated @@ -80,19 +81,16 @@ HTMLRenderer::HTMLRenderer(const Param & param) all_manager.bottom .set_eps(EPS); tracer.on_char_drawn = - [this](double * box) { covered_text_detecor.add_char_bbox(box); }; + [this](double * box) { covered_text_detector.add_char_bbox(box); }; tracer.on_char_clipped = - [this](double * box, bool partial) { covered_text_detecor.add_char_bbox_clipped(box, partial); }; + [this](double * box, bool partial) { covered_text_detector.add_char_bbox_clipped(box, partial); }; tracer.on_non_char_drawn = - [this](double * box) { covered_text_detecor.add_non_char_bbox(box); }; + [this](double * box) { covered_text_detector.add_non_char_bbox(box); }; } HTMLRenderer::~HTMLRenderer() { ffw_finalize(); - delete [] cur_mapping; - delete [] cur_mapping2; - delete [] width_list; } void HTMLRenderer::process(PDFDoc *doc) @@ -106,8 +104,6 @@ void HTMLRenderer::process(PDFDoc *doc) /////////////////// // Process pages - bg_renderer = nullptr; - fallback_bg_renderer = nullptr; if(param.process_nontext) { bg_renderer = BackgroundRenderer::getBackgroundRenderer(param.bg_format, this, param); @@ -132,6 +128,7 @@ void HTMLRenderer::process(PDFDoc *doc) if(param.split_pages) { + // copy the string out, since we will reuse the buffer soon string filled_template_filename = (char*)str_fmt(param.page_filename.c_str(), i); auto page_fn = str_fmt("%s/%s", param.dest_dir.c_str(), filled_template_filename.c_str()); f_curpage = new ofstream((char*)page_fn, ofstream::binary); @@ -167,16 +164,8 @@ void HTMLRenderer::process(PDFDoc *doc) post_process(); - if(bg_renderer) - { - delete bg_renderer; - bg_renderer = nullptr; - } - if(fallback_bg_renderer) - { - delete fallback_bg_renderer; - fallback_bg_renderer = nullptr; - } + bg_renderer = nullptr; + fallback_bg_renderer = nullptr; cerr << endl; } @@ -186,13 +175,9 @@ void HTMLRenderer::setDefaultCTM(double *ctm) memcpy(default_ctm, ctm, sizeof(default_ctm)); } -#if POPPLER_OLDER_THAN_0_23_0 -void HTMLRenderer::startPage(int pageNum, GfxState *state) -#else void HTMLRenderer::startPage(int pageNum, GfxState *state, XRef * xref) -#endif { - covered_text_detecor.reset(); + covered_text_detector.reset(); tracer.reset(state); this->pageNum = pageNum; @@ -239,8 +224,10 @@ void HTMLRenderer::endPage() { if(param.process_nontext) { if (bg_renderer->render_page(cur_doc, pageNum)) + { bg_renderer->embed_image(pageNum); - else if (fallback_bg_renderer != nullptr) + } + else if (fallback_bg_renderer) { if (fallback_bg_renderer->render_page(cur_doc, pageNum)) fallback_bg_renderer->embed_image(pageNum); @@ -252,6 +239,10 @@ void HTMLRenderer::endPage() { html_text_page.dump_css(f_css.fs); html_text_page.clear(); + // process form + if(param.process_form) + process_form(*f_curpage); + // process links before the page is closed cur_doc->processLinks(this, pageNum); @@ -261,18 +252,20 @@ void HTMLRenderer::endPage() { // dump info for js // TODO: create a function for this // BE CAREFUL WITH ESCAPES - (*f_curpage) << "
"; + //default CTM + (*f_curpage) << "\"ctm\":["; + for(int i = 0; i < 6; ++i) + { + if(i > 0) (*f_curpage) << ","; + (*f_curpage) << round(default_ctm[i]); + } + (*f_curpage) << "]"; + + (*f_curpage) << "}'>
"; + } // close page (*f_curpage) << "" << endl; @@ -391,8 +384,8 @@ void HTMLRenderer::pre_process(PDFDoc * doc) void HTMLRenderer::post_process(void) { dump_css(); + // close files if they opened - // it's better to brace single liner LLVM complains if (param.process_outline) { f_outline.fs.close(); @@ -546,7 +539,6 @@ void HTMLRenderer::embed_file(ostream & out, const string & path, const string & string fn = get_filename(path); string suffix = (type == "") ? get_suffix(fn) : type; - // TODO auto iter = EMBED_STRING_MAP.find(suffix); if(iter == EMBED_STRING_MAP.end()) { diff --git a/src/HTMLRenderer/text.cc b/src/HTMLRenderer/text.cc index 5d5ecd9..e58a17a 100644 --- a/src/HTMLRenderer/text.cc +++ b/src/HTMLRenderer/text.cc @@ -19,7 +19,7 @@ namespace pdf2htmlEX { -using std::all_of; +using std::none_of; using std::cerr; using std::endl; @@ -107,7 +107,7 @@ void HTMLRenderer::drawString(GfxState * state, GooString * s) } else { - if((param.decompose_ligature) && (uLen > 1) && all_of(u, u+uLen, isLegalUnicode)) + if((param.decompose_ligature) && (uLen > 1) && none_of(u, u+uLen, is_illegal_unicode)) { html_text_page.get_cur_line()->append_unicodes(u, uLen, ddx); } @@ -153,7 +153,7 @@ void HTMLRenderer::drawString(GfxState * state, GooString * s) bool HTMLRenderer::is_char_covered(int index) { - auto covered = covered_text_detecor.get_chars_covered(); + auto covered = covered_text_detector.get_chars_covered(); if (index < 0 || index >= (int)covered.size()) { std::cerr << "Warning: HTMLRenderer::is_char_covered: index out of bound: " diff --git a/src/HTMLTextLine.cc b/src/HTMLTextLine.cc index ee73934..a0be286 100644 --- a/src/HTMLTextLine.cc +++ b/src/HTMLTextLine.cc @@ -36,7 +36,7 @@ HTMLTextLine::HTMLTextLine (const HTMLLineState & line_state, const Param & para void HTMLTextLine::append_unicodes(const Unicode * u, int l, double width) { - if (l == 1) + if (l == 1) text.push_back(min(u[0], (unsigned)INT_MAX)); else if (l > 1) { @@ -52,12 +52,19 @@ void HTMLTextLine::append_offset(double width) /* * If the last offset is very thin, we can ignore it and directly use it * But this should not happen often, and we will also filter near-zero offsets when outputting them - * So don't check it + * So don't check it. + * + * Offset must be appended immediately after the last real (non-padding) char, or the text optimizing + * algorithm may be confused: it may wrongly convert offsets at the beginning of a line to word-space. */ - if((!offsets.empty()) && (offsets.back().start_idx == text.size())) + + auto offset_idx = text.size(); + while (offset_idx > 0 && text[offset_idx - 1] == 0) + --offset_idx; + if((!offsets.empty()) && (offsets.back().start_idx == offset_idx)) offsets.back().width += width; else - offsets.emplace_back(text.size(), width); + offsets.emplace_back(offset_idx, width); this->width += width; } diff --git a/src/HTMLTextPage.cc b/src/HTMLTextPage.cc index 4bc23d3..a8e2ab8 100644 --- a/src/HTMLTextPage.cc +++ b/src/HTMLTextPage.cc @@ -23,10 +23,8 @@ HTMLTextPage::HTMLTextPage(const Param & param, AllStateManager & all_manager) HTMLTextPage::~HTMLTextPage() { - for(auto iter = text_lines.begin(); iter != text_lines.end(); ++iter) - { - delete (*iter); - } + for(auto p : text_lines) + delete p; } void HTMLTextPage::dump_text(ostream & out) @@ -35,12 +33,12 @@ void HTMLTextPage::dump_text(ostream & out) { // text lines may be split during optimization, collect them std::vector new_text_lines; - for(auto iter = text_lines.begin(); iter != text_lines.end(); ++iter) - (*iter)->optimize(new_text_lines); + for(auto p : text_lines) + p->optimize(new_text_lines); std::swap(text_lines, new_text_lines); } - for(auto iter = text_lines.begin(); iter != text_lines.end(); ++iter) - (*iter)->prepare(); + for(auto p : text_lines) + p->prepare(); if(param.optimize_text) optimize(); diff --git a/src/Param.h b/src/Param.h index b2330ed..84fa426 100644 --- a/src/Param.h +++ b/src/Param.h @@ -38,6 +38,7 @@ struct Param int process_nontext; int process_outline; int process_annotation; + int process_form; int correct_text_visibility; int printing; int fallback; diff --git a/src/Preprocessor.cc b/src/Preprocessor.cc index 075074e..a8859ad 100644 --- a/src/Preprocessor.cc +++ b/src/Preprocessor.cc @@ -36,8 +36,8 @@ Preprocessor::Preprocessor(const Param & param) Preprocessor::~Preprocessor(void) { - for(auto iter = code_maps.begin(); iter != code_maps.end(); ++iter) - delete [] iter->second; + for(auto & p : code_maps) + delete [] p.second; } void Preprocessor::process(PDFDoc * doc) diff --git a/src/StateManager.h b/src/StateManager.h index ffcc507..0a19df0 100644 --- a/src/StateManager.h +++ b/src/StateManager.h @@ -59,19 +59,19 @@ public: } void dump_css(std::ostream & out) { - for(auto iter = value_map.begin(); iter != value_map.end(); ++iter) + for(auto & p : value_map) { - out << "." << imp->get_css_class_name() << iter->second << "{"; - imp->dump_value(out, iter->first); + out << "." << imp->get_css_class_name() << p.second << "{"; + imp->dump_value(out, p.first); out << "}" << std::endl; } } void dump_print_css(std::ostream & out, double scale) { - for(auto iter = value_map.begin(); iter != value_map.end(); ++iter) + for(auto & p : value_map) { - out << "." << imp->get_css_class_name() << iter->second << "{"; - imp->dump_print_value(out, iter->first, scale); + out << "." << imp->get_css_class_name() << p.second << "{"; + imp->dump_print_value(out, p.first, scale); out << "}" << std::endl; } } @@ -109,10 +109,10 @@ public: } void dump_css(std::ostream & out) { - for(auto iter = value_map.begin(); iter != value_map.end(); ++iter) + for(auto & p : value_map) { - out << "." << imp->get_css_class_name() << iter->second << "{"; - imp->dump_value(out, iter->first); + out << "." << imp->get_css_class_name() << p.second << "{"; + imp->dump_value(out, p.first); out << "}" << std::endl; } } @@ -166,10 +166,10 @@ public: imp->dump_transparent(out); out << "}" << std::endl; - for(auto iter = value_map.begin(); iter != value_map.end(); ++iter) + for(auto & p : value_map) { - out << "." << imp->get_css_class_name() << iter->second << "{"; - imp->dump_value(out, iter->first); + out << "." << imp->get_css_class_name() << p.second << "{"; + imp->dump_value(out, p.first); out << "}" << std::endl; } } @@ -245,15 +245,15 @@ public: static const char * get_css_class_name (void) { return CSS::WHITESPACE_CN; } double default_value(void) { return 0; } void dump_value(std::ostream & out, double value) { - out << ((value > 0) ? "display:inline-block;width:" - : "display:inline;margin-left:") + out << ((value > 0) ? "width:" + : "margin-left:") << round(value) << "px;"; } void dump_print_value(std::ostream & out, double value, double scale) { value *= scale; - out << ((value > 0) ? "display:inline-block;width:" - : "display:inline;margin-left:") + out << ((value > 0) ? "width:" + : "margin-left:") << round(value) << "pt;"; } }; @@ -307,15 +307,15 @@ public: auto prefixes = {"", "-ms-", "-webkit-"}; if(tm_equal(m, ID_MATRIX, 4)) { - for(auto iter = prefixes.begin(); iter != prefixes.end(); ++iter) - out << *iter << "transform:none;"; + for(auto & s : prefixes) + out << s << "transform:none;"; } else { - for(auto iter = prefixes.begin(); iter != prefixes.end(); ++iter) + for(auto & s : prefixes) { // PDF use a different coordinate system from Web - out << *iter << "transform:matrix(" + out << s << "transform:matrix(" << round(m[0]) << ',' << round(-m[1]) << ',' << round(-m[2]) << ',' @@ -332,10 +332,10 @@ public: static const char * get_css_class_name (void) { return CSS::FILL_COLOR_CN; } /* override base's method, as we need some workaround in CSS */ void dump_css(std::ostream & out) { - for(auto iter = value_map.begin(); iter != value_map.end(); ++iter) + for(auto & p : value_map) { - out << "." << get_css_class_name() << iter->second - << "{color:" << iter->first << ";}" << std::endl; + out << "." << get_css_class_name() << p.second + << "{color:" << p.first << ";}" << std::endl; } } }; @@ -348,24 +348,24 @@ public: void dump_css(std::ostream & out) { // normal CSS out << "." << get_css_class_name() << CSS::INVALID_ID << "{text-shadow:none;}" << std::endl; - for(auto iter = value_map.begin(); iter != value_map.end(); ++iter) + for(auto & p : value_map) { // TODO: take the stroke width from the graphics state, // currently using 0.015em as a good default - out << "." << get_css_class_name() << iter->second << "{text-shadow:" - << "-0.015em 0 " << iter->first << "," - << "0 0.015em " << iter->first << "," - << "0.015em 0 " << iter->first << "," - << "0 -0.015em " << iter->first << ";" + out << "." << get_css_class_name() << p.second << "{text-shadow:" + << "-0.015em 0 " << p.first << "," + << "0 0.015em " << p.first << "," + << "0.015em 0 " << p.first << "," + << "0 -0.015em " << p.first << ";" << "}" << std::endl; } // webkit out << CSS::WEBKIT_ONLY << "{" << std::endl; out << "." << get_css_class_name() << CSS::INVALID_ID << "{-webkit-text-stroke:0px transparent;}" << std::endl; - for(auto iter = value_map.begin(); iter != value_map.end(); ++iter) + for(auto & p : value_map) { - out << "." << get_css_class_name() << iter->second - << "{-webkit-text-stroke:0.015em " << iter->first << ";text-shadow:none;}" << std::endl; + out << "." << get_css_class_name() << p.second + << "{-webkit-text-stroke:0.015em " << p.first << ";text-shadow:none;}" << std::endl; } out << "}" << std::endl; } @@ -385,20 +385,20 @@ public: } void dump_css(std::ostream & out) { - for(auto iter = value_map.begin(); iter != value_map.end(); ++iter) + for(auto & p : value_map) { - const auto & s = iter->second; - out << "." << CSS::PAGE_CONTENT_BOX_CN << iter->first << "{"; + const auto & s = p.second; + out << "." << CSS::PAGE_CONTENT_BOX_CN << p.first << "{"; out << "background-size:" << round(s.first) << "px " << round(s.second) << "px;"; out << "}" << std::endl; } } void dump_print_css(std::ostream & out, double scale) { - for(auto iter = value_map.begin(); iter != value_map.end(); ++iter) + for(auto & p : value_map) { - const auto & s = iter->second; - out << "." << CSS::PAGE_CONTENT_BOX_CN << iter->first << "{"; + const auto & s = p.second; + out << "." << CSS::PAGE_CONTENT_BOX_CN << p.first << "{"; out << "background-size:" << round(s.first * scale) << "pt " << round(s.second * scale) << "pt;"; out << "}" << std::endl; } diff --git a/src/TmpFiles.cc b/src/TmpFiles.cc index cecd90d..1184548 100644 --- a/src/TmpFiles.cc +++ b/src/TmpFiles.cc @@ -46,8 +46,9 @@ double TmpFiles::get_total_size() const { double total_size = 0; struct stat st; - for(auto iter = tmp_files.begin(); iter != tmp_files.end(); ++iter) { - stat(iter->c_str(), &st); + for(auto & fn : tmp_files) + { + stat(fn.c_str(), &st); total_size += st.st_size; } @@ -60,9 +61,8 @@ void TmpFiles::clean() if(!param.clean_tmp) return; - for(auto iter = tmp_files.begin(); iter != tmp_files.end(); ++iter) + for(auto & fn : tmp_files) { - const string & fn = *iter; remove(fn.c_str()); if(param.debug) cerr << "Remove temporary file: " << fn << endl; diff --git a/src/css_class_names.cmakelists.txt b/src/css_class_names.cmakelists.txt index 9240c6e..067d95a 100644 --- a/src/css_class_names.cmakelists.txt +++ b/src/css_class_names.cmakelists.txt @@ -34,3 +34,6 @@ set(CSS_WIDTH_CN "w") # Width set(CSS_BOTTTOM_CN "y") # Y set(CSS_CSS_DRAW_CN "d") # Draw set(CSS_LINK_CN "l") # Link +set(CSS_INPUT_TEXT_CN "it") # Text input +set(CSS_INPUT_RADIO_CN "ir") # Radio button +set(CSS_RADIO_CHECKED_CN "checked") # Show picture of checked out radio button diff --git a/src/pdf2htmlEX-config.h.in b/src/pdf2htmlEX-config.h.in index cc403e2..7c9b510 100644 --- a/src/pdf2htmlEX-config.h.in +++ b/src/pdf2htmlEX-config.h.in @@ -11,8 +11,6 @@ #include -#define POPPLER_OLDER_THAN_0_25_0 @POPPLER_OLDER_THAN_0_25_0@ -#define POPPLER_OLDER_THAN_0_23_0 @POPPLER_OLDER_THAN_0_23_0@ #define ENABLE_SVG @ENABLE_SVG@ namespace pdf2htmlEX { diff --git a/src/pdf2htmlEX.cc b/src/pdf2htmlEX.cc index 3d4972a..8f7edb4 100644 --- a/src/pdf2htmlEX.cc +++ b/src/pdf2htmlEX.cc @@ -164,6 +164,7 @@ void parse_options (int argc, char **argv) .add("process-nontext", ¶m.process_nontext, 1, "render graphics in addition to text") .add("process-outline", ¶m.process_outline, 1, "show outline in HTML") .add("process-annotation", ¶m.process_annotation, 0, "show annotation in HTML") + .add("process-form", ¶m.process_form, 0, "include text fields and radio buttons") .add("printing", ¶m.printing, 1, "enable printing support") .add("fallback", ¶m.fallback, 0, "output in fallback mode") .add("tmp-file-size-limit", ¶m.tmp_file_size_limit, -1, "Maximum size (in KB) used by temporary files, -1 for no limit.") @@ -344,6 +345,12 @@ void check_param() { cerr << "Warning: No hint tool is specified for truetype fonts, the result may be rendered poorly in some circumstances." << endl; } + + if (param.embed_image && (param.bg_format == "svg") && !param.svg_embed_bitmap) + { + cerr << "Warning: --svg-embed-bitmap is forced on because --embed-image is on, or the dumped bitmaps can't be loaded." << endl; + param.svg_embed_bitmap = 1; + } } int main(int argc, char **argv) diff --git a/src/util/css_const.h.in b/src/util/css_const.h.in index 143adcb..08c23fc 100644 --- a/src/util/css_const.h.in +++ b/src/util/css_const.h.in @@ -56,6 +56,10 @@ const char * const BOTTOM_CN = "@CSS_BOTTTOM_CN@"; const char * const CSS_DRAW_CN = "@CSS_CSS_DRAW_CN@"; const char * const LINK_CN = "@CSS_LINK_CN@"; +const char * const INPUT_TEXT_CN = "@CSS_INPUT_TEXT_CN@"; +const char * const INPUT_RADIO_CN = "@CSS_INPUT_RADIO_CN@"; +const char * const RADIO_CHECKED_CN = "@CSS_RADIO_CHECKED_CN@"; + } } diff --git a/src/util/encoding.cc b/src/util/encoding.cc index 78ce8b0..6b600bc 100644 --- a/src/util/encoding.cc +++ b/src/util/encoding.cc @@ -131,9 +131,9 @@ void writeURL(ostream & out, const string & s) void writeJSON(ostream & out, const string & s) { - for(auto iter = s.begin(); iter != s.end(); ++iter) + for(auto c : s) { - switch (*iter) + switch (c) { case '\\': out << "\\\\"; break; case '"': out << "\\\""; break; @@ -144,16 +144,15 @@ void writeJSON(ostream & out, const string & s) case '\n': out << "\\n"; break; case '\r': out << "\\r"; break; case '\t': out << "\\t"; break; - default: out << *iter; break; + default: out << c; break; } } } void writeAttribute(std::ostream & out, const std::string & s) { - for (auto iter = s.begin(); iter != s.end(); ++iter) + for (auto c : s) { - char c = *iter; switch(c) { case '&': diff --git a/src/util/path.cc b/src/util/path.cc index 4d451f6..5abc7a5 100644 --- a/src/util/path.cc +++ b/src/util/path.cc @@ -131,8 +131,8 @@ string get_suffix(const string & path) else { string s = fn.substr(idx); - for(auto iter = s.begin(); iter != s.end(); ++iter) - *iter = tolower(*iter); + for(auto & c : s) + c = tolower(c); return s; } } diff --git a/src/util/unicode.cc b/src/util/unicode.cc index 9a3d7de..4a2a034 100644 --- a/src/util/unicode.cc +++ b/src/util/unicode.cc @@ -18,81 +18,6 @@ using std::cerr; using std::endl; using std::ostream; -/* - * Test legal for HTML - * - * A legal unicode character should be accepted by browsers, and displayed correctly. - * Many unicode codes have special meaning which will be 'interpreted' by the browser, those should be filtered since they are not interpreted in PDF - * This function is not complete, just to be improved. - */ -bool isLegalUnicode(Unicode u) -{ - const Unicode max_small_unicode = 1024; - static bool valid_small_unicode[max_small_unicode]; - static bool valid_small_unicode_init = false; - if(!valid_small_unicode_init) - { - valid_small_unicode_init = true; - Unicode uu = 0; - - /* - * 9, 10 and 13 are interpreted as white-spaces in HTML - * `word-spacing` may be applied on them - * and the browser may not use the actual glyphs in the font - * So mark them as illegal - * - * The problem is that the correct value can not be copied out in this way - */ - while(uu <= 31) - valid_small_unicode[uu++] = false; - - /* - * 127-159 are not invalid - * 160, or 0xa0 is NBSP, which is legal in HTML - * But some browser will use the glyph for ' ' in the font, it there is one, instead of the glyphs for NBSP - * Again, `word-spacing` is applied. - * So mark it as illegal - * - * And the same problem as above, this character can no longer be copied out - */ - while(uu < 127) - valid_small_unicode[uu++] = true; - while(uu <= 160) - valid_small_unicode[uu++] = false; - - /* - * 173, or 0xad, the soft hyphen - * which can be ignored by the browser in the middle of a line - */ - while(uu < 173) - valid_small_unicode[uu++] = true; - while(uu <= 173) - valid_small_unicode[uu++] = false; - - - while(uu < max_small_unicode) - valid_small_unicode[uu++] = true; - } - - if(u < max_small_unicode) - return valid_small_unicode[u]; - - /* - * U+2029: Paragraph Separator - * TODO: check U+2028 etc - */ - if(u == 0x2029) - return false; - - /* - * Reserved code for utf-16 - */ - if((u >= 0xd800) && (u <= 0xdfff)) - return false; - - return true; -} - Unicode map_to_private(CharCode code) { Unicode private_mapping = (Unicode)(code + 0xE000); @@ -119,12 +44,8 @@ Unicode unicode_from_font (CharCode code, GfxFont * font) // may be untranslated ligature if(cname) { -#if POPPLER_OLDER_THAN_0_25_0 - Unicode ou = globalParams->mapNameToUnicode(cname); -#else Unicode ou = globalParams->mapNameToUnicodeText(cname); -#endif - if(isLegalUnicode(ou)) + if(!is_illegal_unicode(ou)) return ou; } } @@ -139,7 +60,7 @@ Unicode check_unicode(Unicode * u, int len, CharCode code, GfxFont * font) if(len == 1) { - if(isLegalUnicode(*u)) + if(!is_illegal_unicode(*u)) return *u; } diff --git a/src/util/unicode.h b/src/util/unicode.h index 8a04195..2100695 100644 --- a/src/util/unicode.h +++ b/src/util/unicode.h @@ -13,11 +13,58 @@ namespace pdf2htmlEX { -/* - * Check if the unicode is valid for HTML - * http://en.wikipedia.org/wiki/HTML_decimal_character_rendering +/** + * Check whether a unicode character is illegal for the output HTML. + * Unlike PDF readers, browsers has special treatments for such characters (normally treated as + * zero-width space), regardless of metrics and glyphs provided by fonts. So these characters + * should be mapped to unicode private area to "cheat" browsers, at the cost of loosing actual + * unicode values in the HTML. + * + * The following chart shows illegal characters in HTML by webkit, mozilla, and pdf2htmlEX (p2h). + * pdf2htmlEX's illegal character set is the union of webkit's and mozilla's, plus illegal unicode + * characters. "[" and ")" surrounding ranges denote "inclusive" and "exclusive", respectively. + * + * 00(NUL)--09(\t)--0A(\n)--0D(\r)--20(SP)--7F(DEL)--9F(APC)--A0(NBSP)--AD(SHY)--061C(ALM)--1361(Ethiopic word space) + * webkit: [--------------------------------) [------------------) [-] + * moz: [--------------------------------) [---------] [-] + * p2h: [--------------------------------) [------------------] [-] [-] [-] + * + * 200B(ZWSP)--200C(ZWNJ)--200D(ZWJ)--200E(LRM)--200F(RLM)--2028(LSEP)--2029(PSEP)--202A(LRE)--202E(RL0)--2066(LRI)--2069(PDI) + * webkit: [-----------------------------------------------] [----------] + * moz: [-] [----------] [-] [-] [----------] [------------] + * p2h: [-----------------------------------------------] [-] [-] [----------] [------------] + * + * D800(surrogate)--DFFF(surrogate)--FEFF(ZWNBSP)--FFFC(ORC)--FFFE(non-char)--FFFF(non-char) + * webkit: [-] [-] + * moz: + * p2h: [------------------] [-] [-] [-----------------] + * + * Note: 0xA0 (no-break space) affects word-spacing; and if "white-space:pre" is specified, + * \n and \r can break line, \t can shift text, so they are considered illegal. + * + * Resources (retrieved at 2015-03-16) + * * webkit + * * Avoid querying the font cache for the zero-width space glyph ( https://bugs.webkit.org/show_bug.cgi?id=90673 ) + * * treatAsZeroWidthSpace( https://github.com/WebKit/webkit/blob/17bbff7400393e9389b40cc84ce005f7cc954680/Source/WebCore/platform/graphics/FontCascade.h#L272 ) + * * mozilla + * * IsInvalidChar( http://mxr.mozilla.org/mozilla-central/source/gfx/thebes/gfxTextRun.cpp#1973 ) + * * IsBidiControl( http://mxr.mozilla.org/mozilla-central/source/intl/unicharutil/util/nsBidiUtils.h#114 ) + * * Character encodings in HTML ( http://en.wikipedia.org/wiki/Character_encodings_in_HTML#HTML_character_references ) + * * CSS Text Spec ( http://dev.w3.org/csswg/css-text/ ) + * * unicode table ( http://unicode-table.com ) + * + * TODO Web specs? IE? + * */ -bool isLegalUnicode(Unicode u); +inline bool is_illegal_unicode(Unicode c) +{ + return (c < 0x20) || (c >= 0x7F && c <= 0xA0) || (c == 0xAD) + || (c == 0x061C) || (c == 0x1361) + || (c >= 0x200B && c <= 0x200F) || (c == 0x2028) || (c == 0x2029) + || (c >= 0x202A && c <= 0x202E) || (c >= 0x2066 && c <= 0x2069) + || (c >= 0xD800 && c <= 0xDFFF) || (c == 0xFEFF) || (c == 0xFFFC) + || (c == 0xFFFE) || (c == 0xFFFF); +} Unicode map_to_private(CharCode code); diff --git a/test/.gitattributes b/test/.gitattributes new file mode 100644 index 0000000..d665251 --- /dev/null +++ b/test/.gitattributes @@ -0,0 +1,2 @@ +*.pdf binary +*.woff binary diff --git a/test/README.md b/test/README.md new file mode 100644 index 0000000..e6b1225 --- /dev/null +++ b/test/README.md @@ -0,0 +1,38 @@ +### Dependencies + +- python2 and packages + - Python Imaging Library + - Selenium + - unittest +- Firefox + +### Usage +- Run all tests: + - `./test.py` +- Run selected test suites: + - `./test.py test_local_browser` +- Run selected test case: + - `./test.py test_local_browser.test_basic_text` + - Or `./test.py test_basic_text` +- Environment variables: + - Set `P2H_TEST_SAVE_TMP=1` to keep the temporary files in `/tmp/pdf2htmlEX_test` + - Set `P2H_TEST_GEN=1` to generate new reference files + - Set `P2H_TEST_REMOTE=1` to test different browsers using Sauce Labs + - Install `sauceclient` for Python + - Set correct values for `SAUCE_USERNAME` and `SAUCE_ACCESS_KEY` + - Setup a HTTP server at `/` on port 8000 + - Enable Sauce Connect + - See `.travis.yml` as an example + +### Add new test cases + +- Make sure you have the proper copyrights. +- Using meaningful file names, a description of the file, or issueXXX.pdf. +- Make each test case minimal: + - One page only, unless the test case is about multiple pages. + - Grayscale only, unless the test case is about colors. + - Remove unnecessary elements. +- [Optional] Include the source files that the PDF file is generated from. +- Add the new PDF file to the correct folder in `test/`, and add a new function in the corresponding Python file +- Run `P2H_TEST_GEN=1 test/test.py test_issueXXX` to generate the reference, assuming that the new function is called `test_issueXXX` + diff --git a/test/browser_tests.py b/test/browser_tests.py new file mode 100644 index 0000000..278cbd8 --- /dev/null +++ b/test/browser_tests.py @@ -0,0 +1,99 @@ +#!/usr/bin/env python + +import os +import subprocess +import shutil +import unittest + +from PIL import Image, ImageChops +from test import Common + +class BrowserTests(Common): + TEST_DATA_DIR = os.path.join(Common.TEST_DIR, 'browser_tests') + + DEFAULT_PDF2HTMLEX_ARGS = [ + '--fit-width', 800, + '--last-page', 1, + '--embed', 'fi', # avoid base64 to make it faster + ] + + BROWSER_WIDTH=800 + BROWSER_HEIGHT=1200 + + @classmethod + def setUpClass(cls): + pass + + @classmethod + def tearDownClass(cls): + pass + + def run_test_case(self, filename, pdf2htmlEX_args=[], page_must_load=True): + basefilename, extension = os.path.splitext(filename) + htmlfilename = basefilename + '.html' + + ref_htmlfolder = os.path.join(self.TEST_DATA_DIR, basefilename) + ref_htmlfilename = os.path.join(ref_htmlfolder, htmlfilename) + + out_htmlfilename = os.path.join(self.cur_output_dir, htmlfilename) + + self.assertEquals(extension.lower(), '.pdf', 'Input file is not PDF') + + pdf2htmlEX_args = self.DEFAULT_PDF2HTMLEX_ARGS \ + + list(pdf2htmlEX_args) + [ + os.path.join(self.TEST_DATA_DIR, filename), + htmlfilename + ] + + result = self.run_pdf2htmlEX(pdf2htmlEX_args) + self.assertIn(htmlfilename, result['output_files'], 'HTML file is not generated') + + if self.GENERATING_MODE: + # copy generated html files + shutil.rmtree(ref_htmlfolder, True) + shutil.copytree(self.cur_output_dir, ref_htmlfolder) + return + + png_out_dir = os.path.join(self.cur_temp_dir, 'png_out') + os.mkdir(png_out_dir) + + pngfilename_out_fullpath = os.path.join(png_out_dir, basefilename + '.out.png') + self.generate_image(out_htmlfilename, pngfilename_out_fullpath) + out_img = Image.open(pngfilename_out_fullpath) + + pngfilename_ref_fullpath = os.path.join(png_out_dir, basefilename + '.ref.png') + self.generate_image(ref_htmlfilename, pngfilename_ref_fullpath, page_must_load=page_must_load) + ref_img = Image.open(pngfilename_ref_fullpath) + + diff_img = ImageChops.difference(ref_img, out_img); + + diff_bbox = diff_img.getbbox() + if diff_bbox is not None: + diff_size = (diff_bbox[2] - diff_bbox[0]) * (diff_bbox[3] - diff_bbox[1]) + img_size = ref_img.size[0] * ref_img.size[1] + if self.SAVE_TMP: + # save the diff image + # http://stackoverflow.com/questions/15721484/saving-in-png-using-pil-library-after-taking-imagechops-difference-of-two-png + diff_img.convert('RGB').save(os.path.join(png_out_dir, basefilename + '.diff.png')) + self.fail('PNG files differ by <= %d pixels, (%f%% of %d pixels in total)' % (diff_size, 1.0*diff_size/img_size, img_size)) + + @unittest.skipIf(Common.GENERATING_MODE, 'Do not auto generate reference for test_fail') + def test_fail(self): + # The HTML reference is generated manually, which mismatches the PDF + # To test if the environment can detect any errors + # E.g. when network is down, 404 message is shown for any HTML message + with self.assertRaises(AssertionError): + self.run_test_case('test_fail.pdf', page_must_load=False) + + def test_basic_text(self): + self.run_test_case('basic_text.pdf') + + def test_geneve_1564(self): + self.run_test_case('geneve_1564.pdf') + + def test_text_visibility(self): + self.run_test_case('text_visibility.pdf', ['--correct-text-visibility', 1]) + + def test_process_form(self): + self.run_test_case('with_form.pdf', ['--process-form', 1]) + diff --git a/test/browser_tests/basic_text.pdf b/test/browser_tests/basic_text.pdf new file mode 100644 index 0000000..a9a0c5c Binary files /dev/null and b/test/browser_tests/basic_text.pdf differ diff --git a/test/browser_tests/basic_text.tex b/test/browser_tests/basic_text.tex new file mode 100644 index 0000000..43ba1d2 --- /dev/null +++ b/test/browser_tests/basic_text.tex @@ -0,0 +1,36 @@ +\documentclass{article} +\begin{document} +Normal\hspace{10pt}{\tiny tiny}\hspace{10pt}Text +\pdfliteral{5 Ts} +Rise \\ +\pdfliteral{0 Ts} + +\pdfliteral{5 Tc} +CharSpace \\ +\pdfliteral{0 Tc} + +\pdfliteral{200 Tz} +Horizontal\hspace{10pt}Scale \\ +\pdfliteral{100 Tz} + +\vspace{3cm} + +\pdfliteral{q} +\pdfliteral{0.71 0.71 -0.71 0.71 0 0 cm} +Rotated +\pdfliteral{5 Ts} +Rise \\ +\pdfliteral{0 Ts} + +\pdfliteral{5 Tc} +CharSpace \\ +\pdfliteral{0 Tc} + +\pdfliteral{200 Tz} +Horizontal\hspace{10pt}Scale \\ +\pdfliteral{100 Tz} + +\pdfliteral{Q} + + +\end{document} diff --git a/test/browser_tests/basic_text/basic_text.html b/test/browser_tests/basic_text/basic_text.html new file mode 100644 index 0000000..171d451 --- /dev/null +++ b/test/browser_tests/basic_text/basic_text.html @@ -0,0 +1,107 @@ + + + + + + + + + + + + + +
+
Normal tiny Text
Rise
CharSpace
Horizontal Scale
Rotated
Rise
CharSpace
Horizontal Scale
1
+
+ + diff --git a/test/browser_tests/basic_text/f1.woff b/test/browser_tests/basic_text/f1.woff new file mode 100644 index 0000000..f655ddf Binary files /dev/null and b/test/browser_tests/basic_text/f1.woff differ diff --git a/test/browser_tests/basic_text/f2.woff b/test/browser_tests/basic_text/f2.woff new file mode 100644 index 0000000..5a70937 Binary files /dev/null and b/test/browser_tests/basic_text/f2.woff differ diff --git a/test/browser_tests/geneve_1564.pdf b/test/browser_tests/geneve_1564.pdf new file mode 100644 index 0000000..ea60837 Binary files /dev/null and b/test/browser_tests/geneve_1564.pdf differ diff --git a/test/browser_tests/geneve_1564/bg1.png b/test/browser_tests/geneve_1564/bg1.png new file mode 100644 index 0000000..78d4782 Binary files /dev/null and b/test/browser_tests/geneve_1564/bg1.png differ diff --git a/test/browser_tests/geneve_1564/f1.woff b/test/browser_tests/geneve_1564/f1.woff new file mode 100644 index 0000000..4d71f36 Binary files /dev/null and b/test/browser_tests/geneve_1564/f1.woff differ diff --git a/test/browser_tests/geneve_1564/f2.woff b/test/browser_tests/geneve_1564/f2.woff new file mode 100644 index 0000000..9470c45 Binary files /dev/null and b/test/browser_tests/geneve_1564/f2.woff differ diff --git a/test/browser_tests/geneve_1564/geneve_1564.html b/test/browser_tests/geneve_1564/geneve_1564.html new file mode 100644 index 0000000..ffb25bf --- /dev/null +++ b/test/browser_tests/geneve_1564/geneve_1564.html @@ -0,0 +1,738 @@ + + + + + + + + + + + + + +
+
1
Le premier lire de Moyſe,
Di Geneſe.
.
Ce premier lire comprend l’origine caſes de totes choſes, principalement la creation de l’homme, q’il a eſté d
commencement, ſa chete releement : comment d’n tos ont eſté procreés, por lers enormes pechés Die
les a conſmés, par le delge, reſeré hi, dont la ſemence a rempli tote la terre. Pis il deſcrit les ies, fais, reli-
gion, lignees des ſaints Patriarches, qi ont eſc deant la Loy : Les benediions, promees, alliances d Sei-
gner faies aecicex : Comment de le la terre de Chanaan ſont deſcends en Epte. Acns ont appelé ce lire, le
lire des Iſtes. Totefois ceci a obten entre nos predeceers nos, q’il eſt appelé Geneſe, qi eſt n mot Grec,
gnifiant generation origine : d’atant q’en iceli eſt deſcrite l’origine procreation de totes choſes : nom-
mément des Peres anciens, qi ont eſté tant deant q’apres le delge, e eſgard à deſcen-
d d’icex ſelon la chair.
.
I
Creation d ciel de la terre, II, 10. de tot ce qi y eſt
comprins. 3.14. De la lmiere a, 26 de l’homme, 18
qel tot eſt abietti. 2.2. 18 Die benit totes ſes œ-
res, 31 q’il a accomplies en x iors.
1
Ie
a
crea
b
a com
mence -
ment
c
le
ciel & la
terre.
2 Or la
terre eſ-
toit ſans
forme, &
vide, & les tenebres eſtoyent ſr les
abyſmes : & l’Eſprit de Die
d
eſtoit
eſpand par des les eax.
3 Adonc Die dît,
2
’il y ait lmie-
re.
e
Et la lmiere ft.
4 Et Die vid q
̃
la lmiere eſtoit bon-
ne : & ſepara la lmiere des tenebres.
5 Et Die appela la lmiere ior,& les
tenebres ni. Lors ft fai le
f
ſoir &
le matin d premier ior.
6 Pis Die dît,
3
’il y ait ne
g
eſ-
tende entre les eax, & qelle ſepare
les
h
eax daec les eax.
7 Die donc fit leſtende, & diiſa
I
Ce premier cha-
pitre eſt fort diffi-
cile : & por cette
caſe, il eſtoit de-
fend entre les He
briex de le lire &
interpreter deant
laage de trente
ans.
a
Fit de rien, &
ſans acne ma-
tiere.
1
Iob 38.4, Pſea.
33.6, 89.12.,
135.5, Eccleſtiaſti.
13.1, A. 14-15,
17.14
b
Tot premiere-
ment, & aãt q’il
y et acne crea-
tre, Iean 1.10.
2
Hebr. 11.3.
c
Le ciel & la
terre, les eax, les
abyſmes, ſe pren-
nent ici por vne
meſme choſe : aſç.
por ne matiere
cõfſe & ſans for-
me, q
̃
Die forma
& agença apres
par ſa Parole.
d
O, ſe mo-
voit. Ceſt, ſoſte-
noit et conſeroit
en ſon eſtre cette
matiere confſe.
Car il eſt impo-
ble, q
̃
acne cho-
ſe apres aoir eſté
faies,pie ſb-
ſter n ſel mo-
ment, Die ne la
ſoſtient & cõſer-
ve par ſa vert,
Pſea. 130.
e
Cette lmiere
neſtoit point en-
core a ſoleil, car
il naoit pas eſté
creé, mais eſtoit en
la main de Die,
ayãt ſon ordre ſc-
cef aec les tene-
bres, por faire le
ior & la ni &
ce iſqes a qa-
trieme ior, qe
Die fit le ſoleil
por eſtre miniſtre
& diſpenſater de
cette lmiere, aec
la lne & eſtoilles.
3
Pſea. 33.6,
136.5.
Ierem. 10.11
51.15.
f
Ici eſt la caſe
les eax, qi eſtoyent ſos leſtende,
daec celles, qi eſtoyent ſr leſten-
de. Et ft ain fai.
8 EtDie appela leſtende, Ciel. Lors
ft fai le ſoir & le matin d ſecond
ior.
9 Pis Die dît,
4 i
e les eax, qi
ſont ſos le ciel, ſoyent aemblees en
n lie, & qe le ſec apparoie. Et ft
ain fai.
10 EtDie appeꝉale ſec,Terre,& laem
blee des eax, mers. Et Die vid qe
celà eſtoit bon.
11 Et Die dît, e la terre prodiſe
verdre, herbe prodiſant ſemence, &
arbre friier, faiſant fri ſelon ſon
eſpece, leqel ait ſa ſemẽce en ſoy-meſ-
me ſr la terre. Et ft ain fai.
12 La terre dõc prodit verdre, her-
be prodiſant ſemẽce ſelon ſon eſpece,
& arbre ſans fri, leqel aoit ſa
ſemence en ſoymeſme ſelon ſon eſpe-
ce. Et Die vid qe celà eſtoit bon.
13 Lors ft fai le ſoir & le matin d
troieme ior.
14 Apres Die dît,
5 k
’il y ait lmi
naires en leſtende d ciel, por ſepa-
rer la ni d ior : & ſoyẽt en
l
gnes,
a en
porqoy les He-
briex cõmencent
le ior natrel le
ſoir apres le ſoleil
cochant.
g
Ce mot d’Eſtẽ
de, comprẽd tot
ce qi ſe voit par
des nos, tãt en
la region celeſte,
qelementaire.
4
Pſea. 33.7.
h
Il eſt ici parlé
de dex manieres
deax : asçaoir,
celles q ſont ſos
leſtende, comme
la mer, les flees,
& atres qi ſont
ſr la terre & cel-
les, qi ſont ſr
leſtende, comme
ſont les nees plei-
nes dea ça hat
en lair par des
nos. Die a mis
entre ces dex for
ces deax ne grã
de eſtende, qon
appelle le ciel : de
nos appelons
les oiſeax d ciel.
i
Ceci appartiẽt a
ſecõd ior, aqel
Die ſepara, & fit
apparoir la terre d
milie des eax.
k
Il inſtite n
noel ordre en
natre, qand il
fat & ordonne le
ſoleil diſtribter
de cette lmiere
q’il aoit creée
aant li, & aant
la lne & les eſ-
toilles.
5
Pſea. 136.7
l
Ceſt por -
gnifier dierſes di-
ſpotions qe les
corps ĩferiers ſe-
lon lordre de na-
tre ont des corps
celeſtes, cõme ca
ſes ſecõdes ordon
nees de Die à ce-
là. En qoy to-
teſfois fat fir c-
rioté & ſperſti-
tion q
̃
les hõmes
ont cõtroee ſr
celà.
+
+ + diff --git a/test/browser_tests/test_fail.pdf b/test/browser_tests/test_fail.pdf new file mode 100644 index 0000000..86882f8 Binary files /dev/null and b/test/browser_tests/test_fail.pdf differ diff --git a/test/browser_tests/test_fail.tex b/test/browser_tests/test_fail.tex new file mode 100644 index 0000000..48e5045 --- /dev/null +++ b/test/browser_tests/test_fail.tex @@ -0,0 +1,9 @@ +\documentclass{article} +\begin{document} +\Huge +The quick brown fox jumps over the lazy dog + +The quick brown fox jumps over the lazy dog + +The quick brown fox jumps over the lazy dog +\end{document} diff --git a/test/browser_tests/test_fail/THIS_FOLDER_SHOULD_BE_EMPTY b/test/browser_tests/test_fail/THIS_FOLDER_SHOULD_BE_EMPTY new file mode 100644 index 0000000..e69de29 diff --git a/test/browser_tests/text_visibility.pdf b/test/browser_tests/text_visibility.pdf new file mode 100644 index 0000000..9cdcc90 Binary files /dev/null and b/test/browser_tests/text_visibility.pdf differ diff --git a/test/browser_tests/text_visibility/bg1.png b/test/browser_tests/text_visibility/bg1.png new file mode 100644 index 0000000..63d8b89 Binary files /dev/null and b/test/browser_tests/text_visibility/bg1.png differ diff --git a/test/browser_tests/text_visibility/f1.woff b/test/browser_tests/text_visibility/f1.woff new file mode 100644 index 0000000..97f7e0b Binary files /dev/null and b/test/browser_tests/text_visibility/f1.woff differ diff --git a/test/browser_tests/text_visibility/f2.woff b/test/browser_tests/text_visibility/f2.woff new file mode 100644 index 0000000..9e136a3 Binary files /dev/null and b/test/browser_tests/text_visibility/f2.woff differ diff --git a/test/browser_tests/text_visibility/text_visibility.html b/test/browser_tests/text_visibility/text_visibility.html new file mode 100644 index 0000000..6c0001d --- /dev/null +++ b/test/browser_tests/text_visibility/text_visibility.html @@ -0,0 +1,153 @@ + + + + + + + + + + + + + +
+
ecient ecient
ecient ecient
e cient
e cient
ecient
ecient ecient ecient
ecient ecient ecient
ecient ecient ecient
ecient ecient ecient
ecient wordspace ecient
cient ecient ecient
ecient ecient ecient
ecient ecient ecient
ecient ecient ecient
ecient ecient ecient
cient ecient ecient
ecient ecient ecient
ecient ecient ecient
ecient ecient ecient
ecient ecient ecient
+
+ + diff --git a/test/browser_tests/with_form.pdf b/test/browser_tests/with_form.pdf new file mode 100644 index 0000000..e491abf Binary files /dev/null and b/test/browser_tests/with_form.pdf differ diff --git a/test/browser_tests/with_form/bg1.png b/test/browser_tests/with_form/bg1.png new file mode 100644 index 0000000..4528d03 Binary files /dev/null and b/test/browser_tests/with_form/bg1.png differ diff --git a/test/browser_tests/with_form/f1.woff b/test/browser_tests/with_form/f1.woff new file mode 100644 index 0000000..659ea5e Binary files /dev/null and b/test/browser_tests/with_form/f1.woff differ diff --git a/test/browser_tests/with_form/f2.woff b/test/browser_tests/with_form/f2.woff new file mode 100644 index 0000000..2837466 Binary files /dev/null and b/test/browser_tests/with_form/f2.woff differ diff --git a/test/browser_tests/with_form/f3.woff b/test/browser_tests/with_form/f3.woff new file mode 100644 index 0000000..0863237 Binary files /dev/null and b/test/browser_tests/with_form/f3.woff differ diff --git a/test/browser_tests/with_form/f4.woff b/test/browser_tests/with_form/f4.woff new file mode 100644 index 0000000..f402121 Binary files /dev/null and b/test/browser_tests/with_form/f4.woff differ diff --git a/test/browser_tests/with_form/f5.woff b/test/browser_tests/with_form/f5.woff new file mode 100644 index 0000000..7dd6058 Binary files /dev/null and b/test/browser_tests/with_form/f5.woff differ diff --git a/test/browser_tests/with_form/f6.woff b/test/browser_tests/with_form/f6.woff new file mode 100644 index 0000000..fc2ed0f Binary files /dev/null and b/test/browser_tests/with_form/f6.woff differ diff --git a/test/browser_tests/with_form/with_form.html b/test/browser_tests/with_form/with_form.html new file mode 100644 index 0000000..2e672fc --- /dev/null +++ b/test/browser_tests/with_form/with_form.html @@ -0,0 +1,216 @@ + + + + + + + + + + + + + +
+
Éditions « À Reproduire » Internet : www.envolee.com
Question de textes 4
7
Nom
Mona veut un chien
4 Que veut dire la mère de Mona quand elle dit : « C’est beaucoup de travail, avoir un chien » ?
a) Mona doit faire plus de recherche avant d’être prête à avoir un chien.
b) Les chiens travaillent fort afi n d’être de bons animaux de compagnie pour les gens.
c) Les chiens ont besoin de nourriture et d’exercice. Il faut aussi ramasser leurs dégâts.
d) Mona devra se reposer plus si elle a un chien.
5 Comment Mona a-t-elle acquis autant de connaissances sur les chiens ?
a) En écoutant son enseignante à l’école.
b) En visitant un site Web.
c) En écrivant à la Société protectrice des animaux.
d) En regardant une vidéo sur les chiens.
6 Pourquoi Mona veut-elle un Jack Russell ?
7 Que devra faire Mona même si elle est fatiguée ou qu’il pleut ?
8 Décris physiquement le chien que Mona veut choisir.
9 Quand fait-elle ses recherches sur le Web ?
10 Que contient le site Web préféré de Mona ? Nomme deux éléments.
REPÉRAGE
+
+
+
+
+ + + + + + +
+
+
+
+ +
+
+ + diff --git a/test/fancy.min.css b/test/fancy.min.css new file mode 100644 index 0000000..f6bfaf6 --- /dev/null +++ b/test/fancy.min.css @@ -0,0 +1,4 @@ +/* CSS for test cases */ +#page-container { + overflow:hidden; +} diff --git a/test/old/convert_to_woff.fontforge b/test/old/convert_to_woff.fontforge new file mode 100644 index 0000000..aa85cb4 --- /dev/null +++ b/test/old/convert_to_woff.fontforge @@ -0,0 +1,3 @@ +Open($1); +Generate($1:r+".woff"); +Quit(0); diff --git a/test/old/test.py b/test/old/test.py new file mode 100755 index 0000000..ccaf9cc --- /dev/null +++ b/test/old/test.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python + +DIR = 'pdf' +#DIR = '../../pdf.js/test/pdfs' + +import os +import sys + +with open('out.html','w') as outf: + outf.write('\n
') + + for f in os.listdir(DIR): + if not f.lower().endswith('.pdf'): + continue + print f + if os.system('pdf2htmlEX -l 10 --no-drm 1 --fit-width 1024 --dest-dir html --external-hint-tool="ttfautohint" "%s/%s"' % (DIR,f)) != 0: + print "error on ", f + sys.exit(-1) + + ff = f[:-3] + outf.write('%s
' % (ff,ff)) + outf.flush(); + + outf.write('
') diff --git a/test/start_xvfb.sh b/test/start_xvfb.sh new file mode 100755 index 0000000..9664154 --- /dev/null +++ b/test/start_xvfb.sh @@ -0,0 +1,2 @@ +#!/bin/sh +/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -ac -screen 0 1280x1920x16 diff --git a/test/test.py b/test/test.py index ccaf9cc..253abaa 100755 --- a/test/test.py +++ b/test/test.py @@ -1,24 +1,129 @@ #!/usr/bin/env python -DIR = 'pdf' -#DIR = '../../pdf.js/test/pdfs' - +import unittest import os import sys +import tempfile +import shutil +import subprocess -with open('out.html','w') as outf: - outf.write('\n
') +class Common(object): + SRC_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + TEST_DIR = os.path.join(SRC_DIR, 'test') + DATA_DIR = os.path.join(SRC_DIR, 'share') + PDF2HTMLEX_PATH = os.path.join(SRC_DIR, 'pdf2htmlEX') - for f in os.listdir(DIR): - if not f.lower().endswith('.pdf'): - continue - print f - if os.system('pdf2htmlEX -l 10 --no-drm 1 --fit-width 1024 --dest-dir html --external-hint-tool="ttfautohint" "%s/%s"' % (DIR,f)) != 0: - print "error on ", f - sys.exit(-1) + SAVE_TMP = bool(os.environ.get('P2H_TEST_SAVE_TMP')) + GENERATING_MODE = bool(os.environ.get('P2H_TEST_GEN')) - ff = f[:-3] - outf.write('%s
' % (ff,ff)) - outf.flush(); + CANONICAL_TEMPDIR = '/tmp/pdf2htmlEX_test' + + def setUp(self): + if not self.SAVE_TMP: + self.cur_temp_dir = tempfile.mkdtemp(prefix='pdf2htmlEX_test') + else: + shutil.rmtree(self.CANONICAL_TEMPDIR, True) + os.mkdir(self.CANONICAL_TEMPDIR) + self.cur_temp_dir = self.CANONICAL_TEMPDIR - outf.write('
') + self.cur_data_dir = os.path.join(self.cur_temp_dir, 'share') + self.cur_output_dir = os.path.join(self.cur_temp_dir, 'out') + os.mkdir(self.cur_data_dir) + os.mkdir(self.cur_output_dir) + + # filter manifest + with open(os.path.join(self.DATA_DIR, 'manifest')) as inf: + with open(os.path.join(self.cur_data_dir, 'manifest'), 'w') as outf: + ignore = False + for line in inf: + if ignore: + if line.startswith('#TEST_IGNORE_END'): + ignore = False + elif line.startswith('#TEST_IGNORE_BEGIN'): + ignore = True + else: + outf.write(line) + + # copy files + shutil.copy(os.path.join(self.DATA_DIR, 'base.min.css'), + os.path.join(self.cur_data_dir, 'base.min.css')) + shutil.copy(os.path.join(self.TEST_DIR, 'fancy.min.css'), + os.path.join(self.cur_data_dir, 'fancy.min.css')) + + def tearDown(self): + if not self.SAVE_TMP: + shutil.rmtree(self.cur_temp_dir, True) + + def run_pdf2htmlEX(self, args): + """ + Execute the pdf2htmlEX with the specified arguments. + + :type args: list of values + :param args: list of arguments to pass to executable. + :return: an object of relevant info + """ + + args = [self.PDF2HTMLEX_PATH, + '--data-dir', self.cur_data_dir, + '--dest-dir', self.cur_output_dir + ] + args + + with open(os.devnull, 'w') as fnull: + return_code = subprocess.call(list(map(str, args)), stderr=fnull) + + self.assertEquals(return_code, 0, 'cannot execute pdf2htmlEX') + + files = os.listdir(self.cur_output_dir) + + return { + 'return_code' : return_code, + 'output_files' : files + } + + +if __name__ == '__main__': + if not os.path.isfile(Common.PDF2HTMLEX_PATH) or not os.access(Common.PDF2HTMLEX_PATH, os.X_OK): + print >> sys.stderr, "Cannot locate pdf2htmlEX executable. Make sure source was built before running this test." + exit(1) + suites = [] + loader = unittest.TestLoader() + + all_modules = [] + all_modules.append(__import__('test_output')) + all_modules.append(__import__('test_local_browser')) + all_classes = ['test_output', 'test_local_browser'] + + if bool(os.environ.get('P2H_TEST_REMOTE')): + m = __import__('test_remote_browser') + all_modules.append(m) + all_classes += m.test_classnames + + test_names = [] + for name in sys.argv[1:]: + if name.find('.') != -1: + test_names.append(name) + else: + for m in all_classes: + test_names.append(m + '.' + name) + + for module in all_modules: + if len(test_names) > 0: + for n in test_names: + try: + suites.append(loader.loadTestsFromName(n, module)) + except: + pass + else: + suites.append(loader.loadTestsFromModule(module)) + + if len(suites) == 0: + print >>sys.stderr, 'No test found' + exit(1) + + failure_count = 0 + runner = unittest.TextTestRunner(verbosity=2) + for suite in suites: + result = runner.run(suite) + failure_count += len(result.errors) + len(result.failures) + + exit(failure_count) diff --git a/test/test_local_browser.py b/test/test_local_browser.py new file mode 100755 index 0000000..46527ff --- /dev/null +++ b/test/test_local_browser.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python + +# Run browsers tests with a local Firefox + +import unittest + +from selenium import webdriver +from selenium.webdriver.common.by import By +from selenium.webdriver.support.ui import WebDriverWait +from selenium.webdriver.support import expected_conditions +from browser_tests import BrowserTests + +class test_local_browser(BrowserTests, unittest.TestCase): + @classmethod + def setUpClass(cls): + super(test_local_browser, cls).setUpClass() + if not cls.GENERATING_MODE: + cls.browser = webdriver.Firefox() + cls.browser.maximize_window() + size = cls.browser.get_window_size() + assert ((size['width'] >= cls.BROWSER_WIDTH) and (size['height'] >= cls.BROWSER_HEIGHT)), 'Screen is not large enough' + cls.browser.set_window_size(cls.BROWSER_WIDTH, cls.BROWSER_HEIGHT) + + @classmethod + def tearDownClass(cls): + if not cls.GENERATING_MODE: + cls.browser.quit() + super(test_local_browser, cls).tearDownClass() + + def generate_image(self, html_file, png_file, page_must_load=True): + self.browser.get('file://' + html_file) + try: + WebDriverWait(self.browser, 5).until(expected_conditions.presence_of_element_located((By.ID, 'page-container'))) + except: + if page_must_load: + raise + self.browser.save_screenshot(png_file) diff --git a/test/test_naming.py b/test/test_naming.py deleted file mode 100644 index e1f6eff..0000000 --- a/test/test_naming.py +++ /dev/null @@ -1,262 +0,0 @@ -#!/usr/bin/env python - -import unittest -import os -import sys -import tempfile -import shutil -import subprocess - -# We assume that this file is put inside SRC_DIR/test -TEST_DIR = os.path.dirname(__file__) -# The location where our test PDFs are stored -TEST_DATA_DIR = os.path.join(TEST_DIR, 'test_data') -# The location where the base css file, etc is stored in the build folder -DATA_DIR = os.path.join(TEST_DIR, '../share') - -# The script should be run in the directory containing the binary -# The location where the executable is generated by the build -PDF2HTMLEX_PATH = './pdf2htmlEX' - - -def execute_pdf2htmlex_with_args(args): - """ - Execute the pdf2htmlEX with the specified arguments. - - :type args: list of values - :param args: list of arguments to pass to executable. First part of each tuple is the argument, second part is the value. - - :rtype: int - :return: The exit code of the command - """ - executable = os.path.abspath(PDF2HTMLEX_PATH) - - cmd = [executable, '--data-dir', os.path.abspath(DATA_DIR)] - - for val in args: - cmd.append(str(val)) - - return_code = subprocess.call(cmd) - - if return_code != 0: - print >> sys.stderr, "Command return code %d: %s" % (return_code, ' '.join(cmd)) - - return return_code - -def execute_pdf2htmlex_and_get_files(args): - """ - Execute the pdf2htmlEX with the specified arguments, and get the names of the output files. Will automatically create - a temporary directory for the output, pass that as the output dir to pdf2htmlEX, determine the files generated, and - clean up the temporary directory afterwards. - - :type args: list of values - :param args: list of arguments to pass to executable. First part of each tuple is the argument, second part is the value. - - :rtype: list of str - :return: List of the file names that were generated as output in alphabetical order. None if the command does not execute successfully. - """ - temp_dir = tempfile.mkdtemp() - - try: - if execute_pdf2htmlex_with_args(['--dest-dir', temp_dir] + args) != 0: - return None - - files = os.listdir(temp_dir) - files.sort() - return files - finally: - shutil.rmtree(path=temp_dir, ignore_errors=True) - -def path_to_test_file(filename): - """ - Retrieve an absolute path to the specified test file. - - :type filename: - :param filename: the name of the test file to get the path to - - :rtype: str - :returns: the full path to the test file - """ - return os.path.abspath(os.path.join(TEST_DATA_DIR, filename)) - -class OutputNamingTests(unittest.TestCase): - def test_generate_single_html_default_name_single_page_pdf(self): - files = execute_pdf2htmlex_and_get_files([ - path_to_test_file('1-page.pdf') - ]) - self.assertEquals(files, ['1-page.html']) - - def test_generate_single_html_default_name_multiple_page_pdf(self): - files = execute_pdf2htmlex_and_get_files([ - path_to_test_file('2-pages.pdf') - ]) - self.assertEquals(files, ['2-pages.html']) - - def test_generate_single_html_specify_name_single_page_pdf(self): - files = execute_pdf2htmlex_and_get_files([ - path_to_test_file('1-page.pdf'), - 'foo.html' - ]) - self.assertEquals(files, ['foo.html']) - - def test_generate_single_html_specify_name_multiple_page_pdf(self): - files = execute_pdf2htmlex_and_get_files([ - path_to_test_file('2-pages.pdf'), - 'foo.html' - ]) - self.assertEquals(files, ['foo.html']) - - def test_generate_split_pages_default_name_single_page(self): - files = execute_pdf2htmlex_and_get_files([ - '--split-pages', 1, - path_to_test_file('1-page.pdf') - ]) - self.assertEquals(files, sorted(['1-page.html', '1-page1.page'])) - - def test_generate_split_pages_default_name_multiple_pages(self): - files = execute_pdf2htmlex_and_get_files([ - '--split-pages', 1, - path_to_test_file('3-pages.pdf') - ]) - self.assertEquals(files, sorted(['3-pages.html', '3-pages1.page', '3-pages2.page', '3-pages3.page'])) - - def test_generate_split_pages_specify_name_single_page(self): - files = execute_pdf2htmlex_and_get_files([ - '--split-pages', 1, - '--page-filename', 'foo.xyz', - path_to_test_file('1-page.pdf'), - ]) - self.assertEquals(files, sorted(['1-page.html', 'foo1.xyz'])) - - def test_generate_split_pages_specify_name_multiple_pages(self): - files = execute_pdf2htmlex_and_get_files([ - '--split-pages', 1, - '--page-filename', 'foo.xyz', - path_to_test_file('3-pages.pdf'), - ]) - self.assertEquals(files, sorted(['3-pages.html', 'foo1.xyz', 'foo2.xyz', 'foo3.xyz'])) - - def test_generate_split_pages_specify_name_formatter_multiple_pages(self): - files = execute_pdf2htmlex_and_get_files([ - '--split-pages', 1, - '--page-filename', 'fo%do.xyz', - path_to_test_file('3-pages.pdf'), - ]) - self.assertEquals(files, sorted(['3-pages.html', 'fo1o.xyz', 'fo2o.xyz', 'fo3o.xyz'])) - - def test_generate_split_pages_specify_name_formatter_with_padded_zeros_multiple_pages(self): - files = execute_pdf2htmlex_and_get_files([ - '--split-pages', 1, - '--page-filename', 'fo%03do.xyz', - path_to_test_file('3-pages.pdf') - ]) - self.assertEquals(files, sorted(['3-pages.html', 'fo001o.xyz', 'fo002o.xyz', 'fo003o.xyz'])) - - def test_generate_split_pages_specify_name_only_first_formatter_gets_taken(self): - files = execute_pdf2htmlex_and_get_files([ - '--split-pages', 1, - '--page-filename', 'f%do%do.xyz', - path_to_test_file('3-pages.pdf') - ]) - self.assertEquals(files, sorted(['3-pages.html', 'f1o%do.xyz', 'f2o%do.xyz', 'f3o%do.xyz'])) - - def test_generate_split_pages_specify_name_only_percent_d_is_used_percent_s(self): - files = execute_pdf2htmlex_and_get_files([ - '--split-pages', 1, - '--page-filename', 'f%soo.xyz', - path_to_test_file('3-pages.pdf') - ]) - self.assertEquals(files, sorted(['3-pages.html', 'f%soo1.xyz', 'f%soo2.xyz', 'f%soo3.xyz'])) - - def test_generate_split_pages_specify_name_only_percent_d_is_used_percent_p(self): - files = execute_pdf2htmlex_and_get_files([ - '--split-pages', 1, - '--page-filename', 'f%poo.xyz', - path_to_test_file('3-pages.pdf'), - ]) - self.assertEquals(files, sorted(['3-pages.html', 'f%poo1.xyz', 'f%poo2.xyz', 'f%poo3.xyz'])) - - - def test_generate_split_pages_specify_name_only_percent_d_is_used_percent_n(self): - files = execute_pdf2htmlex_and_get_files([ - '--split-pages', 1, - '--page-filename', 'f%noo.xyz', - path_to_test_file('3-pages.pdf') - ]) - self.assertEquals(files, sorted(['3-pages.html', 'f%noo1.xyz', 'f%noo2.xyz', 'f%noo3.xyz'])) - - def test_generate_split_pages_specify_name_only_percent_d_is_used_percent_percent(self): - files = execute_pdf2htmlex_and_get_files([ - '--split-pages', 1, - '--page-filename', 'f%%oo.xyz', - path_to_test_file('3-pages.pdf') - ]) - self.assertEquals(files, sorted(['3-pages.html', 'f%%oo1.xyz', 'f%%oo2.xyz', 'f%%oo3.xyz'])) - - def test_generate_split_pages_specify_name_only_percent_d_is_used_percent_percent_with_actual_placeholder(self): - files = execute_pdf2htmlex_and_get_files([ - '--split-pages', 1, - '--page-filename', 'f%%o%do.xyz', - path_to_test_file('3-pages.pdf') - ]) - self.assertEquals(files, sorted(['3-pages.html', 'f%%o1o.xyz', 'f%%o2o.xyz', 'f%%o3o.xyz'])) - - def test_generate_split_pages_specify_name_only_percent_d_is_used_percent_percent_with_actual_placeholder(self): - files = execute_pdf2htmlex_and_get_files([ - '--split-pages', 1, - '--page-filename', 'fo%do%%.xyz', - path_to_test_file('3-pages.pdf') - ]) - self.assertEquals(files, sorted(['3-pages.html', 'fo1o%%.xyz', 'fo2o%%.xyz', 'fo3o%%.xyz'])) - - def test_generate_split_pages_specify_name_only_formatter_starts_part_way_through_invalid_formatter(self): - files = execute_pdf2htmlex_and_get_files([ - '--split-pages', 1, - '--page-filename', 'f%02%doo.xyz', - path_to_test_file('3-pages.pdf'), - ]) - self.assertEquals(files, sorted(['3-pages.html', 'f%021oo.xyz', 'f%022oo.xyz', 'f%023oo.xyz'])) - - def test_generate_split_pages_specify_output_filename_no_formatter_no_extension(self): - files = execute_pdf2htmlex_and_get_files([ - '--split-pages', 1, - '--page-filename', 'foo', - path_to_test_file('1-page.pdf'), - ]) - self.assertEquals(files, sorted(['1-page.html', 'foo1'])) - - def test_generate_single_html_name_specified_format_characters_percent_d(self): - files = execute_pdf2htmlex_and_get_files([ - path_to_test_file('2-pages.pdf'), - 'foo%d.html' - ]) - self.assertEquals(files, ['foo%d.html']) - - def test_generate_single_html_name_specified_format_characters_percent_p(self): - files = execute_pdf2htmlex_and_get_files([ - path_to_test_file('2-pages.pdf'), - 'foo%p.html' - ]) - self.assertEquals(files, ['foo%p.html']) - - def test_generate_single_html_name_specified_format_characters_percent_n(self): - files = execute_pdf2htmlex_and_get_files([ - path_to_test_file('2-pages.pdf'), - 'foo%n.html' - ]) - self.assertEquals(files, ['foo%n.html']) - - def test_generate_single_html_name_specified_format_characters_percent_percent(self): - files = execute_pdf2htmlex_and_get_files([ - path_to_test_file('2-pages.pdf'), - 'foo%%.html' - ]) - self.assertEquals(files, ['foo%%.html']) - -if __name__=="__main__": - executable = os.path.abspath(PDF2HTMLEX_PATH) - if not os.path.isfile(executable) or not os.access(executable, os.X_OK): - print >> sys.stderr, "Cannot locate pdf2htmlEX executable. Make sure source was built before running this test." - exit(1) - - unittest.main() diff --git a/test/test_output.py b/test/test_output.py new file mode 100644 index 0000000..e7e30a3 --- /dev/null +++ b/test/test_output.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python + +# Test output files + +import unittest +import os + +from test import Common + +@unittest.skipIf(Common.GENERATING_MODE, 'Skipping test_output in generating mode') +class test_output(Common, unittest.TestCase): + def run_test_case(self, input_file, expected_output_files, args=[]): + args = list(args) + args.insert(0, os.path.join(self.TEST_DIR, 'test_output', input_file)) + self.assertItemsEqual(self.run_pdf2htmlEX(args)['output_files'], expected_output_files) + + def test_generate_single_html_default_name_single_page_pdf(self): + self.run_test_case('1-page.pdf', ['1-page.html']) + + def test_generate_single_html_default_name_multiple_page_pdf(self): + self.run_test_case('2-pages.pdf', ['2-pages.html']) + + def test_generate_single_html_specify_name_single_page_pdf(self): + self.run_test_case('1-page.pdf', ['foo.html'], ['foo.html']) + + def test_generate_single_html_specify_name_multiple_page_pdf(self): + self.run_test_case('2-pages.pdf', ['foo.html'], ['foo.html']) + + def test_generate_split_pages_default_name_single_page(self): + self.run_test_case('1-page.pdf', ['1-page.html', '1-page1.page'], ['--split-pages', 1]) + + def test_generate_split_pages_default_name_multiple_pages(self): + self.run_test_case('3-pages.pdf', ['3-pages.html', '3-pages1.page', '3-pages2.page', '3-pages3.page'], ['--split-pages', 1]) + + def test_generate_split_pages_specify_name_single_page(self): + self.run_test_case('1-page.pdf', ['1-page.html', 'foo1.xyz'], ['--split-pages', 1, '--page-filename', 'foo.xyz']) + + def test_generate_split_pages_specify_name_multiple_pages(self): + self.run_test_case('3-pages.pdf', ['3-pages.html', 'foo1.xyz', 'foo2.xyz', 'foo3.xyz'], ['--split-pages', 1, '--page-filename', 'foo.xyz']) + + def test_generate_split_pages_specify_name_formatter_multiple_pages(self): + self.run_test_case('3-pages.pdf', ['3-pages.html', 'fo1o.xyz', 'fo2o.xyz', 'fo3o.xyz'], ['--split-pages', 1, '--page-filename', 'fo%do.xyz']) + + def test_generate_split_pages_specify_name_formatter_with_padded_zeros_multiple_pages(self): + self.run_test_case('3-pages.pdf', ['3-pages.html', 'fo001o.xyz', 'fo002o.xyz', 'fo003o.xyz'], ['--split-pages', 1, '--page-filename', 'fo%03do.xyz']) + + def test_generate_split_pages_specify_name_only_first_formatter_gets_taken(self): + self.run_test_case('3-pages.pdf', ['3-pages.html', 'f1o%do.xyz', 'f2o%do.xyz', 'f3o%do.xyz'], ['--split-pages', 1, '--page-filename', 'f%do%do.xyz']) + + def test_generate_split_pages_specify_name_only_percent_d_is_used_percent_s(self): + self.run_test_case('3-pages.pdf', ['3-pages.html', 'f%soo1.xyz', 'f%soo2.xyz', 'f%soo3.xyz'], ['--split-pages', 1, '--page-filename', 'f%soo.xyz']) + + def test_generate_split_pages_specify_name_only_percent_d_is_used_percent_p(self): + self.run_test_case('3-pages.pdf', ['3-pages.html', 'f%poo1.xyz', 'f%poo2.xyz', 'f%poo3.xyz'], ['--split-pages', 1, '--page-filename', 'f%poo.xyz']) + + + def test_generate_split_pages_specify_name_only_percent_d_is_used_percent_n(self): + self.run_test_case('3-pages.pdf', ['3-pages.html', 'f%noo1.xyz', 'f%noo2.xyz', 'f%noo3.xyz'], ['--split-pages', 1, '--page-filename', 'f%noo.xyz']) + + def test_generate_split_pages_specify_name_only_percent_d_is_used_percent_percent(self): + self.run_test_case('3-pages.pdf', ['3-pages.html', 'f%%oo1.xyz', 'f%%oo2.xyz', 'f%%oo3.xyz'], ['--split-pages', 1, '--page-filename', 'f%%oo.xyz']) + + def test_generate_split_pages_specify_name_only_percent_d_is_used_percent_percent_with_actual_placeholder(self): + self.run_test_case('3-pages.pdf', ['3-pages.html', 'f%%o1o.xyz', 'f%%o2o.xyz', 'f%%o3o.xyz'], ['--split-pages', 1, '--page-filename', 'f%%o%do.xyz']) + + def test_generate_split_pages_specify_name_only_percent_d_is_used_percent_percent_with_actual_placeholder(self): + self.run_test_case('3-pages.pdf', ['3-pages.html', 'fo1o%%.xyz', 'fo2o%%.xyz', 'fo3o%%.xyz'], ['--split-pages', 1, '--page-filename', 'fo%do%%.xyz']) + + def test_generate_split_pages_specify_name_only_formatter_starts_part_way_through_invalid_formatter(self): + self.run_test_case('3-pages.pdf', ['3-pages.html', 'f%021oo.xyz', 'f%022oo.xyz', 'f%023oo.xyz'], ['--split-pages', 1, '--page-filename', 'f%02%doo.xyz']) + + def test_generate_split_pages_specify_output_filename_no_formatter_no_extension(self): + self.run_test_case('1-page.pdf', ['1-page.html', 'foo1'], ['--split-pages', 1, '--page-filename', 'foo']) + + def test_generate_single_html_name_specified_format_characters_percent_d(self): + self.run_test_case('2-pages.pdf', ['foo%d.html'], ['foo%d.html']) + + def test_generate_single_html_name_specified_format_characters_percent_p(self): + self.run_test_case('2-pages.pdf', ['foo%p.html'], ['foo%p.html']) + + def test_generate_single_html_name_specified_format_characters_percent_n(self): + self.run_test_case('2-pages.pdf', ['foo%n.html'], ['foo%n.html']) + + def test_generate_single_html_name_specified_format_characters_percent_percent(self): + self.run_test_case('2-pages.pdf', ['foo%%.html'], ['foo%%.html']) + diff --git a/test/test_data/1-page.pdf b/test/test_output/1-page.pdf similarity index 100% rename from test/test_data/1-page.pdf rename to test/test_output/1-page.pdf diff --git a/test/test_data/2-pages.pdf b/test/test_output/2-pages.pdf similarity index 100% rename from test/test_data/2-pages.pdf rename to test/test_output/2-pages.pdf diff --git a/test/test_data/3-pages.pdf b/test/test_output/3-pages.pdf similarity index 100% rename from test/test_data/3-pages.pdf rename to test/test_output/3-pages.pdf diff --git a/test/test_remote_browser.py b/test/test_remote_browser.py new file mode 100755 index 0000000..0bc7c40 --- /dev/null +++ b/test/test_remote_browser.py @@ -0,0 +1,138 @@ +#!/usr/bin/env python + +# Run browser tests through Sauce Labs + +import unittest +import sys +import os + +from selenium import webdriver +from selenium.webdriver.common.by import By +from selenium.webdriver.support.ui import WebDriverWait +from selenium.webdriver.support import expected_conditions +from sauceclient import SauceClient +from browser_tests import BrowserTests + +# Set your own environment variables +USERNAME = os.environ.get('SAUCE_USERNAME') +ACCESS_KEY = os.environ.get('SAUCE_ACCESS_KEY') + +# The base url that remote browser will access +# Usually a HTTP server should be set up in the folder containing the test cases +# Also Sauce Connect should be enabled +BASEURL='http://localhost:8000/' + +SAUCE_OPTIONS = { + 'record-video': 'false', +} + +# we want to test the latest stable version +# and 'beta' is usually the best estimation +BROWSER_MATRIX = [ + ('win_ie', { + 'platform': 'Windows 8.1', + 'browserName': 'internet explorer', + 'version': '11', + }), + ('win_firefox', { + 'platform': 'Windows 8.1', + 'browserName': 'firefox', + 'version': 'beta', + }), + ('win_chrome', { + 'platform': 'Windows 8.1', + 'browserName': 'chrome', + 'version': 'beta', + }), + ('mac_firefox', { + 'platform': 'OS X 10.9', + 'browserName': 'firefox', + 'version': 'beta', + }), + ('mac_chrome', { + 'platform': 'OS X 10.9', + 'browserName': 'chrome', + 'version': '40.0', # beta is not supported + }), + ('linux_firefox', { + 'platform': 'Linux', + 'browserName': 'firefox', + 'version': 'beta', + }), + ('linux_chrome', { + 'platform': 'Linux', + 'browserName': 'chrome', + 'version': 'beta', + }), +] + +@unittest.skipIf((not (USERNAME and ACCESS_KEY)), 'Sauce Labs is not available') +class test_remote_browser_base(BrowserTests): + @classmethod + def setUpClass(cls): + super(test_remote_browser_base, cls).setUpClass() + if not cls.GENERATING_MODE: + cls.sauce = SauceClient(USERNAME, ACCESS_KEY) + cls.sauce_url = 'http://%s:%s@ondemand.saucelabs.com:80/wd/hub' % (USERNAME, ACCESS_KEY) + + cls.browser = webdriver.Remote( + desired_capabilities=cls.desired_capabilities, + command_executor=cls.sauce_url + ) + + cls.browser.implicitly_wait(30) + # remote screen may not be large enough for the whole page + cls.browser.set_window_size(cls.BROWSER_WIDTH, cls.BROWSER_HEIGHT) + + @classmethod + def tearDownClass(cls): + if not cls.GENERATING_MODE: + cls.browser.quit() + super(test_remote_browser_base, cls).tearDownClass() + + def setUp(self): + super(test_remote_browser_base, self).setUp() + sys.exc_clear() + + def tearDown(self): + try: + passed = (sys.exc_info() == (None, None, None)) + branch = os.environ.get('TRAVIS_BRANCH', 'manual') + pull_request = os.environ.get('TRAVIS_PULL_REQUEST', 'false') + self.sauce.jobs.update_job(self.browser.session_id, + build_num=os.environ.get('TRAVIS_BUILD_NUMBER', '0'), + name='pdf2htmlEX', + passed=passed, + public='public restricted', + tags = [pull_request] if pull_request != 'false' else [branch] + ) + except: + raise + pass + + def generate_image(self, html_file, png_file, page_must_load=True): + self.browser.get(BASEURL + html_file) + try: + WebDriverWait(self.browser, 5).until(expected_conditions.presence_of_element_located((By.ID, 'page-container'))) + except: + if page_must_load: + raise + self.browser.save_screenshot(png_file) + +test_classnames = [] + +def generate_classes(): + module = globals() + for browser_name, browser_caps in BROWSER_MATRIX: + d = dict(test_remote_browser_base.__dict__) + caps = SAUCE_OPTIONS.copy() + caps.update(browser_caps) + tunnel_identifier = os.environ.get('TRAVIS_JOB_NUMBER') + if tunnel_identifier: + caps['tunnel-identifier'] = tunnel_identifier + d['desired_capabilities'] = caps + name = "test_remote_%s" % (browser_name, ) + module[name] = type(name, (test_remote_browser_base, unittest.TestCase), d) + test_classnames.append(name) + +generate_classes()