mirror of
https://github.com/pdf2htmlEX/pdf2htmlEX.git
synced 2024-12-21 12:40:08 +00:00
Merge branch 'incoming'
This commit is contained in:
commit
2edf41eaa1
14
.gitignore
vendored
14
.gitignore
vendored
@ -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
|
||||
|
25
.travis.yml
25
.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=
|
||||
|
803
3rdparty/poppler/0.22.5/CairoFontEngine.cc
vendored
803
3rdparty/poppler/0.22.5/CairoFontEngine.cc
vendored
@ -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 <jeff@infidigm.net>
|
||||
// Copyright (C) 2005, 2006 Kristian Høgsberg <krh@redhat.com>
|
||||
// Copyright (C) 2005 Martin Kretzschmar <martink@gnome.org>
|
||||
// Copyright (C) 2005, 2009, 2012 Albert Astals Cid <aacid@kde.org>
|
||||
// Copyright (C) 2006, 2007, 2010, 2011 Carlos Garcia Campos <carlosgc@gnome.org>
|
||||
// Copyright (C) 2007 Koji Otani <sho@bbr.jp>
|
||||
// Copyright (C) 2008, 2009 Chris Wilson <chris@chris-wilson.co.uk>
|
||||
// Copyright (C) 2008, 2012 Adrian Johnson <ajohnson@redneon.com>
|
||||
// Copyright (C) 2009 Darren Kenny <darren.kenny@sun.com>
|
||||
// Copyright (C) 2010 Suzuki Toshiya <mpsuzuki@hiroshima-u.ac.jp>
|
||||
// Copyright (C) 2010 Jan Kümmel <jan+freedesktop@snorc.org>
|
||||
// Copyright (C) 2012 Hib Eris <hib@hiberis.nl>
|
||||
//
|
||||
// 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 <poppler-config.h>
|
||||
|
||||
#include <string.h>
|
||||
#include "CairoFontEngine.h"
|
||||
#include "CairoOutputDev.h"
|
||||
#include "GlobalParams.h"
|
||||
#include <fofi/FoFiTrueType.h>
|
||||
#include <fofi/FoFiType1C.h>
|
||||
#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 <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mman.h>
|
||||
#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;
|
||||
}
|
124
3rdparty/poppler/0.22.5/CairoFontEngine.h
vendored
124
3rdparty/poppler/0.22.5/CairoFontEngine.h
vendored
@ -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 <krh@redhat.com>
|
||||
// Copyright (C) 2005 Albert Astals Cid <aacid@kde.org>
|
||||
// Copyright (C) 2006, 2007 Jeff Muizelaar <jeff@infidigm.net>
|
||||
// Copyright (C) 2006, 2010 Carlos Garcia Campos <carlosgc@gnome.org>
|
||||
// Copyright (C) 2008 Adrian Johnson <ajohnson@redneon.com>
|
||||
//
|
||||
// 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 <cairo-ft.h>
|
||||
|
||||
#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
|
3200
3rdparty/poppler/0.22.5/CairoOutputDev.cc
vendored
3200
3rdparty/poppler/0.22.5/CairoOutputDev.cc
vendored
File diff suppressed because it is too large
Load Diff
503
3rdparty/poppler/0.22.5/CairoOutputDev.h
vendored
503
3rdparty/poppler/0.22.5/CairoOutputDev.h
vendored
@ -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 <jeff@infidigm.net>
|
||||
// Copyright (C) 2005, 2006 Kristian Høgsberg <krh@redhat.com>
|
||||
// Copyright (C) 2005 Nickolay V. Shmyrev <nshmyrev@yandex.ru>
|
||||
// Copyright (C) 2006-2011 Carlos Garcia Campos <carlosgc@gnome.org>
|
||||
// Copyright (C) 2008, 2009, 2011, 2012 Adrian Johnson <ajohnson@redneon.com>
|
||||
// Copyright (C) 2008 Michael Vrable <mvrable@cs.ucsd.edu>
|
||||
// Copyright (C) 2010-2012 Thomas Freitag <Thomas.Freitag@alfa.de>
|
||||
//
|
||||
// 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 <cairo-ft.h>
|
||||
#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
|
377
3rdparty/poppler/0.22.5/CairoRescaleBox.cc
vendored
377
3rdparty/poppler/0.22.5/CairoRescaleBox.cc
vendored
@ -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 <hib@hiberis.nl>
|
||||
// Copyright (C) 2012 Adrian Johnson <ajohnson@redneon.com>
|
||||
//
|
||||
// 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 <config.h>
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#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<dest_length; i++)
|
||||
{
|
||||
float left_side = i*scale;
|
||||
float right_side = (i+1)*scale;
|
||||
float right_fract = right_side - floor (right_side);
|
||||
float left_fract = ceil (left_side) - left_side;
|
||||
int overage;
|
||||
/* find out how many source pixels will be used to fill the box */
|
||||
int count = floor (right_side) - ceil (left_side);
|
||||
/* what's the maximum value this expression can become?
|
||||
floor((i+1)*scale) - ceil(i*scale)
|
||||
|
||||
(i+1)*scale - i*scale == scale
|
||||
|
||||
since floor((i+1)*scale) <= (i+1)*scale
|
||||
and ceil(i*scale) >= 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;
|
||||
}
|
61
3rdparty/poppler/0.22.5/CairoRescaleBox.h
vendored
61
3rdparty/poppler/0.22.5/CairoRescaleBox.h
vendored
@ -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 <ajohnson@redneon.com>
|
||||
//
|
||||
// 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 <cairo.h>
|
||||
|
||||
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 */
|
1
AUTHORS
1
AUTHORS
@ -18,6 +18,7 @@ Marc Sanfacon <marc.sanfacon@gmail.com>
|
||||
Michele Redolfi <michele@tecnicaict.com>
|
||||
Mick Giles <mick@mickgiles.com>
|
||||
Ryan Morlok <ryan.morlok@morlok.com>
|
||||
Simon Chenard <chenard.simon@gmail.com>
|
||||
Wanmin Liu <wanminliu@gmail.com>
|
||||
|
||||
Packagers:
|
||||
|
@ -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()
|
||||
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 <vector>
|
||||
int main()
|
||||
{
|
||||
char * ptr = nullptr;
|
||||
std::vector<int> 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)
|
||||
|
@ -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
|
||||
|
||||
|
@ -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@gmail.com>
|
||||
* [@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)
|
||||
|
3
TODO
3
TODO
@ -1,3 +1,5 @@
|
||||
Redesign textline
|
||||
|
||||
save width in Textline, create new <div> 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
|
||||
|
4
debian/control
vendored
4
debian/control
vendored
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -157,17 +157,21 @@
|
||||
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@ span { /* text blocks within a line */
|
||||
position:relative;
|
||||
/* _<id> for spaces may need display:inline, which will override this */
|
||||
.@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;
|
||||
unicode-bidi:bidi-override; /* For rtl languages, e.g. Hebrew, we don't want the default Unicode behaviour */
|
||||
}
|
||||
.@CSS_WHITESPACE_CN@ { /* text shift */
|
||||
/* 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;
|
||||
}
|
||||
@ -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 */
|
||||
|
@ -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 */
|
||||
|
@ -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
|
||||
"""
|
||||
<script>
|
||||
@ -56,6 +62,8 @@ try{
|
||||
}catch(e){}
|
||||
</script>
|
||||
"""
|
||||
#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.
|
||||
# <div id="sidebar" class="opened">
|
||||
|
||||
#TEST_IGNORE_BEGIN
|
||||
"""
|
||||
<div id="sidebar">
|
||||
"""
|
||||
@ -83,6 +93,7 @@ $outline
|
||||
</div>
|
||||
</div>
|
||||
"""
|
||||
#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
|
||||
"""
|
||||
<div class="loading-indicator">
|
||||
"""
|
||||
@ -107,6 +120,7 @@ $pages
|
||||
"""
|
||||
</div>
|
||||
"""
|
||||
#TEST_IGNORE_END
|
||||
|
||||
#############
|
||||
# Do not modify
|
||||
|
@ -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();
|
||||
},
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18,34 +18,34 @@
|
||||
|
||||
namespace pdf2htmlEX {
|
||||
|
||||
BackgroundRenderer * BackgroundRenderer::getBackgroundRenderer(const std::string & format, HTMLRenderer * html_renderer, const Param & param)
|
||||
std::unique_ptr<BackgroundRenderer> 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<BackgroundRenderer>(new SplashBackgroundRenderer(format, html_renderer, param));
|
||||
}
|
||||
#endif
|
||||
#ifdef ENABLE_LIBJPEG
|
||||
if(format == "jpg")
|
||||
{
|
||||
return new SplashBackgroundRenderer(format, html_renderer, param);
|
||||
return std::unique_ptr<BackgroundRenderer>(new SplashBackgroundRenderer(format, html_renderer, param));
|
||||
}
|
||||
#endif
|
||||
#if ENABLE_SVG
|
||||
if (format == "svg")
|
||||
{
|
||||
return new CairoBackgroundRenderer(html_renderer, param);
|
||||
return std::unique_ptr<BackgroundRenderer>(new CairoBackgroundRenderer(html_renderer, param));
|
||||
}
|
||||
#endif
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BackgroundRenderer * BackgroundRenderer::getFallbackBackgroundRenderer(HTMLRenderer * html_renderer, const Param & param)
|
||||
std::unique_ptr<BackgroundRenderer> 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<BackgroundRenderer>(new SplashBackgroundRenderer("", html_renderer, param));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -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<BackgroundRenderer> 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<BackgroundRenderer> getFallbackBackgroundRenderer(HTMLRenderer * html_renderer, const Param & param);
|
||||
|
||||
BackgroundRenderer() {}
|
||||
virtual ~BackgroundRenderer() {}
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include <unordered_map>
|
||||
#include <cstdint>
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
|
||||
#include <OutputDev.h>
|
||||
#include <GfxState.h>
|
||||
@ -20,6 +21,10 @@
|
||||
#include <GfxFont.h>
|
||||
#include <Annot.h>
|
||||
|
||||
// for form.cc
|
||||
#include <Page.h>
|
||||
#include <Form.h>
|
||||
|
||||
#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<int32_t> cur_mapping;
|
||||
std::vector<char*> cur_mapping2;
|
||||
std::vector<int> 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<BackgroundRenderer> 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;
|
||||
};
|
||||
|
||||
|
@ -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<string> 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<int>(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;";
|
||||
|
76
src/HTMLRenderer/form.cc
Normal file
76
src/HTMLRenderer/form.cc
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* form.cc
|
||||
*
|
||||
* Handling Forms
|
||||
*
|
||||
* by Simon Chenard
|
||||
* 2014.07.25
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#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 << "<input id=\"text-" << pageNum << "-" << i
|
||||
<< "\" class=\"" << CSS::INPUT_TEXT_CN
|
||||
<< "\" type=\"text\" value=\"\""
|
||||
<< " style=\"position: absolute; left: " << x1
|
||||
<< "px; bottom: " << y1 << "px;"
|
||||
<< " width: " << width << "px; height: " << std::to_string(height)
|
||||
<< "px; line-height: " << std::to_string(height) << "px; font-size: "
|
||||
<< font_size << "px;\" />" << endl;
|
||||
}
|
||||
else if(w->getType() == formButton)
|
||||
{
|
||||
//Ideally would check w->getButtonType()
|
||||
//for more specific rendering
|
||||
width += 3;
|
||||
height += 3;
|
||||
|
||||
out << "<div id=\"cb-" << pageNum << "-" << i
|
||||
<< "\" class=\"" << CSS::INPUT_RADIO_CN
|
||||
<< "\" style=\"position: absolute; left: " << x1
|
||||
<< "px; bottom: " << y1 << "px;"
|
||||
<< " width: " << width << "px; height: "
|
||||
<< std::to_string(height) << "px; background-size: cover;\" ></div>" << endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
cerr << "Unsupported form field detected" << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -3,7 +3,7 @@
|
||||
*
|
||||
* Handling general stuffs
|
||||
*
|
||||
* Copyright (C) 2012,2013 Lu Wang <coolwanglu@gmail.com>
|
||||
* Copyright (C) 2012,2013,2014 Lu Wang <coolwanglu@gmail.com>
|
||||
*/
|
||||
|
||||
#include <cstdio>
|
||||
@ -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;
|
||||
}
|
||||
|
||||
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,6 +252,7 @@ void HTMLRenderer::endPage() {
|
||||
// dump info for js
|
||||
// TODO: create a function for this
|
||||
// BE CAREFUL WITH ESCAPES
|
||||
{
|
||||
(*f_curpage) << "<div class=\"" << CSS::PAGE_DATA_CN << "\" data-data='{";
|
||||
|
||||
//default CTM
|
||||
@ -273,6 +265,7 @@ void HTMLRenderer::endPage() {
|
||||
(*f_curpage) << "]";
|
||||
|
||||
(*f_curpage) << "}'></div>";
|
||||
}
|
||||
|
||||
// close page
|
||||
(*f_curpage) << "</div>" << 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())
|
||||
{
|
||||
|
@ -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: "
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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<HTMLTextLine*> 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();
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -11,8 +11,6 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#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 {
|
||||
|
@ -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)
|
||||
|
@ -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@";
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 '&':
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
2
test/.gitattributes
vendored
Normal file
2
test/.gitattributes
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
*.pdf binary
|
||||
*.woff binary
|
38
test/README.md
Normal file
38
test/README.md
Normal file
@ -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`
|
||||
|
99
test/browser_tests.py
Normal file
99
test/browser_tests.py
Normal file
@ -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])
|
||||
|
BIN
test/browser_tests/basic_text.pdf
Normal file
BIN
test/browser_tests/basic_text.pdf
Normal file
Binary file not shown.
36
test/browser_tests/basic_text.tex
Normal file
36
test/browser_tests/basic_text.tex
Normal file
@ -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}
|
107
test/browser_tests/basic_text/basic_text.html
Normal file
107
test/browser_tests/basic_text/basic_text.html
Normal file
@ -0,0 +1,107 @@
|
||||
<!DOCTYPE html>
|
||||
<!-- Created by pdf2htmlEX (https://github.com/coolwanglu/pdf2htmlex) -->
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="generator" content="pdf2htmlEX"/>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
|
||||
<style type="text/css">
|
||||
/*!
|
||||
* Base CSS for pdf2htmlEX
|
||||
* Copyright 2012,2013 Lu Wang <coolwanglu@gmail.com>
|
||||
* https://github.com/coolwanglu/pdf2htmlEX/blob/master/share/LICENSE
|
||||
*/#sidebar{position:absolute;top:0;left:0;bottom:0;width:250px;padding:0;margin:0;overflow:auto}#page-container{position:absolute;top:0;left:0;margin:0;padding:0;border:0}@media screen{#sidebar.opened+#page-container{left:250px}#page-container{bottom:0;right:0;overflow:auto}.loading-indicator{display:none}.loading-indicator.active{display:block;position:absolute;width:64px;height:64px;top:50%;left:50%;margin-top:-32px;margin-left:-32px}.loading-indicator img{position:absolute;top:0;left:0;bottom:0;right:0}}@media print{@page{margin:0}html{margin:0}body{margin:0;-webkit-print-color-adjust:exact}#sidebar{display:none}#page-container{width:auto;height:auto;overflow:visible;background-color:transparent}.d{display:none}}.pf{position:relative;background-color:white;overflow:hidden;margin:0;border:0}.pc{position:absolute;border:0;padding:0;margin:0;top:0;left:0;width:100%;height:100%;overflow:hidden;display:block;transform-origin:0 0;-ms-transform-origin:0 0;-webkit-transform-origin:0 0}.pc.opened{display:block}.bf{position:absolute;border:0;margin:0;top:0;bottom:0;width:100%;height:100%;-ms-user-select:none;-moz-user-select:none;-webkit-user-select:none;user-select:none}.bi{position:absolute;border:0;margin:0;-ms-user-select:none;-moz-user-select:none;-webkit-user-select:none;user-select:none}@media print{.pf{margin:0;box-shadow:none;page-break-after:always;page-break-inside:avoid}@-moz-document url-prefix(){.pf{overflow:visible;border:1px solid #fff}.pc{overflow:visible}}}.c{position:absolute;border:0;padding:0;margin:0;overflow:hidden;display:block}.t{position:absolute;white-space:pre;font-size:1px;transform-origin:0 100%;-ms-transform-origin:0 100%;-webkit-transform-origin:0 100%;unicode-bidi:bidi-override;-moz-font-feature-settings:"liga" 0}.t:after{content:''}.t:before{content:'';display:inline-block}.t span{position:relative;unicode-bidi:bidi-override}._{display:inline-block;color:transparent;z-index:-1}::selection{background:rgba(127,255,255,0.4)}::-moz-selection{background:rgba(127,255,255,0.4)}.pi{display:none}.d{position:absolute;transform-origin:0 100%;-ms-transform-origin:0 100%;-webkit-transform-origin:0 100%}.it{border:0;background-color:rgba(255,255,255,0.0)}.ir:hover{cursor:pointer}</style>
|
||||
<style type="text/css">
|
||||
/* CSS for test cases */
|
||||
#page-container {
|
||||
overflow:hidden;
|
||||
}
|
||||
</style>
|
||||
<style type="text/css">
|
||||
.ff0{font-family:sans-serif;visibility:hidden;}
|
||||
@font-face{font-family:ff1;src:url(f1.woff)format("woff");}.ff1{font-family:ff1;line-height:0.899000;font-style:normal;font-weight:normal;visibility:visible;}
|
||||
@font-face{font-family:ff2;src:url(f2.woff)format("woff");}.ff2{font-family:ff2;line-height:0.882000;font-style:normal;font-weight:normal;visibility:visible;}
|
||||
.m2{transform:matrix(0.231081,-0.231081,0.231081,0.231081,0,0);-ms-transform:matrix(0.231081,-0.231081,0.231081,0.231081,0,0);-webkit-transform:matrix(0.231081,-0.231081,0.231081,0.231081,0,0);}
|
||||
.m0{transform:matrix(0.326797,0.000000,0.000000,0.326797,0,0);-ms-transform:matrix(0.326797,0.000000,0.000000,0.326797,0,0);-webkit-transform:matrix(0.326797,0.000000,0.000000,0.326797,0,0);}
|
||||
.m3{transform:matrix(0.462161,-0.462161,0.231081,0.231081,0,0);-ms-transform:matrix(0.462161,-0.462161,0.231081,0.231081,0,0);-webkit-transform:matrix(0.462161,-0.462161,0.231081,0.231081,0,0);}
|
||||
.m1{transform:matrix(0.653595,0.000000,0.000000,0.326797,0,0);-ms-transform:matrix(0.653595,0.000000,0.000000,0.326797,0,0);-webkit-transform:matrix(0.653595,0.000000,0.000000,0.326797,0,0);}
|
||||
.v0{vertical-align:0.000000px;}
|
||||
.ls0{letter-spacing:0.000000px;}
|
||||
.ls1{letter-spacing:20.000000px;}
|
||||
.ls2{letter-spacing:20.081833px;}
|
||||
.sc_{text-shadow:none;}
|
||||
.sc0{text-shadow:-0.015em 0 transparent,0 0.015em transparent,0.015em 0 transparent,0 -0.015em transparent;}
|
||||
@media screen and (-webkit-min-device-pixel-ratio:0){
|
||||
.sc_{-webkit-text-stroke:0px transparent;}
|
||||
.sc0{-webkit-text-stroke:0.015em transparent;text-shadow:none;}
|
||||
}
|
||||
.ws0{word-spacing:0.000000px;}
|
||||
._1{margin-left:-3.997949px;}
|
||||
._2{margin-left:-1.115811px;}
|
||||
._0{width:39.850619px;}
|
||||
.fc0{color:rgb(0,0,0);}
|
||||
.fs1{font-size:19.925200px;}
|
||||
.fs0{font-size:39.850400px;}
|
||||
.fs2{font-size:40.013453px;}
|
||||
.y8{bottom:210.583007px;}
|
||||
.y7{bottom:628.221281px;}
|
||||
.y6{bottom:650.413190px;}
|
||||
.y4{bottom:672.604170px;}
|
||||
.y5{bottom:715.900248px;}
|
||||
.y3{bottom:796.620915px;}
|
||||
.y2{bottom:827.875817px;}
|
||||
.y0{bottom:859.130719px;}
|
||||
.y1{bottom:865.666667px;}
|
||||
.h1{height:28.094532px;}
|
||||
.h2{height:28.209484px;}
|
||||
.h0{height:1035.294118px;}
|
||||
.w0{width:800.000000px;}
|
||||
.x0{left:194.394771px;}
|
||||
.x2{left:199.825216px;}
|
||||
.x4{left:222.016196px;}
|
||||
.x3{left:233.840248px;}
|
||||
.x5{left:244.208105px;}
|
||||
.x1{left:312.996078px;}
|
||||
.x6{left:396.252288px;}
|
||||
@media print{
|
||||
.v0{vertical-align:0.000000pt;}
|
||||
.ls0{letter-spacing:0.000000pt;}
|
||||
.ls1{letter-spacing:20.400000pt;}
|
||||
.ls2{letter-spacing:20.483469pt;}
|
||||
.ws0{word-spacing:0.000000pt;}
|
||||
._1{margin-left:-4.077908pt;}
|
||||
._2{margin-left:-1.138127pt;}
|
||||
._0{width:40.647631pt;}
|
||||
.fs1{font-size:20.323704pt;}
|
||||
.fs0{font-size:40.647408pt;}
|
||||
.fs2{font-size:40.813722pt;}
|
||||
.y8{bottom:214.794667pt;}
|
||||
.y7{bottom:640.785707pt;}
|
||||
.y6{bottom:663.421453pt;}
|
||||
.y4{bottom:686.056253pt;}
|
||||
.y5{bottom:730.218253pt;}
|
||||
.y3{bottom:812.553333pt;}
|
||||
.y2{bottom:844.433333pt;}
|
||||
.y0{bottom:876.313333pt;}
|
||||
.y1{bottom:882.980000pt;}
|
||||
.h1{height:28.656423pt;}
|
||||
.h2{height:28.773674pt;}
|
||||
.h0{height:1056.000000pt;}
|
||||
.w0{width:816.000000pt;}
|
||||
.x0{left:198.282667pt;}
|
||||
.x2{left:203.821720pt;}
|
||||
.x4{left:226.456520pt;}
|
||||
.x3{left:238.517053pt;}
|
||||
.x5{left:249.092267pt;}
|
||||
.x1{left:319.256000pt;}
|
||||
.x6{left:404.177333pt;}
|
||||
}
|
||||
</style>
|
||||
<title></title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="page-container">
|
||||
<div id="pf1" class="pf w0 h0" data-page-no="1"><div class="pc pc1 w0 h0"><div class="t m0 x0 h1 y0 ff1 fs0 fc0 sc0 ls0 ws0">Normal<span class="_ _0"> </span><span class="ff2 fs1">tiny<span class="_ _0"> </span></span>T<span class="_ _1"></span>ext</div><div class="t m0 x1 h1 y1 ff1 fs0 fc0 sc0 ls0 ws0">Rise</div><div class="t m0 x0 h1 y2 ff1 fs0 fc0 sc0 ls1 ws0">CharSpace</div><div class="t m1 x0 h1 y3 ff1 fs0 fc0 sc0 ls0 ws0">Horizon<span class="_ _2"></span>tal<span class="_ _0"> </span>Scale</div><div class="t m2 x2 h2 y4 ff1 fs2 fc0 sc0 ls0 ws0">Rotated</div><div class="t m2 x3 h2 y5 ff1 fs2 fc0 sc0 ls0 ws0">Rise</div><div class="t m2 x4 h2 y6 ff1 fs2 fc0 sc0 ls2 ws0">CharSpace</div><div class="t m3 x5 h2 y7 ff1 fs2 fc0 sc0 ls0 ws0">Horizon<span class="_ _2"></span>tal<span class="_ _0"> </span>Scale</div><div class="t m0 x6 h1 y8 ff1 fs0 fc0 sc0 ls0 ws0">1</div></div><div class="pi" data-data='{"ctm":[1.307190,0.000000,0.000000,1.307190,0.000000,0.000000]}'></div></div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
BIN
test/browser_tests/basic_text/f1.woff
Normal file
BIN
test/browser_tests/basic_text/f1.woff
Normal file
Binary file not shown.
BIN
test/browser_tests/basic_text/f2.woff
Normal file
BIN
test/browser_tests/basic_text/f2.woff
Normal file
Binary file not shown.
BIN
test/browser_tests/geneve_1564.pdf
Normal file
BIN
test/browser_tests/geneve_1564.pdf
Normal file
Binary file not shown.
BIN
test/browser_tests/geneve_1564/bg1.png
Normal file
BIN
test/browser_tests/geneve_1564/bg1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 174 KiB |
BIN
test/browser_tests/geneve_1564/f1.woff
Normal file
BIN
test/browser_tests/geneve_1564/f1.woff
Normal file
Binary file not shown.
BIN
test/browser_tests/geneve_1564/f2.woff
Normal file
BIN
test/browser_tests/geneve_1564/f2.woff
Normal file
Binary file not shown.
738
test/browser_tests/geneve_1564/geneve_1564.html
Normal file
738
test/browser_tests/geneve_1564/geneve_1564.html
Normal file
File diff suppressed because one or more lines are too long
BIN
test/browser_tests/test_fail.pdf
Normal file
BIN
test/browser_tests/test_fail.pdf
Normal file
Binary file not shown.
9
test/browser_tests/test_fail.tex
Normal file
9
test/browser_tests/test_fail.tex
Normal file
@ -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}
|
BIN
test/browser_tests/text_visibility.pdf
Normal file
BIN
test/browser_tests/text_visibility.pdf
Normal file
Binary file not shown.
BIN
test/browser_tests/text_visibility/bg1.png
Normal file
BIN
test/browser_tests/text_visibility/bg1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 109 KiB |
BIN
test/browser_tests/text_visibility/f1.woff
Normal file
BIN
test/browser_tests/text_visibility/f1.woff
Normal file
Binary file not shown.
BIN
test/browser_tests/text_visibility/f2.woff
Normal file
BIN
test/browser_tests/text_visibility/f2.woff
Normal file
Binary file not shown.
153
test/browser_tests/text_visibility/text_visibility.html
Normal file
153
test/browser_tests/text_visibility/text_visibility.html
Normal file
File diff suppressed because one or more lines are too long
BIN
test/browser_tests/with_form.pdf
Normal file
BIN
test/browser_tests/with_form.pdf
Normal file
Binary file not shown.
BIN
test/browser_tests/with_form/bg1.png
Normal file
BIN
test/browser_tests/with_form/bg1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 9.1 KiB |
BIN
test/browser_tests/with_form/f1.woff
Normal file
BIN
test/browser_tests/with_form/f1.woff
Normal file
Binary file not shown.
BIN
test/browser_tests/with_form/f2.woff
Normal file
BIN
test/browser_tests/with_form/f2.woff
Normal file
Binary file not shown.
BIN
test/browser_tests/with_form/f3.woff
Normal file
BIN
test/browser_tests/with_form/f3.woff
Normal file
Binary file not shown.
BIN
test/browser_tests/with_form/f4.woff
Normal file
BIN
test/browser_tests/with_form/f4.woff
Normal file
Binary file not shown.
BIN
test/browser_tests/with_form/f5.woff
Normal file
BIN
test/browser_tests/with_form/f5.woff
Normal file
Binary file not shown.
BIN
test/browser_tests/with_form/f6.woff
Normal file
BIN
test/browser_tests/with_form/f6.woff
Normal file
Binary file not shown.
216
test/browser_tests/with_form/with_form.html
Normal file
216
test/browser_tests/with_form/with_form.html
Normal file
@ -0,0 +1,216 @@
|
||||
<!DOCTYPE html>
|
||||
<!-- Created by pdf2htmlEX (https://github.com/coolwanglu/pdf2htmlex) -->
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="generator" content="pdf2htmlEX"/>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
|
||||
<style type="text/css">
|
||||
/*!
|
||||
* Base CSS for pdf2htmlEX
|
||||
* Copyright 2012,2013 Lu Wang <coolwanglu@gmail.com>
|
||||
* https://github.com/coolwanglu/pdf2htmlEX/blob/master/share/LICENSE
|
||||
*/#sidebar{position:absolute;top:0;left:0;bottom:0;width:250px;padding:0;margin:0;overflow:auto}#page-container{position:absolute;top:0;left:0;margin:0;padding:0;border:0}@media screen{#sidebar.opened+#page-container{left:250px}#page-container{bottom:0;right:0;overflow:auto}.loading-indicator{display:none}.loading-indicator.active{display:block;position:absolute;width:64px;height:64px;top:50%;left:50%;margin-top:-32px;margin-left:-32px}.loading-indicator img{position:absolute;top:0;left:0;bottom:0;right:0}}@media print{@page{margin:0}html{margin:0}body{margin:0;-webkit-print-color-adjust:exact}#sidebar{display:none}#page-container{width:auto;height:auto;overflow:visible;background-color:transparent}.d{display:none}}.pf{position:relative;background-color:white;overflow:hidden;margin:0;border:0}.pc{position:absolute;border:0;padding:0;margin:0;top:0;left:0;width:100%;height:100%;overflow:hidden;display:block;transform-origin:0 0;-ms-transform-origin:0 0;-webkit-transform-origin:0 0}.pc.opened{display:block}.bf{position:absolute;border:0;margin:0;top:0;bottom:0;width:100%;height:100%;-ms-user-select:none;-moz-user-select:none;-webkit-user-select:none;user-select:none}.bi{position:absolute;border:0;margin:0;-ms-user-select:none;-moz-user-select:none;-webkit-user-select:none;user-select:none}@media print{.pf{margin:0;box-shadow:none;page-break-after:always;page-break-inside:avoid}@-moz-document url-prefix(){.pf{overflow:visible;border:1px solid #fff}.pc{overflow:visible}}}.c{position:absolute;border:0;padding:0;margin:0;overflow:hidden;display:block}.t{position:absolute;white-space:pre;font-size:1px;transform-origin:0 100%;-ms-transform-origin:0 100%;-webkit-transform-origin:0 100%;unicode-bidi:bidi-override;-moz-font-feature-settings:"liga" 0}.t:after{content:''}.t:before{content:'';display:inline-block}.t span{position:relative;unicode-bidi:bidi-override}._{display:inline-block;color:transparent;z-index:-1}::selection{background:rgba(127,255,255,0.4)}::-moz-selection{background:rgba(127,255,255,0.4)}.pi{display:none}.d{position:absolute;transform-origin:0 100%;-ms-transform-origin:0 100%;-webkit-transform-origin:0 100%}.it{border:0;background-color:rgba(255,255,255,0.0)}.ir:hover{cursor:pointer}</style>
|
||||
<style type="text/css">
|
||||
/* CSS for test cases */
|
||||
#page-container {
|
||||
overflow:hidden;
|
||||
}
|
||||
</style>
|
||||
<style type="text/css">
|
||||
.ff0{font-family:sans-serif;visibility:hidden;}
|
||||
@font-face{font-family:ff1;src:url(f1.woff)format("woff");}.ff1{font-family:ff1;line-height:1.108000;font-style:normal;font-weight:normal;visibility:visible;}
|
||||
@font-face{font-family:ff2;src:url(f2.woff)format("woff");}.ff2{font-family:ff2;line-height:0.889000;font-style:normal;font-weight:normal;visibility:visible;}
|
||||
@font-face{font-family:ff3;src:url(f3.woff)format("woff");}.ff3{font-family:ff3;line-height:1.326000;font-style:normal;font-weight:normal;visibility:visible;}
|
||||
@font-face{font-family:ff4;src:url(f4.woff)format("woff");}.ff4{font-family:ff4;line-height:1.170898;font-style:normal;font-weight:normal;visibility:visible;}
|
||||
@font-face{font-family:ff5;src:url(f5.woff)format("woff");}.ff5{font-family:ff5;line-height:1.254000;font-style:normal;font-weight:normal;visibility:visible;}
|
||||
@font-face{font-family:ff6;src:url(f6.woff)format("woff");}.ff6{font-family:ff6;line-height:1.292000;font-style:normal;font-weight:normal;visibility:visible;}
|
||||
.m1{transform:matrix(0.000000,-0.326797,0.326797,0.000000,0,0);-ms-transform:matrix(0.000000,-0.326797,0.326797,0.000000,0,0);-webkit-transform:matrix(0.000000,-0.326797,0.326797,0.000000,0,0);}
|
||||
.m0{transform:matrix(0.326797,0.000000,0.000000,0.326797,0,0);-ms-transform:matrix(0.326797,0.000000,0.000000,0.326797,0,0);-webkit-transform:matrix(0.326797,0.000000,0.000000,0.326797,0,0);}
|
||||
.v0{vertical-align:0.000000px;}
|
||||
.ls1{letter-spacing:-0.480000px;}
|
||||
.ls0{letter-spacing:0.000000px;}
|
||||
.ls3{letter-spacing:4.326400px;}
|
||||
.ls2{letter-spacing:105.216000px;}
|
||||
.sc_{text-shadow:none;}
|
||||
.sc0{text-shadow:-0.015em 0 transparent,0 0.015em transparent,0.015em 0 transparent,0 -0.015em transparent;}
|
||||
@media screen and (-webkit-min-device-pixel-ratio:0){
|
||||
.sc_{-webkit-text-stroke:0px transparent;}
|
||||
.sc0{-webkit-text-stroke:0.015em transparent;text-shadow:none;}
|
||||
}
|
||||
.ws0{word-spacing:0.000000px;}
|
||||
.ws1{word-spacing:103.742400px;}
|
||||
.ws2{word-spacing:108.590400px;}
|
||||
._9{margin-left:-123.403200px;}
|
||||
._6{margin-left:-118.555200px;}
|
||||
._8{margin-left:-99.360000px;}
|
||||
._5{margin-left:-94.512000px;}
|
||||
._c{margin-left:-13.520000px;}
|
||||
._7{margin-left:-11.280000px;}
|
||||
._3{margin-left:-6.240000px;}
|
||||
._a{margin-left:-4.996000px;}
|
||||
._0{margin-left:-2.640000px;}
|
||||
._e{margin-left:-1.560000px;}
|
||||
._1{width:9.412000px;}
|
||||
._d{width:31.564000px;}
|
||||
._2{width:49.400000px;}
|
||||
._4{width:103.728000px;}
|
||||
._b{width:108.576000px;}
|
||||
.fc1{color:rgb(255,255,255);}
|
||||
.fc0{color:rgb(0,0,0);}
|
||||
.fs1{font-size:28.000000px;}
|
||||
.fs0{font-size:48.000000px;}
|
||||
.fs4{font-size:52.000000px;}
|
||||
.fs2{font-size:56.000000px;}
|
||||
.fs3{font-size:116.000000px;}
|
||||
.y2{bottom:22.652157px;}
|
||||
.y0{bottom:39.869281px;}
|
||||
.y1{bottom:51.602614px;}
|
||||
.y3{bottom:52.972549px;}
|
||||
.y1b{bottom:74.683660px;}
|
||||
.y1a{bottom:102.134641px;}
|
||||
.y19{bottom:135.467974px;}
|
||||
.y18{bottom:190.369935px;}
|
||||
.y17{bottom:223.703268px;}
|
||||
.y16{bottom:278.605229px;}
|
||||
.y15{bottom:306.056209px;}
|
||||
.y14{bottom:339.389542px;}
|
||||
.y13{bottom:394.291503px;}
|
||||
.y12{bottom:427.624837px;}
|
||||
.y1c{bottom:449.921569px;}
|
||||
.y11{bottom:482.526797px;}
|
||||
.y10{bottom:515.860131px;}
|
||||
.yf{bottom:598.213072px;}
|
||||
.ye{bottom:623.703268px;}
|
||||
.yd{bottom:649.193464px;}
|
||||
.yc{bottom:674.683660px;}
|
||||
.yb{bottom:700.173856px;}
|
||||
.ya{bottom:755.075817px;}
|
||||
.y9{bottom:780.566013px;}
|
||||
.y8{bottom:806.056209px;}
|
||||
.y7{bottom:831.546405px;}
|
||||
.y6{bottom:857.036601px;}
|
||||
.y5{bottom:916.539739px;}
|
||||
.y4{bottom:966.777908px;}
|
||||
.h3{height:19.124000px;}
|
||||
.h2{height:42.720000px;}
|
||||
.h7{height:49.344000px;}
|
||||
.h6{height:55.640000px;}
|
||||
.h4{height:59.920000px;}
|
||||
.h5{height:109.259766px;}
|
||||
.h1{height:924.836601px;}
|
||||
.h0{height:1035.294118px;}
|
||||
.w1{width:682.352941px;}
|
||||
.w0{width:800.000000px;}
|
||||
.x0{left:47.058824px;}
|
||||
.x8{left:89.175817px;}
|
||||
.x5{left:91.424837px;}
|
||||
.x6{left:117.568627px;}
|
||||
.x7{left:156.784314px;}
|
||||
.x1{left:185.962092px;}
|
||||
.x2{left:367.098039px;}
|
||||
.x4{left:438.899346px;}
|
||||
.x3{left:708.799869px;}
|
||||
@media print{
|
||||
.v0{vertical-align:0.000000pt;}
|
||||
.ls1{letter-spacing:-0.489600pt;}
|
||||
.ls0{letter-spacing:0.000000pt;}
|
||||
.ls3{letter-spacing:4.412928pt;}
|
||||
.ls2{letter-spacing:107.320320pt;}
|
||||
.ws0{word-spacing:0.000000pt;}
|
||||
.ws1{word-spacing:105.817248pt;}
|
||||
.ws2{word-spacing:110.762208pt;}
|
||||
._9{margin-left:-125.871264pt;}
|
||||
._6{margin-left:-120.926304pt;}
|
||||
._8{margin-left:-101.347200pt;}
|
||||
._5{margin-left:-96.402240pt;}
|
||||
._c{margin-left:-13.790400pt;}
|
||||
._7{margin-left:-11.505600pt;}
|
||||
._3{margin-left:-6.364800pt;}
|
||||
._a{margin-left:-5.095920pt;}
|
||||
._0{margin-left:-2.692800pt;}
|
||||
._e{margin-left:-1.591200pt;}
|
||||
._1{width:9.600240pt;}
|
||||
._d{width:32.195280pt;}
|
||||
._2{width:50.388000pt;}
|
||||
._4{width:105.802560pt;}
|
||||
._b{width:110.747520pt;}
|
||||
.fs1{font-size:28.560000pt;}
|
||||
.fs0{font-size:48.960000pt;}
|
||||
.fs4{font-size:53.040000pt;}
|
||||
.fs2{font-size:57.120000pt;}
|
||||
.fs3{font-size:118.320000pt;}
|
||||
.y2{bottom:23.105200pt;}
|
||||
.y0{bottom:40.666667pt;}
|
||||
.y1{bottom:52.634667pt;}
|
||||
.y3{bottom:54.032000pt;}
|
||||
.y1b{bottom:76.177333pt;}
|
||||
.y1a{bottom:104.177333pt;}
|
||||
.y19{bottom:138.177333pt;}
|
||||
.y18{bottom:194.177333pt;}
|
||||
.y17{bottom:228.177333pt;}
|
||||
.y16{bottom:284.177333pt;}
|
||||
.y15{bottom:312.177333pt;}
|
||||
.y14{bottom:346.177333pt;}
|
||||
.y13{bottom:402.177333pt;}
|
||||
.y12{bottom:436.177333pt;}
|
||||
.y1c{bottom:458.920000pt;}
|
||||
.y11{bottom:492.177333pt;}
|
||||
.y10{bottom:526.177333pt;}
|
||||
.yf{bottom:610.177333pt;}
|
||||
.ye{bottom:636.177333pt;}
|
||||
.yd{bottom:662.177333pt;}
|
||||
.yc{bottom:688.177333pt;}
|
||||
.yb{bottom:714.177333pt;}
|
||||
.ya{bottom:770.177333pt;}
|
||||
.y9{bottom:796.177333pt;}
|
||||
.y8{bottom:822.177333pt;}
|
||||
.y7{bottom:848.177333pt;}
|
||||
.y6{bottom:874.177333pt;}
|
||||
.y5{bottom:934.870533pt;}
|
||||
.y4{bottom:986.113467pt;}
|
||||
.h3{height:19.506480pt;}
|
||||
.h2{height:43.574400pt;}
|
||||
.h7{height:50.330880pt;}
|
||||
.h6{height:56.752800pt;}
|
||||
.h4{height:61.118400pt;}
|
||||
.h5{height:111.444961pt;}
|
||||
.h1{height:943.333333pt;}
|
||||
.h0{height:1056.000000pt;}
|
||||
.w1{width:696.000000pt;}
|
||||
.w0{width:816.000000pt;}
|
||||
.x0{left:48.000000pt;}
|
||||
.x8{left:90.959333pt;}
|
||||
.x5{left:93.253333pt;}
|
||||
.x6{left:119.920000pt;}
|
||||
.x7{left:159.920000pt;}
|
||||
.x1{left:189.681333pt;}
|
||||
.x2{left:374.440000pt;}
|
||||
.x4{left:447.677333pt;}
|
||||
.x3{left:722.975867pt;}
|
||||
}
|
||||
</style>
|
||||
<title></title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="page-container">
|
||||
<div id="pf1" class="pf w0 h0" data-page-no="1"><div class="pc pc1 w0 h0"><img class="bi x0 y0 w1 h1" alt="" src="bg1.png"/><div class="t m0 x1 h2 y1 ff1 fs0 fc0 sc0 ls0 ws0">Éditions « <span class="_ _0"></span>À Reproduire » Internet : www<span class="_ _0"></span>.en<span class="_ _0"></span>volee.com</div><div class="t m0 x2 h3 y2 ff2 fs1 fc0 sc0 ls0 ws0">Question de textes 4</div><div class="t m0 x3 h4 y3 ff3 fs2 fc1 sc0 ls0 ws0">7</div><div class="t m0 x4 h2 y4 ff1 fs0 fc0 sc0 ls0 ws0">Nom</div><div class="t m0 x5 h5 y5 ff4 fs3 fc1 sc0 ls0 ws0">Mona veut un chien</div><div class="t m0 x6 h6 y6 ff5 fs4 fc0 sc0 ls0 ws0"> <span class="_ _1"> </span><span class="ff3 fc1">4</span> <span class="_ _2"> </span>Que veut dire la mère de Mona quand elle dit : « C’est beaucoup de travail, avoir un chien » <span class="_ _3"></span>?</div><div class="t m0 x6 h7 y7 ff6 fs0 fc0 sc0 ls0 ws0"> <span class="_ _4"> </span>a) <span class="_ _1"> </span>Mona doit faire plus de recherche avant d’être prête à avoir un chien.</div><div class="t m0 x6 h7 y8 ff6 fs0 fc0 sc0 ls0 ws1"> b) <span class="_ _5"></span> <span class="_ _6"></span><span class="ls1 ws0">Les chiens travaillent fort afi<span class="_ _7"></span> <span class="_ _0"></span>n d’être de bons animaux de compagnie pour les gens.</span></div><div class="t m0 x6 h7 y9 ff6 fs0 fc0 sc0 ls0 ws2"> c) <span class="_ _8"></span> <span class="_ _9"></span><span class="ls1 ws0">Les chiens ont besoin de nourriture et d’exercice. Il faut aussi ramasser leurs dégâts.</span></div><div class="t m0 x6 h7 ya ff6 fs0 fc0 sc0 ls0 ws0"> <span class="_ _4"> </span>d) <span class="_ _1"> </span>Mona devra se reposer plus si elle a un chien.</div><div class="t m0 x6 h6 yb ff5 fs4 fc0 sc0 ls0 ws0"> <span class="_ _1"> </span><span class="ff3 fc1">5</span> <span class="_ _2"> </span>Comment Mona a-t-elle acquis autant de connaissances sur les chiens <span class="_ _3"></span>?</div><div class="t m0 x6 h7 yc ff6 fs0 fc0 sc0 ls0 ws0"> <span class="_ _4"> </span>a) <span class="_ _1"> </span>En écoutant son enseignante à l’école.</div><div class="t m0 x6 h7 yd ff6 fs0 fc0 sc0 ls0 ws0"> <span class="_ _4"> </span>b) <span class="_ _1"> </span>En visitant un site W<span class="_ _a"></span>eb.</div><div class="t m0 x6 h7 ye ff6 fs0 fc0 sc0 ls0 ws0"> <span class="_ _b"> </span>c) <span class="_ _1"> </span>En écrivant à la Société protectrice des animaux.</div><div class="t m0 x6 h7 yf ff6 fs0 fc0 sc0 ls0 ws0"> <span class="_ _4"> </span>d) <span class="_ _1"> </span>En regardant une vidéo sur les chiens.</div><div class="t m0 x6 h6 y10 ff5 fs4 fc0 sc0 ls0 ws0"> <span class="_ _1"> </span><span class="ff3 fc1">6</span> <span class="_ _2"> </span>Pourquoi Mona veut-elle un Jack Russell <span class="_ _3"></span>?</div><div class="t m0 x6 h7 y11 ff6 fs0 fc0 sc0 ls2 ws0"> </div><div class="t m0 x6 h6 y12 ff3 fs4 fc1 sc0 ls3 ws0"> 7<span class="_ _a"></span><span class="ff5 fc0 ls0"> <span class="_ _2"> </span>Que devra faire Mona même si elle est fatiguée ou qu’il pleut <span class="_ _3"></span>?</span></div><div class="t m0 x6 h7 y13 ff6 fs0 fc0 sc0 ls2 ws0"> </div><div class="t m0 x6 h6 y14 ff5 fs4 fc0 sc0 ls0 ws0"> <span class="_ _1"> </span><span class="ff3 fc1">8</span> <span class="_ _2"> </span>Décris physiquement le chien que Mona veut choisir<span class="_ _0"></span>.</div><div class="t m0 x6 h7 y15 ff6 fs0 fc0 sc0 ls2 ws0"> </div><div class="t m0 x7 h7 y16 ff6 fs0 fc0 sc0 ls0 ws0"> </div><div class="t m0 x6 h6 y17 ff3 fs4 fc1 sc0 ls3 ws0"> 9<span class="_ _a"></span><span class="ff5 fc0 ls0"> <span class="_ _2"> </span>Quand fait-elle ses recherches sur le Web <span class="_ _3"></span>?</span></div><div class="t m0 x6 h7 y18 ff6 fs0 fc0 sc0 ls2 ws0"> </div><div class="t m0 x6 h6 y19 ff3 fs4 fc1 sc0 ls0 ws0"> <span class="_ _c"></span>10<span class="ff5 fc0"> <span class="_ _d"> </span>Que contient le site W<span class="_ _e"></span>eb préféré de Mona <span class="_ _3"></span>? Nomme deux éléments.</span></div><div class="t m0 x6 h7 y1a ff6 fs0 fc0 sc0 ls2 ws0"> </div><div class="t m0 x7 h7 y1b ff6 fs0 fc0 sc0 ls0 ws0"> </div><div class="t m1 x8 h7 y1c ff6 fs0 fc0 sc0 ls0 ws0">REPÉRAGE</div><input id="text-1-0" class="it" type="text" value="" style="position: absolute; left: 0.000000px; bottom: 0.000000px; width: 0.000000px; height: 0.000000px; line-height: 0.000000px; font-size: 0.000000px;" />
|
||||
<div id="cb-1-1" class="ir" style="position: absolute; left: 0.000000px; bottom: 0.000000px; width: 3.000000px; height: 3.000000px; background-size: cover;" ></div>
|
||||
<div id="cb-1-2" class="ir" style="position: absolute; left: 0.000000px; bottom: 0.000000px; width: 3.000000px; height: 3.000000px; background-size: cover;" ></div>
|
||||
<div id="cb-1-3" class="ir" style="position: absolute; left: 0.000000px; bottom: 0.000000px; width: 3.000000px; height: 3.000000px; background-size: cover;" ></div>
|
||||
<div id="cb-1-4" class="ir" style="position: absolute; left: 0.000000px; bottom: 0.000000px; width: 3.000000px; height: 3.000000px; background-size: cover;" ></div>
|
||||
<input id="text-1-5" class="it" type="text" value="" style="position: absolute; left: 0.000000px; bottom: 0.000000px; width: 0.000000px; height: 0.000000px; line-height: 0.000000px; font-size: 0.000000px;" />
|
||||
<input id="text-1-6" class="it" type="text" value="" style="position: absolute; left: 0.000000px; bottom: 0.000000px; width: 0.000000px; height: 0.000000px; line-height: 0.000000px; font-size: 0.000000px;" />
|
||||
<input id="text-1-7" class="it" type="text" value="" style="position: absolute; left: 0.000000px; bottom: 0.000000px; width: 0.000000px; height: 0.000000px; line-height: 0.000000px; font-size: 0.000000px;" />
|
||||
<input id="text-1-8" class="it" type="text" value="" style="position: absolute; left: 0.000000px; bottom: 0.000000px; width: 0.000000px; height: 0.000000px; line-height: 0.000000px; font-size: 0.000000px;" />
|
||||
<input id="text-1-9" class="it" type="text" value="" style="position: absolute; left: 0.000000px; bottom: 0.000000px; width: 0.000000px; height: 0.000000px; line-height: 0.000000px; font-size: 0.000000px;" />
|
||||
<input id="text-1-a" class="it" type="text" value="" style="position: absolute; left: 0.000000px; bottom: 0.000000px; width: 0.000000px; height: 0.000000px; line-height: 0.000000px; font-size: 0.000000px;" />
|
||||
<div id="cb-1-b" class="ir" style="position: absolute; left: 0.000000px; bottom: 0.000000px; width: 3.000000px; height: 3.000000px; background-size: cover;" ></div>
|
||||
<div id="cb-1-c" class="ir" style="position: absolute; left: 0.000000px; bottom: 0.000000px; width: 3.000000px; height: 3.000000px; background-size: cover;" ></div>
|
||||
<div id="cb-1-d" class="ir" style="position: absolute; left: 0.000000px; bottom: 0.000000px; width: 3.000000px; height: 3.000000px; background-size: cover;" ></div>
|
||||
<div id="cb-1-e" class="ir" style="position: absolute; left: 0.000000px; bottom: 0.000000px; width: 3.000000px; height: 3.000000px; background-size: cover;" ></div>
|
||||
<input id="text-1-f" class="it" type="text" value="" style="position: absolute; left: 0.000000px; bottom: 0.000000px; width: 0.000000px; height: 0.000000px; line-height: 0.000000px; font-size: 0.000000px;" />
|
||||
</div><div class="pi" data-data='{"ctm":[1.307190,0.000000,0.000000,1.307190,0.000000,0.000000]}'></div></div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
4
test/fancy.min.css
vendored
Normal file
4
test/fancy.min.css
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
/* CSS for test cases */
|
||||
#page-container {
|
||||
overflow:hidden;
|
||||
}
|
3
test/old/convert_to_woff.fontforge
Normal file
3
test/old/convert_to_woff.fontforge
Normal file
@ -0,0 +1,3 @@
|
||||
Open($1);
|
||||
Generate($1:r+".woff");
|
||||
Quit(0);
|
24
test/old/test.py
Executable file
24
test/old/test.py
Executable file
@ -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('<!DOCTYPE html>\n<html><head><meta charset=\"utf-8\"></head><body><div style="position:absolute;top:0;left:0;width:80%;height:100%;"><iframe width="100%" height="100%" name="pdf"></iframe></div><div style="position:absolute;top:0;right:0;width:20%;height:100%;overflow:auto;text-align:right;">')
|
||||
|
||||
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('<a href="html/%shtml" target="pdf">%s</a><br/>' % (ff,ff))
|
||||
outf.flush();
|
||||
|
||||
outf.write('</div></body></html>')
|
2
test/start_xvfb.sh
Executable file
2
test/start_xvfb.sh
Executable file
@ -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
|
137
test/test.py
137
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('<!DOCTYPE html>\n<html><head><meta charset=\"utf-8\"></head><body><div style="position:absolute;top:0;left:0;width:80%;height:100%;"><iframe width="100%" height="100%" name="pdf"></iframe></div><div style="position:absolute;top:0;right:0;width:20%;height:100%;overflow:auto;text-align:right;">')
|
||||
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('<a href="html/%shtml" target="pdf">%s</a><br/>' % (ff,ff))
|
||||
outf.flush();
|
||||
CANONICAL_TEMPDIR = '/tmp/pdf2htmlEX_test'
|
||||
|
||||
outf.write('</div></body></html>')
|
||||
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
|
||||
|
||||
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)
|
||||
|
37
test/test_local_browser.py
Executable file
37
test/test_local_browser.py
Executable file
@ -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)
|
@ -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()
|
86
test/test_output.py
Normal file
86
test/test_output.py
Normal file
@ -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'])
|
||||
|
138
test/test_remote_browser.py
Executable file
138
test/test_remote_browser.py
Executable file
@ -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()
|
Loading…
Reference in New Issue
Block a user