IGN:Fix a memory leak in the new PDF get_metadata code, remove use of tmpfile and make it build on windows

This commit is contained in:
Kovid Goyal 2009-09-23 11:28:55 -06:00
parent 9d292633c7
commit 73801dd2ae
10 changed files with 265 additions and 61 deletions

View File

@ -73,14 +73,17 @@ def pkgconfig_libs(name, envvar, default):
def consolidate(envvar, default):
val = os.environ.get(envvar, default)
ans = [x.strip() for x in val.split(os.pathsep())]
ans = [x.strip() for x in val.split(os.pathsep)]
return [x for x in ans if x and os.path.exists(x)]
pyqt = pyqtconfig.Configuration()
qt_inc = pyqt.qt_inc_dir
qt_lib = pyqt.qt_lib_dir
ft_lib_dirs = []
ft_libs = []
jpg_libs = []
jpg_lib_dirs = []
fc_inc = '/usr/include/fontconfig'
fc_lib = '/usr/lib'
podofo_inc = '/usr/include/podofo'
@ -90,13 +93,27 @@ if iswindows:
fc_inc = r'C:\cygwin\home\kovid\fontconfig\include\fontconfig'
fc_lib = r'C:\cygwin\home\kovid\fontconfig\lib'
poppler_inc_dirs = consolidate('POPPLER_INC_DIR',
r'C:\cygwin\home\kovid\poppler\include\poppler')
popplerqt4_inc_dirs = poppler_inc_dirs + [poppler_inc_dirs[0]+r'\qt4']
(r'C:\cygwin\home\kovid\poppler\poppler-source\poppler;'
r'C:\cygwin\home\kovid\poppler\poppler-source;'
r'C:\cygwin\home\kovid\poppler\poppler-build;'
r'C:\cygwin\home\kovid\poppler\poppler-build\poppler'))
popplerqt4_inc_dirs = poppler_inc_dirs + [poppler_inc_dirs[1]+r'\qt4']
poppler_lib_dirs = consolidate('POPPLER_LIB_DIR',
r'C:\cygwin\home\kovid\poppler\lib')
(r'C:\cygwin\home\kovid\poppler\poppler-build\qt4\src\Release;'
r'C:\cygwin\home\kovid\poppler\poppler-build\Release'))
popplerqt4_lib_dirs = poppler_lib_dirs
poppler_libs = ['poppler']
poppler_libs = ['poppler', 'poppler-qt4']
popplerqt4_libs = poppler_libs + ['QtCore4', 'QtGui4']
png_inc_dirs = [r'C:\cygwin\home\\kovid\gnuwin32\include']
png_lib_dirs = [r'C:\cygwin\home\\kovid\gnuwin32\lib']
png_libs = [r'libpng']
jpg_lib_dirs = [r'C:\cygwin\home\\kovid\gnuwin32\lib']
jpg_libs = ['jpeg']
magick_inc_dirs = [r'C:\cygwin\home\kovid\ImageMagick-6.5.6-Q16\include']
magick_lib_dirs = [r'C:\cygwin\home\kovid\ImageMagick-6.5.6-Q16\lib']
magick_libs = ['CORE_RL_wand_', 'CORE_RL_magick_']
ft_lib_dirs = [r'C:\cygwin\home\\kovid\gnuwin32\lib']
ft_libs = ['freetype']
podofo_inc = 'C:\\podofo\\include\\podofo'
podofo_lib = r'C:\podofo'
elif isosx:

View File

@ -17,7 +17,7 @@ from setup.build_environment import fc_inc, fc_lib, \
podofo_lib, podofo_error, poppler_error, pyqt, OSX_SDK, NMAKE, \
leopard_build, QMAKE, msvc, MT, win_inc, win_lib, png_inc_dirs, \
magick_inc_dirs, magick_lib_dirs, png_lib_dirs, png_libs, \
magick_error, magick_libs
magick_error, magick_libs, ft_lib_dirs, ft_libs, jpg_libs, jpg_lib_dirs
MT
isunix = islinux or isosx
@ -49,7 +49,21 @@ reflow_sources = glob.glob(os.path.join(SRC, 'calibre', 'ebooks', 'pdf', '*.cpp'
reflow_headers = glob.glob(os.path.join(SRC, 'calibre', 'ebooks', 'pdf', '*.h'))
reflow_error = poppler_error if poppler_error else magick_error
pdfreflow_libs = []
if iswindows:
pdfreflow_libs = ['advapi32', 'User32', 'Gdi32']
extensions = [
Extension('pdfreflow',
reflow_sources,
headers=reflow_headers,
libraries=poppler_libs+magick_libs+png_libs+ft_libs+jpg_libs+pdfreflow_libs,
lib_dirs=poppler_lib_dirs+magick_lib_dirs+png_lib_dirs+ft_lib_dirs+jpg_lib_dirs,
inc_dirs=poppler_inc_dirs+magick_inc_dirs+png_inc_dirs,
error=reflow_error,
cflags=['-DPNG_SKIP_SETJMP_CHECK'] if islinux else []
),
Extension('lzx',
['calibre/utils/lzx/lzxmodule.c',
'calibre/utils/lzx/compressor.c',
@ -96,15 +110,6 @@ extensions = [
sip_files = ['calibre/gui2/pictureflow/pictureflow.sip']
),
Extension('pdfreflow',
reflow_sources,
headers=reflow_headers,
libraries=poppler_libs+magick_libs+png_libs,
lib_dirs=poppler_lib_dirs+magick_lib_dirs+png_lib_dirs,
inc_dirs=poppler_inc_dirs+magick_inc_dirs+png_inc_dirs,
error=reflow_error,
cflags=['-DPNG_SKIP_SETJMP_CHECK'] if islinux else []
)
]
@ -266,7 +271,7 @@ class Build(Command):
['/EXPORT:init'+ext.name] + objects + ['/OUT:'+dest]
else:
cmd += objects + ['-o', dest] + ldflags + ext.ldflags + elib + xlib
print ' '.join(cmd)
self.info('\n\n', ' '.join(cmd), '\n\n')
subprocess.check_call(cmd)
if iswindows:
#manifest = dest+'.manifest'
@ -369,7 +374,7 @@ class BuildPDF2XML(Command):
continue
obj = self.j(odest, self.b(src+'.o'))
if self.newer(obj, [src]+reflow_headers):
cmd = ['g++', '-pthread', '-pedantic', '-g', '-c', '-Wall', '-I/usr/include/poppler',
cmd = ['g++', '-pthread', '-pedantic', '-ggdb', '-c', '-Wall', '-I/usr/include/poppler',
'-I/usr/include/ImageMagick',
'-DPDF2XML', '-o', obj, src]
self.info(*cmd)

View File

@ -296,8 +296,15 @@ File ::F1D1D581-9194-D12F-6139-F920EEC1B14E -name winutil.pyd -parent 0AC00D67-8
File ::A94E4D31-5F8C-A363-AE61-A8F2E3A7B756 -name lzx.pyd -parent 0AC00D67-8452-CABB-6843-FE6A464E9AE9
File ::2C88788E-B9FB-729D-442B-600ED97096A0 -name cPalmdoc.pyd -parent 0AC00D67-8452-CABB-6843-FE6A464E9AE9
File ::26503DDE-8C5F-102F-B689-C6A17B2D6C93 -name msdes.pyd -parent 0AC00D67-8452-CABB-6843-FE6A464E9AE9
File ::0FE771BC-EBF3-E4E0-DDE0-2D69F2BD99C6 -name calibre_poppler.pyd -parent 0AC00D67-8452-CABB-6843-FE6A464E9AE9
File ::FCB0D098-8F90-1C97-9E20-65546F08A203 -name fontconfig.pyd -parent 0AC00D67-8452-CABB-6843-FE6A464E9AE9
File ::E3BD0592-A0B9-B69D-7FD8-AD828D301351 -name cPalmdoc.pyd.manifest -parent 0AC00D67-8452-CABB-6843-FE6A464E9AE9
File ::03E9E137-98F0-1B8C-CBE7-6E3A6691D4F1 -name pdfreflow.pyd -parent 0AC00D67-8452-CABB-6843-FE6A464E9AE9
File ::BF6697B0-1BA5-024A-0566-2F3B22D8449F -name lzx.pyd.manifest -parent 0AC00D67-8452-CABB-6843-FE6A464E9AE9
File ::520B27CC-2288-A6BA-689B-B7C090B1D27E -name pdfreflow.pyd.manifest -parent 0AC00D67-8452-CABB-6843-FE6A464E9AE9
File ::F49E4A29-3607-039C-C67C-31E59861037C -name fontconfig.pyd.manifest -parent 0AC00D67-8452-CABB-6843-FE6A464E9AE9
File ::3F660706-F752-6458-0DF5-CFD1C556DDC1 -name winutil.pyd.manifest -parent 0AC00D67-8452-CABB-6843-FE6A464E9AE9
File ::4DD0593A-E9D1-2EB4-4CB4-DF75F28C9D0C -name podofo.pyd.manifest -parent 0AC00D67-8452-CABB-6843-FE6A464E9AE9
File ::F6486848-58DE-9CC7-7F72-A1A3BC936DD3 -name msdes.pyd.manifest -parent 0AC00D67-8452-CABB-6843-FE6A464E9AE9
File ::01034EB7-C79C-42B9-6FF0-E06C72EF2623 -type dir -name iconengines -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
File ::8ADB07A0-6B9E-8F53-34DF-2035C7C343F1 -name qsvgicon4.dll -parent 01034EB7-C79C-42B9-6FF0-E06C72EF2623
File ::41F512E3-9F39-68C6-BB7F-58F572755D35 -name qsvgicon4.exp -parent 01034EB7-C79C-42B9-6FF0-E06C72EF2623
@ -565,8 +572,8 @@ File ::7BE6B538-70D5-A7EB-5F91-E14CE57B394B -name calibre-complete.exe.local -pa
File ::C4E40030-3EE0-8B05-E6B9-89E81433EE1F -name phonon4.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
File ::9E84342F-36ED-7ED3-8F90-1EC55267BCFC -name poppler-qt4.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
File ::C9967023-A4C2-856C-1D90-DC710105EBCD -name jpeg62.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
File ::12FA46DA-F25E-0D26-E23C-006013332FED -name freetype.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
File ::B1560042-C99B-9803-552E-21C15F0DFD85 -type dir -name resources -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
File ::DEDE8BE9-D712-2770-A1EC-7E9164CC6D29 -name libpng12.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
Component ::F6829AB7-9F66-4CEE-CA0E-21F54C6D3609 -setup Install -active Yes -platforms {AIX-ppc FreeBSD-4-x86 FreeBSD-x86 HPUX-hppa Linux-x86 Solaris-sparc Windows FreeBSD-5-x86 FreeBSD-6-x86 FreeBSD-7-x86 Linux-x86_64 Solaris-x86} -name Main -parent Components
SetupType ::D9ADE41C-B744-690C-2CED-CF826BF03D2E -setup Install -active Yes -platforms {AIX-ppc FreeBSD-4-x86 FreeBSD-x86 HPUX-hppa Linux-x86 Solaris-sparc Windows FreeBSD-5-x86 FreeBSD-6-x86 FreeBSD-7-x86 Linux-x86_64 Solaris-x86} -name Typical -parent SetupTypes

View File

@ -9,8 +9,8 @@ Freeze app into executable using py2exe.
QT_DIR = 'C:\\Qt\\4.5.2'
LIBUSB_DIR = 'C:\\libusb'
LIBUNRAR = 'C:\\Program Files\\UnrarDLL\\unrar.dll'
PDFTOHTML = 'C:\\cygwin\\home\\kovid\\poppler-0.10.6\\rel\\pdftohtml.exe'
POPPLER = 'C:\\cygwin\\home\\kovid\\poppler'
POPPLER = 'C:\\cygwin\\home\\kovid\\poppler\\poppler-build'
GNUWIN32 = r'C:\cygwin\home\kovid\gnuwin32'
IMAGEMAGICK_DIR = 'C:\\ImageMagick'
PDFTK = 'C:\\pdftk.exe'
PODOFO = 'C:\\podofo'
@ -189,10 +189,12 @@ class BuildEXE(bc):
print '\tAdding unrar'
shutil.copyfile(LIBUNRAR, os.path.join(PY2EXE_DIR, os.path.basename(LIBUNRAR)))
print '\tAdding poppler'
for x in ('bin\\pdftohtml.exe', 'bin\\poppler-qt4.dll',
'bin\\freetype.dll', 'bin\\jpeg62.dll'):
for x in (r'utils\Release\pdftohtml.exe',
r'qt4\src\Release\poppler-qt4.dll'):
shutil.copyfile(os.path.join(POPPLER, x),
os.path.join(PY2EXE_DIR, os.path.basename(x)))
for x in ('jpeg62', 'zlib1', 'libpng12'):
shutil.copy2(os.path.join(GNUWIN32, 'bin', x+'.dll'), PY2EXE_DIR)
print '\tAdding podofo'
for f in glob.glob(os.path.join(PODOFO, '*.dll')):
shutil.copyfile(f, os.path.join(PY2EXE_DIR, os.path.basename(f)))

View File

@ -31,11 +31,12 @@ static const char *FONT_MODS[7] = {
NULL
};
#ifdef _WIN32
#define ap_toupper(c) (toupper(((unsigned char)(c))))
static inline
char *strcasestr( char *h, char *n )
const char *strcasestr(const char *h, const char *n )
{ /* h="haystack", n="needle" */
char *a=h, *e=n;
const char *a=h, *e=n;
if( !h || !*h || !n || !*n ) { return 0; }
@ -49,6 +50,7 @@ char *strcasestr( char *h, char *n )
}
return *e ? 0 : h;
}
#endif
static string* family_name(const string *font_name) {
if (!font_name) return NULL;

View File

@ -10,6 +10,10 @@
#include "images.h"
#include "utils.h"
#ifdef _WIN32
inline double round(double x) { return (x-floor(x))>0.5 ? ceil(x) : floor(x); }
#endif
#define xoutRound(x) ( static_cast<int>(round(x)) )
using namespace std;
using namespace calibre_reflow;
@ -287,3 +291,134 @@ void PNGWriter::write_splash_bitmap(SplashBitmap *bitmap) {
this->writePointers(row_pointers);
delete[] row_pointers;
}
void calibre_png_mem_write(png_structp png_ptr, png_bytep data, png_size_t length) {
if (!png_ptr || length < 1) return;
vector<char> *buf = static_cast< vector<char>* >(png_ptr->io_ptr);
buf->reserve(buf->capacity() + length);
do {
buf->push_back(static_cast<char>(*data));
data++; length--;
} while(length > 0);
}
void calibre_png_mem_flush(png_structp png_ptr) {}
void PNGMemWriter::init(vector<char> *buf, int width, int height) {
/* initialize stuff */
this->png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!this->png_ptr)
throw ReflowException("png_create_write_struct failed");
this->info_ptr = png_create_info_struct(png_ptr);
if (!this->info_ptr)
throw ReflowException("png_create_info_struct failed");
if (setjmp(png_jmpbuf(this->png_ptr)))
throw ReflowException("png_jmpbuf failed");
png_set_write_fn(this->png_ptr, static_cast<void *>(buf),
calibre_png_mem_write, calibre_png_mem_flush);
if (setjmp(png_jmpbuf(this->png_ptr)))
throw ReflowException("png_set_write failed");
// Set up the type of PNG image and the compression level
png_set_compression_level(this->png_ptr, Z_BEST_COMPRESSION);
png_byte bit_depth = 8;
png_byte color_type = PNG_COLOR_TYPE_RGB;
png_byte interlace_type = PNG_INTERLACE_NONE;
png_set_IHDR(this->png_ptr, this->info_ptr, width, height,
bit_depth, color_type, interlace_type,
PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
png_write_info(png_ptr, info_ptr);
if (setjmp(png_jmpbuf(png_ptr)))
throw ReflowException("error during writing png info bytes");
}
void calibre_jpeg_error_exit (j_common_ptr cinfo)
{
/* cinfo->err really points to a my_error_mgr struct, so coerce pointer */
calibre_jpeg_err_mgr *err = (calibre_jpeg_err_mgr *)(cinfo->err);
/* Always display the message. */
/* We could postpone this until after returning, if we chose. */
//(*cinfo->err->output_message) (cinfo);
/* Return control to the setjmp point */
longjmp(err->setjmp_buffer, 1);
}
JPEGWriter::JPEGWriter() {
this->cinfo.err = jpeg_std_error(&this->jerr.pub);
jpeg_create_compress(&this->cinfo);
this->jerr.pub.error_exit = calibre_jpeg_error_exit;
this->check();
this->outfile = NULL;
}
void JPEGWriter::init(int width, int height) {
cinfo.image_width = width;
cinfo.image_height = height;
cinfo.input_components = 3; /* # of color components per pixel */
cinfo.in_color_space = JCS_RGB;
jpeg_set_defaults(&this->cinfo);
this->check();
jpeg_start_compress(&this->cinfo, TRUE);
this->check();
}
void JPEGWriter::init_io(FILE *f) {
jpeg_stdio_dest(&this->cinfo, f);
this->check();
this->outfile = f;
}
void JPEGWriter::check() {
if (setjmp(jerr.setjmp_buffer)) this->raise();
}
void JPEGWriter::raise() {
char buffer[JMSG_LENGTH_MAX];
/* Create the message */
(*this->cinfo.err->format_message) ((jpeg_common_struct *)(&this->cinfo), buffer);
jpeg_destroy_compress(&this->cinfo);
throw ReflowException(buffer);
}
void JPEGWriter::write_image(JSAMPARRAY image_buffer, JDIMENSION num) {
size_t num_written = jpeg_write_scanlines(&this->cinfo, image_buffer, num);
this->check();
if (num_written != num) {
jpeg_destroy_compress(&this->cinfo);
throw ReflowException("Failed to write all JPEG scanlines.");
}
}
void JPEGWriter::write_splash_bitmap(SplashBitmap *bitmap) {
SplashColorPtr row = bitmap->getDataPtr();
int height = bitmap->getHeight();
int row_size = bitmap->getRowSize();
JSAMPARRAY row_pointers = new JSAMPLE*[height];
for (int y = 0; y < height; ++y) {
row_pointers[y] = row;
row += row_size;
}
this->write_image(row_pointers, height);
delete[] row_pointers;
jpeg_finish_compress(&this->cinfo);
this->check();
fclose(this->outfile);
}
JPEGWriter::~JPEGWriter() {
jpeg_destroy_compress(&this->cinfo);
}

View File

@ -5,6 +5,8 @@
#include <GfxState.h>
#include <splash/SplashBitmap.h>
#include <png.h>
#include <jpeglib.h>
#include "utils.h"
using namespace std;
@ -27,11 +29,19 @@ namespace calibre_reflow {
void write_splash_bitmap(SplashBitmap *bitmap);
void close();
private:
protected:
png_structp png_ptr;
png_infop info_ptr;
};
class PNGMemWriter : public PNGWriter
{
public:
void init(vector<char> *buf, int width, int height);
};
class ImageInfo {
public:
@ -89,6 +99,32 @@ namespace calibre_reflow {
vector<string*> str() const;
void clear();
};
struct calibre_jpeg_err_mgr {
struct jpeg_error_mgr pub; /* "public" fields */
jmp_buf setjmp_buffer; /* for return to caller */
};
class JPEGWriter {
private:
FILE *outfile;
protected:
struct jpeg_compress_struct cinfo;
struct calibre_jpeg_err_mgr jerr;
void raise();
void check();
public:
JPEGWriter();
~JPEGWriter();
void init_io(FILE *f);
void init(int width, int height);
void write_image(JSAMPARRAY image_buffer, JDIMENSION number_of_scanlines);
void write_splash_bitmap(SplashBitmap *bitmap);
};
}
#endif

View File

@ -52,12 +52,14 @@ extern "C" {
info = reflow.get_info();
if (PyObject_IsTrue(cover)) {
if (!reflow.is_locked()) {
size_t size;
char *data = reflow.render_first_page(&size);
PyObject *d = PyString_FromStringAndSize(data, size);
delete[] data;
if (d == NULL) return PyErr_NoMemory();
if (PyDict_SetItemString(ans, "cover", d) == -1) return NULL;
vector<char> *data = reflow.render_first_page();
if (data->size() > 0) {
PyObject *d = PyBytes_FromStringAndSize(&((*data)[0]), data->size());
delete data;
if (d == NULL) return PyErr_NoMemory();
if (PyDict_SetItemString(ans, "cover", d) == -1) return NULL;
Py_XDECREF(d);
}
} else {
if (PyDict_SetItemString(ans, "cover", Py_None) == -1) return NULL;
}
@ -75,6 +77,7 @@ extern "C" {
PyObject *val = PyUnicode_Decode((*it).second.c_str(), (*it).second.size(), "UTF-8", "replace");
if (!val) return NULL;
if (PyDict_SetItem(ans, key, val) == -1) return NULL;
Py_XDECREF(key); Py_XDECREF(val);
}
return ans;
}
@ -180,19 +183,21 @@ int main(int argc, char **argv) {
return 1;
}
int ret = 0;
try {
Reflow reflow(memblock, size);
reflow.render();
size_t sz = 0;
char *data = reflow.render_first_page(&sz);
vector<char> *data = reflow.render_first_page();
ofstream file("cover.png", ios::binary);
file.write(data, sz);
file.write(&((*data)[0]), data->size());
delete data;
file.close();
} catch(exception &e) {
cerr << e.what() << endl;
return 1;
ret = 1;
}
return 0;
delete[] memblock;
return ret;
}
#endif

View File

@ -3,7 +3,11 @@
* License: GNU GPL v3
*/
#ifdef _WIN32
#include <poppler/Object.h>
#else
#include <Object.h>
#endif
#include <Outline.h>
#include <PDFDocEncoding.h>
#include <goo/GooList.h>
@ -683,11 +687,14 @@ void XMLOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
static char stream_pdf[15] = "stream.pdf";
class MemInStream : public MemStream {
private:
GooString stream_name;
public:
MemInStream(char *buf, size_t st, size_t sz, Object *obj) :
MemStream(buf, st, sz, obj) {}
MemStream(buf, st, sz, obj), stream_name(stream_pdf) {}
~MemInStream() {}
GooString *getFileName() { return new GooString(stream_pdf); }
GooString *getFileName() { return &this->stream_name; }
};
Reflow::Reflow(char *pdfdata, size_t sz) :
@ -861,8 +868,7 @@ string Reflow::decode_info_string(Dict *info, const char *key) const {
return oss.str();
}
char* Reflow::render_first_page(size_t *data_size,
bool use_crop_box, double x_res,
vector<char>* Reflow::render_first_page(bool use_crop_box, double x_res,
double y_res) {
if (this->is_locked()) throw ReflowException("Document is locked.");
char encoding[10] = "UTF-8";
@ -900,24 +906,14 @@ char* Reflow::render_first_page(size_t *data_size,
this->doc->displayPageSlice(out, pg, x_res, y_res, 0,
!use_crop_box, false, false, x, y, pg_w, pg_h);
FILE * f = tmpfile();
if (!f) throw ReflowException(strerror(errno));
SplashBitmap *bmp = out->getBitmap();
PNGWriter *writer = new PNGWriter();
writer->init(f, bmp->getWidth(), bmp->getHeight());
writer->write_splash_bitmap(bmp);
writer->close();
delete writer;
long size = ftell(f);
rewind(f);
char *buffer = new char[size];
*data_size = fread(buffer, 1, size, f);
if (*data_size != (size_t)size) {
throw ReflowException("I/O error reading from tmpfile");
}
return buffer;
PNGMemWriter writer;
vector<char> *buf = new vector<char>();
writer.init(buf, bmp->getWidth(), bmp->getHeight());
writer.write_splash_bitmap(bmp);
writer.close();
delete out;
return buf;
}
class MemOutStream : public OutStream {

View File

@ -68,8 +68,7 @@ class Reflow {
bool is_locked() const { return !this->doc || this->doc->isEncrypted(); }
/* Return the first page of the PDF, rendered as a PNG image */
char* render_first_page(size_t *data_size,
bool use_crop_box=true, double x_res=150.0,
vector<char>* render_first_page(bool use_crop_box=true, double x_res=150.0,
double y_res = 150.0);
/* Dump the PDF outline as the file outline.xml in the current directory */