I have revisited the pdftocairo patches that Stefan Thomas was working
on a year ago that were based on a patch I wrote back in 2009. The
original code had some limitations particularly when trying to support
all the printing options. I have rewritten pdftocairo (except for
Stefan's cmake patch which I reused). This version supports all
applicable options from both pdftoppm and pdftops. I have also added a
couple of extra options:
-icc <file> (PNG only) specify an ICC profile for the output
format. The profile will be embedded in the PNG file.
-transp (PNG only) use transparent page instead of white.
The attached patches:
1. Use stdint.h instead of assuming the type sizes for the uintN_t
types used by CairoOutputDev
2. Fix a problem with the resolution information that PNGWriter writes
to PNG files
3. Add support for extra pixel formats to PNGWriter. RGBA is required
to support the -transp option. The GRAY, and MONOCHROME formats
allow PNGWriter to store these formats more compactly than
if RGB pixels were used.
4. Add support for writing ICC profiles to PNGWriter
5. Add pdftocairo
6. Add pdftocairo manpage
7. pdftocairo cmake patch
8. use show_text_glyphs() in CairoOutputDev so that text can be
extracted from the cairo output
9. Add the poppler version to PSOutputDev output to help with
debugging pdftops issues by identifying the poppler version that
created the file.
>From e6e28cbf71bb51d505bee430b3bb46743543031b Mon Sep 17 00:00:00 2001
From: Adrian Johnson <[email protected]>
Date: Tue, 9 Aug 2011 22:05:53 +0930
Subject: [PATCH 1/9] Use stdint.h instead of assuming the size of types
---
configure.ac | 1 +
goo/gtypes.h | 20 ++++++++++++++++++++
poppler/CairoOutputDev.cc | 2 --
poppler/CairoRescaleBox.cc | 2 --
4 files changed, 21 insertions(+), 4 deletions(-)
diff --git a/configure.ac b/configure.ac
index b7af92b..8afb13b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -114,6 +114,7 @@ AC_DEFINE_DIR(POPPLER_DATADIR, "{datarootdir}/poppler", [Poppler data dir])
dnl ##### Checks for header files.
AC_PATH_XTRA
AC_HEADER_DIRENT
+AC_CHECK_HEADERS([stdint.h])
dnl ##### Switch over to C++. This will make the checks below a little
dnl ##### bit stricter (requiring function prototypes in include files).
diff --git a/goo/gtypes.h b/goo/gtypes.h
index b7a2dd2..3bc2092 100644
--- a/goo/gtypes.h
+++ b/goo/gtypes.h
@@ -24,6 +24,8 @@
#ifndef GTYPES_H
#define GTYPES_H
+#include "config.h"
+
/*
* These have stupid names to avoid conflicts with some (but not all)
* C++ compilers which define them.
@@ -45,4 +47,22 @@ typedef unsigned short Gushort;
typedef unsigned int Guint;
typedef unsigned long Gulong;
+/*
+ * Define precise integer types.
+ */
+#if HAVE_STDINT_H
+#include <stdint.h>
+#elif _MSC_VER
+typedef signed __int8 int8_t;
+typedef unsigned __int8 uint8_t;
+typedef signed __int16 int16_t;
+typedef unsigned __int16 uint16_t;
+typedef signed __int32 int32_t;
+typedef unsigned __int32 uint32_t;
+typedef signed __int64 int64_t;
+typedef unsigned __int64 uint64_t;
+#else
+#error No stdint.h types defined
+#endif
+
#endif
diff --git a/poppler/CairoOutputDev.cc b/poppler/CairoOutputDev.cc
index 477030a..6e7fd53 100644
--- a/poppler/CairoOutputDev.cc
+++ b/poppler/CairoOutputDev.cc
@@ -1343,8 +1343,6 @@ void CairoOutputDev::paintTransparencyGroup(GfxState * /*state*/, double * /*bbo
popTransparencyGroup();
}
-typedef unsigned int uint32_t;
-
static uint32_t luminocity(uint32_t x)
{
int r = (x >> 16) & 0xff;
diff --git a/poppler/CairoRescaleBox.cc b/poppler/CairoRescaleBox.cc
index dce5ddd..fea5891 100644
--- a/poppler/CairoRescaleBox.cc
+++ b/poppler/CairoRescaleBox.cc
@@ -37,8 +37,6 @@
#include "goo/gmem.h"
#include "CairoRescaleBox.h"
-typedef unsigned short int uint16_t;
-typedef unsigned int uint32_t;
/* we work in fixed point where 1. == 1 << 24 */
#define FIXED_SHIFT 24
--
1.7.4.1
>From 9b74e48f3d370309fce5d0181dd9aa4866ffccfb Mon Sep 17 00:00:00 2001
From: Adrian Johnson <[email protected]>
Date: Wed, 10 Aug 2011 18:45:24 +0930
Subject: [PATCH 2/9] png: use PNG_RESOLUTION_METER instead of PNG_RESOLUTION_UNKNOWN to set resolution
gimp does not show the correct resolution unless PNG_RESOLUTION_METER is used
---
goo/PNGWriter.cc | 3 +--
1 files changed, 1 insertions(+), 2 deletions(-)
diff --git a/goo/PNGWriter.cc b/goo/PNGWriter.cc
index bbe90f5..400c515 100644
--- a/goo/PNGWriter.cc
+++ b/goo/PNGWriter.cc
@@ -67,8 +67,7 @@ bool PNGWriter::init(FILE *f, int width, int height, int hDPI, int vDPI)
png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, color_type, interlace_type, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
- // PNG_RESOLUTION_UNKNOWN means dots per inch
- png_set_pHYs(png_ptr, info_ptr, hDPI, vDPI, PNG_RESOLUTION_UNKNOWN);
+ png_set_pHYs(png_ptr, info_ptr, hDPI/0.0254, vDPI/0.0254, PNG_RESOLUTION_METER);
png_write_info(png_ptr, info_ptr);
if (setjmp(png_jmpbuf(png_ptr))) {
--
1.7.4.1
>From 73331243f87cb90640b985c39aa7ad94984887c6 Mon Sep 17 00:00:00 2001
From: Adrian Johnson <[email protected]>
Date: Wed, 10 Aug 2011 18:48:15 +0930
Subject: [PATCH 3/9] png: Add additional pixel formats
RGBA is required for images with transparency. GRAY and MONOCHROME
allow PNGWriter write more compact PNG files when the images is known
to be all gray or monochrome.
---
goo/PNGWriter.cc | 30 ++++++++++++++++++++++++++----
goo/PNGWriter.h | 15 +++++++++++++--
2 files changed, 39 insertions(+), 6 deletions(-)
diff --git a/goo/PNGWriter.cc b/goo/PNGWriter.cc
index 400c515..1cabfc5 100644
--- a/goo/PNGWriter.cc
+++ b/goo/PNGWriter.cc
@@ -21,7 +21,7 @@
#include "poppler/Error.h"
-PNGWriter::PNGWriter()
+PNGWriter::PNGWriter(Format formatA) : format(formatA)
{
}
@@ -61,8 +61,26 @@ bool PNGWriter::init(FILE *f, int width, int height, int hDPI, int vDPI)
// Set up the type of PNG image and the compression level
png_set_compression_level(png_ptr, Z_BEST_COMPRESSION);
- png_byte bit_depth = 8;
- png_byte color_type = PNG_COLOR_TYPE_RGB;
+ png_byte bit_depth;
+ png_byte color_type;
+ switch (format) {
+ case RGB:
+ bit_depth = 8;
+ color_type = PNG_COLOR_TYPE_RGB;
+ break;
+ case RGBA:
+ bit_depth = 8;
+ color_type = PNG_COLOR_TYPE_RGB_ALPHA;
+ break;
+ case GRAY:
+ bit_depth = 8;
+ color_type = PNG_COLOR_TYPE_GRAY;
+ break;
+ case MONOCHROME:
+ bit_depth = 1;
+ color_type = PNG_COLOR_TYPE_GRAY;
+ break;
+ }
png_byte interlace_type = PNG_INTERLACE_NONE;
png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, color_type, interlace_type, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
@@ -74,7 +92,11 @@ bool PNGWriter::init(FILE *f, int width, int height, int hDPI, int vDPI)
error(-1, "error during writing png info bytes");
return false;
}
-
+
+ // pack 1 pixel/byte rows into 8 pixels/byte
+ if (format == MONOCHROME)
+ png_set_packing(png_ptr);
+
return true;
}
diff --git a/goo/PNGWriter.h b/goo/PNGWriter.h
index 0f79bf9..1d2b7ca 100644
--- a/goo/PNGWriter.h
+++ b/goo/PNGWriter.h
@@ -23,13 +23,23 @@
#include <png.h>
#include "ImgWriter.h"
+
+
class PNGWriter : public ImgWriter
{
public:
- PNGWriter();
+
+ /* RGB - 3 bytes/pixel
+ * RGBA - 4 bytes/pixel
+ * GRAY - 1 byte/pixel
+ * MONOCHROME - 1 byte/pixel. PNGWriter will bitpack to 8 pixels/byte
+ */
+ enum Format { RGB, RGBA, GRAY, MONOCHROME };
+
+ PNGWriter(Format format = RGB);
~PNGWriter();
- bool init(FILE *f, int width, int height, int hDPI, int vDPI);
+ bool init(FILE *f, int width, int height, int hDPI, int vDPI);
bool writePointers(unsigned char **rowPointers, int rowCount);
bool writeRow(unsigned char **row);
@@ -37,6 +47,7 @@ class PNGWriter : public ImgWriter
bool close();
private:
+ Format format;
png_structp png_ptr;
png_infop info_ptr;
};
--
1.7.4.1
>From 19c8984ee8383f353278e3261d7bb63878d47a28 Mon Sep 17 00:00:00 2001
From: Adrian Johnson <[email protected]>
Date: Thu, 11 Aug 2011 21:32:53 +0930
Subject: [PATCH 4/9] png: add support for embedding ICC profile
---
goo/PNGWriter.cc | 32 ++++++++++++++++++++++++++++++--
goo/PNGWriter.h | 10 +++++++++-
2 files changed, 39 insertions(+), 3 deletions(-)
diff --git a/goo/PNGWriter.cc b/goo/PNGWriter.cc
index 1cabfc5..9828a62 100644
--- a/goo/PNGWriter.cc
+++ b/goo/PNGWriter.cc
@@ -18,17 +18,40 @@
#ifdef ENABLE_LIBPNG
#include <zlib.h>
+#include <stdlib.h>
#include "poppler/Error.h"
+#include "goo/gmem.h"
PNGWriter::PNGWriter(Format formatA) : format(formatA)
{
+ icc_data = NULL;
+ icc_data_size = 0;
+ icc_name = NULL;
+ sRGB_profile = false;
}
PNGWriter::~PNGWriter()
{
- /* cleanup heap allocation */
- png_destroy_write_struct(&png_ptr, &info_ptr);
+ /* cleanup heap allocation */
+ png_destroy_write_struct(&png_ptr, &info_ptr);
+ if (icc_data) {
+ gfree(icc_data);
+ free(icc_name);
+ }
+}
+
+void PNGWriter::setICCProfile(const char *name, unsigned char *data, int size)
+{
+ icc_data = (unsigned char *)gmalloc(size);
+ memcpy(icc_data, data, size);
+ icc_data_size = size;
+ icc_name = strdup(name);
+}
+
+void PNGWriter::setSRGBProfile()
+{
+ sRGB_profile = true;
}
bool PNGWriter::init(FILE *f, int width, int height, int hDPI, int vDPI)
@@ -87,6 +110,11 @@ bool PNGWriter::init(FILE *f, int width, int height, int hDPI, int vDPI)
png_set_pHYs(png_ptr, info_ptr, hDPI/0.0254, vDPI/0.0254, PNG_RESOLUTION_METER);
+ if (icc_data)
+ png_set_iCCP(png_ptr, info_ptr, icc_name, PNG_COMPRESSION_TYPE_BASE, (char*)icc_data, icc_data_size);
+ else if (sRGB_profile)
+ png_set_sRGB(png_ptr, info_ptr, PNG_sRGB_INTENT_RELATIVE);
+
png_write_info(png_ptr, info_ptr);
if (setjmp(png_jmpbuf(png_ptr))) {
error(-1, "error during writing png info bytes");
diff --git a/goo/PNGWriter.h b/goo/PNGWriter.h
index 1d2b7ca..ba7fd1e 100644
--- a/goo/PNGWriter.h
+++ b/goo/PNGWriter.h
@@ -36,8 +36,12 @@ class PNGWriter : public ImgWriter
*/
enum Format { RGB, RGBA, GRAY, MONOCHROME };
- PNGWriter(Format format = RGB);
+ PNGWriter(Format format = RGB);
~PNGWriter();
+
+ void setICCProfile(const char *name, unsigned char *data, int size);
+ void setSRGBProfile();
+
bool init(FILE *f, int width, int height, int hDPI, int vDPI);
@@ -50,6 +54,10 @@ class PNGWriter : public ImgWriter
Format format;
png_structp png_ptr;
png_infop info_ptr;
+ unsigned char *icc_data;
+ int icc_data_size;
+ char *icc_name;
+ bool sRGB_profile;
};
#endif
--
1.7.4.1
>From 89ce7c4cf1e247eb1a2e35220b75272551e0141e Mon Sep 17 00:00:00 2001
From: Adrian Johnson <[email protected]>
Date: Thu, 11 Aug 2011 21:34:11 +0930
Subject: [PATCH 5/9] pdftocairo - utility for creating png/jpeg/ps/eps/pdf/svg using CairoOutputDev
---
utils/.gitignore | 2 +-
utils/Makefile.am | 20 +-
utils/pdftocairo.cc | 970 +++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 989 insertions(+), 3 deletions(-)
create mode 100644 utils/pdftocairo.cc
diff --git a/utils/.gitignore b/utils/.gitignore
index 696f074..d8a48f4 100644
--- a/utils/.gitignore
+++ b/utils/.gitignore
@@ -9,4 +9,4 @@ pdftohtml
pdftoppm
pdftops
pdftotext
-pdftoabw
+pdftocairo
diff --git a/utils/Makefile.am b/utils/Makefile.am
index 624045e..5193452 100644
--- a/utils/Makefile.am
+++ b/utils/Makefile.am
@@ -16,13 +16,28 @@ INCLUDES = \
-I$(top_srcdir)/utils \
-I$(top_srcdir)/poppler \
$(UTILS_CFLAGS) \
- $(FONTCONFIG_CFLAGS)
+ $(FONTCONFIG_CFLAGS) \
+ $(CAIRO_CFLAGS)
LDADD = \
$(top_builddir)/poppler/libpoppler.la \
$(UTILS_LIBS) \
$(FONTCONFIG_LIBS)
+if BUILD_CAIRO_OUTPUT
+
+pdftocairo_SOURCES = \
+ pdftocairo.cc \
+ $(common)
+
+pdftocairo_LDADD = $(LDADD) $(CAIRO_LIBS) \
+ $(top_builddir)/poppler/libpoppler-cairo.la
+
+
+pdftocairo_binary = pdftocairo
+
+endif
+
AM_LDFLAGS = @auto_import_flags@
bin_PROGRAMS = \
@@ -32,7 +47,8 @@ bin_PROGRAMS = \
pdftops \
pdftotext \
pdftohtml \
- $(pdftoppm_binary)
+ $(pdftoppm_binary) \
+ $(pdftocairo_binary)
dist_man1_MANS = \
pdffonts.1 \
diff --git a/utils/pdftocairo.cc b/utils/pdftocairo.cc
new file mode 100644
index 0000000..46cec36
--- /dev/null
+++ b/utils/pdftocairo.cc
@@ -0,0 +1,970 @@
+//========================================================================
+//
+// pdftoppm.cc
+//
+// Copyright 2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+//========================================================================
+//
+// 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) 2007 Ilmari Heikkinen <[email protected]>
+// Copyright (C) 2008 Richard Airlie <[email protected]>
+// Copyright (C) 2009 Michael K. Johnson <[email protected]>
+// Copyright (C) 2009 Shen Liang <[email protected]>
+// Copyright (C) 2009 Stefan Thomas <[email protected]>
+// Copyright (C) 2009, 2010 Albert Astals Cid <[email protected]>
+// Copyright (C) 2010, 2011 Adrian Johnson <[email protected]>
+// Copyright (C) 2010 Hib Eris <[email protected]>
+// Copyright (C) 2010 Jonathan Liu <[email protected]>
+// Copyright (C) 2010 William Bader <[email protected]>
+// Copyright (C) 2011 Thomas Freitag <[email protected]>
+//
+// 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 "config.h"
+#include <poppler-config.h>
+#include <sys/param.h> // for MAXPATHLEN
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include "parseargs.h"
+#include "goo/gmem.h"
+#include "goo/gtypes.h"
+#include "goo/GooString.h"
+#include "goo/ImgWriter.h"
+#include "goo/JpegWriter.h"
+#include "goo/PNGWriter.h"
+#include "GlobalParams.h"
+#include "Object.h"
+#include "PDFDoc.h"
+#include "PDFDocFactory.h"
+#include "CairoOutputDev.h"
+#if USE_CMS
+#include <lcms.h>
+#endif
+#include <cairo.h>
+#if CAIRO_HAS_PS_SURFACE
+#include <cairo-ps.h>
+#endif
+#if CAIRO_HAS_PDF_SURFACE
+#include <cairo-pdf.h>
+#endif
+#if CAIRO_HAS_SVG_SURFACE
+#include <cairo-svg.h>
+#endif
+
+
+static GBool png = gFalse;
+static GBool jpeg = gFalse;
+static GBool ps = gFalse;
+static GBool eps = gFalse;
+static GBool pdf = gFalse;
+static GBool svg = gFalse;
+
+static int firstPage = 1;
+static int lastPage = 0;
+static GBool printOnlyOdd = gFalse;
+static GBool printOnlyEven = gFalse;
+static GBool singleFile = gFalse;
+static double resolution = 0.0;
+static double x_resolution = 150.0;
+static double y_resolution = 150.0;
+static int scaleTo = 0;
+static int x_scaleTo = 0;
+static int y_scaleTo = 0;
+static int crop_x = 0;
+static int crop_y = 0;
+static int crop_w = 0;
+static int crop_h = 0;
+static int sz = 0;
+static GBool useCropBox = gFalse;
+static GBool mono = gFalse;
+static GBool gray = gFalse;
+static GBool transp = gFalse;
+static char icc[MAXPATHLEN] = "";
+
+static GBool level2 = gFalse;
+static GBool level3 = gFalse;
+static GBool doOrigPageSizes = gFalse;
+static char paperSize[15] = "";
+static int paperWidth = -1;
+static int paperHeight = -1;
+static GBool noCrop = gFalse;
+static GBool expand = gFalse;
+static GBool noShrink = gFalse;
+static GBool noCenter = gFalse;
+static GBool duplex = gFalse;
+
+static char ownerPassword[33] = "";
+static char userPassword[33] = "";
+static GBool quiet = gFalse;
+static GBool printVersion = gFalse;
+static GBool printHelp = gFalse;
+
+static const ArgDesc argDesc[] = {
+#if ENABLE_LIBPNG
+ {"-png", argFlag, &png, 0,
+ "generate a PNG file"},
+#endif
+#if ENABLE_LIBJPEG
+ {"-jpeg", argFlag, &jpeg, 0,
+ "generate a JPEG file"},
+#endif
+#if CAIRO_HAS_PS_SURFACE
+ {"-ps", argFlag, &ps, 0,
+ "generate PostScript file"},
+ {"-eps", argFlag, &eps, 0,
+ "generate Encapsulated PostScript (EPS)"},
+#endif
+#if CAIRO_HAS_PDF_SURFACE
+ {"-pdf", argFlag, &pdf, 0,
+ "generate a PDF file"},
+#endif
+#if CAIRO_HAS_SVG_SURFACE
+ {"-svg", argFlag, &svg, 0,
+ "generate a Scalable Vector Graphics (SVG) file"},
+#endif
+
+ {"-f", argInt, &firstPage, 0,
+ "first page to print"},
+ {"-l", argInt, &lastPage, 0,
+ "last page to print"},
+ {"-o", argFlag, &printOnlyOdd, 0,
+ "print only odd pages"},
+ {"-e", argFlag, &printOnlyEven, 0,
+ "print only even pages"},
+ {"-singlefile", argFlag, &singleFile, 0,
+ "write only the first page and do not add digits"},
+
+ {"-r", argFP, &resolution, 0,
+ "resolution, in PPI (default is 150)"},
+ {"-rx", argFP, &x_resolution, 0,
+ "X resolution, in PPI (default is 150)"},
+ {"-ry", argFP, &y_resolution, 0,
+ "Y resolution, in PPI (default is 150)"},
+ {"-scale-to", argInt, &scaleTo, 0,
+ "scales each page to fit within scale-to*scale-to pixel box"},
+ {"-scale-to-x", argInt, &x_scaleTo, 0,
+ "scales each page horizontally to fit in scale-to-x pixels"},
+ {"-scale-to-y", argInt, &y_scaleTo, 0,
+ "scales each page vertically to fit in scale-to-y pixels"},
+
+ {"-x", argInt, &crop_x, 0,
+ "x-coordinate of the crop area top left corner"},
+ {"-y", argInt, &crop_y, 0,
+ "y-coordinate of the crop area top left corner"},
+ {"-W", argInt, &crop_w, 0,
+ "width of crop area in pixels (default is 0)"},
+ {"-H", argInt, &crop_h, 0,
+ "height of crop area in pixels (default is 0)"},
+ {"-sz", argInt, &sz, 0,
+ "size of crop square in pixels (sets W and H)"},
+ {"-cropbox",argFlag, &useCropBox, 0,
+ "use the crop box rather than media box"},
+
+ {"-mono", argFlag, &mono, 0,
+ "generate a monochrome image file (PNG, JPEG)"},
+ {"-gray", argFlag, &gray, 0,
+ "generate a grayscale image file (PNG, JPEG)"},
+ {"-transp", argFlag, &transp, 0,
+ "use a transparent background instead of white (PNG)"},
+#if USE_CMS
+ {"-icc", argString, &icc, sizeof(icc),
+ "ICC color profile to use"},
+#endif
+
+ {"-level2", argFlag, &level2, 0,
+ "generate Level 2 PostScript (PS, EPS)"},
+ {"-level3", argFlag, &level3, 0,
+ "generate Level 3 PostScript (PS, EPS)"},
+ {"-origpagesizes",argFlag, &doOrigPageSizes,0,
+ "conserve original page sizes (PS, PDF, SVG)"},
+ {"-paper", argString, paperSize, sizeof(paperSize),
+ "paper size (letter, legal, A4, A3, match)"},
+ {"-paperw", argInt, &paperWidth, 0,
+ "paper width, in points"},
+ {"-paperh", argInt, &paperHeight, 0,
+ "paper height, in points"},
+ {"-nocrop", argFlag, &noCrop, 0,
+ "don't crop pages to CropBox"},
+ {"-expand", argFlag, &expand, 0,
+ "expand pages smaller than the paper size"},
+ {"-noshrink", argFlag, &noShrink, 0,
+ "don't shrink pages larger than the paper size"},
+ {"-nocenter", argFlag, &noCenter, 0,
+ "don't center pages smaller than the paper size"},
+ {"-duplex", argFlag, &duplex, 0,
+ "enable duplex printing"},
+
+ {"-opw", argString, ownerPassword, sizeof(ownerPassword),
+ "owner password (for encrypted files)"},
+ {"-upw", argString, userPassword, sizeof(userPassword),
+ "user password (for encrypted files)"},
+
+ {"-q", argFlag, &quiet, 0,
+ "don't print any messages or errors"},
+ {"-v", argFlag, &printVersion, 0,
+ "print copyright and version info"},
+ {"-h", argFlag, &printHelp, 0,
+ "print usage information"},
+ {"-help", argFlag, &printHelp, 0,
+ "print usage information"},
+ {"--help", argFlag, &printHelp, 0,
+ "print usage information"},
+ {"-?", argFlag, &printHelp, 0,
+ "print usage information"},
+ {NULL}
+};
+
+
+static cairo_surface_t *surface;
+static GBool printing;
+
+#if USE_CMS
+static unsigned char *icc_data;
+static int icc_data_size;
+static cmsHPROFILE profile;
+#endif
+
+
+void writePageImage(GooString *filename)
+{
+ ImgWriter *writer = 0;
+ FILE *file;
+ int height, width, stride;
+ unsigned char *data;
+
+ if (png) {
+#if ENABLE_LIBPNG
+ if (transp)
+ writer = new PNGWriter(PNGWriter::RGBA);
+ else if (gray)
+ writer = new PNGWriter(PNGWriter::GRAY);
+ else if (mono)
+ writer = new PNGWriter(PNGWriter::MONOCHROME);
+ else
+ writer = new PNGWriter(PNGWriter::RGB);
+
+#if USE_CMS
+ if (icc_data)
+ static_cast<PNGWriter*>(writer)->setICCProfile(cmsTakeProductName(profile), icc_data, icc_data_size);
+ else
+ static_cast<PNGWriter*>(writer)->setSRGBProfile();
+#endif
+#endif
+
+ } else if (jpeg) {
+#if ENABLE_LIBJPEG
+ if (gray)
+ writer = new JpegWriter(JCS_GRAYSCALE);
+ else
+ writer = new JpegWriter(JCS_RGB);
+#endif
+ }
+ if (!writer)
+ return;
+
+ if (filename->cmp("fd://0") == 0)
+ file = stdout;
+ else
+ file = fopen(filename->getCString(), "wb");
+
+ if (!file) {
+ fprintf(stderr, "Error opening output file %s\n", filename->getCString());
+ exit(2);
+ }
+
+ height = cairo_image_surface_get_height(surface);
+ width = cairo_image_surface_get_width(surface);
+ stride = cairo_image_surface_get_stride(surface);
+ data = cairo_image_surface_get_data(surface);
+
+ if (!writer->init(file, width, height, x_resolution, y_resolution)) {
+ fprintf(stderr, "Error writing %s\n", filename->getCString());
+ exit(2);
+ }
+ unsigned char *row = (unsigned char *) gmallocn(width, 4);
+
+ for (int y = 0; y < height; y++ ) {
+ uint32_t *pixel = (uint32_t *) (data + y*stride);
+ unsigned char *rowp = row;
+ for (int x = 0; x < width; x++, pixel++) {
+ if (transp) {
+ // unpremultiply into RGBA format
+ uint8_t a;
+ a = (*pixel & 0xff000000) >> 24;
+ if (a == 0) {
+ *rowp++ = 0;
+ *rowp++ = 0;
+ *rowp++ = 0;
+ } else {
+ *rowp++ = (((*pixel & 0xff0000) >> 16) * 255 + a / 2) / a;
+ *rowp++ = (((*pixel & 0x00ff00) >> 8) * 255 + a / 2) / a;
+ *rowp++ = (((*pixel & 0x0000ff) >> 0) * 255 + a / 2) / a;
+ }
+ *rowp++ = a;
+ } else if (gray || mono) {
+ // convert to gray
+ // The PDF Reference specifies the DeviceRGB to DeviceGray conversion as
+ // gray = 0.3*red + 0.59*green + 0.11*blue
+ int r = (*pixel & 0x00ff0000) >> 16;
+ int g = (*pixel & 0x0000ff00) >> 8;
+ int b = (*pixel & 0x000000ff) >> 0;
+ // an arbitrary integer approximation of .3*r + .59*g + .11*b
+ int y = (r*19661+g*38666+b*7209 + 32829)>>16;
+ *rowp++ = y;
+ } else {
+ // copy into RGB format
+ *rowp++ = (*pixel & 0x00ff0000) >> 16;
+ *rowp++ = (*pixel & 0x0000ff00) >> 8;
+ *rowp++ = (*pixel & 0x000000ff) >> 0;
+ }
+ }
+ writer->writeRow(&row);
+ }
+ gfree(row);
+ writer->close();
+ delete writer;
+}
+
+static void getCropSize(double page_w, double page_h, double *width, double *height)
+{
+ int w = crop_w;
+ int h = crop_h;
+
+ if (w == 0)
+ w = (int)ceil(page_w);
+
+ if (h == 0)
+ h = (int)ceil(page_h);
+
+ *width = (crop_x + w > page_w ? (int)ceil(page_w - crop_x) : w);
+ *height = (crop_y + h > page_h ? (int)ceil(page_h - crop_y) : h);
+}
+
+static void getOutputSize(double page_w, double page_h, double *width, double *height)
+{
+
+ if (printing) {
+ if (doOrigPageSizes) {
+ *width = page_w;
+ *height = page_h;
+ } else {
+ *width = paperWidth;
+ *height = paperHeight;
+ }
+ } else {
+ getCropSize(page_w * (x_resolution / 72.0),
+ page_h * (y_resolution / 72.0),
+ width, height);
+ }
+}
+
+static void getFitToPageTransform(double page_w, double page_h,
+ double paper_w, double paper_h,
+ cairo_matrix_t *m)
+{
+ double x_scale, y_scale, scale;
+
+ x_scale = paper_w / page_w;
+ y_scale = paper_h / page_h;
+ if (x_scale < y_scale)
+ scale = x_scale;
+ else
+ scale = y_scale;
+
+ cairo_matrix_init_identity (m);
+ if (scale > 1.0) {
+ // page is smaller than paper
+ if (expand) {
+ // expand to fit
+ cairo_matrix_scale (m, scale, scale);
+ } else if (!noCenter) {
+ // centre page
+ cairo_matrix_translate (m, (paper_w - page_w)/2, (paper_h - page_h)/2);
+ } else {
+ if (!svg) {
+ // move to PostScript origin
+ cairo_matrix_translate (m, 0, (paper_h - page_h));
+ }
+ }
+ } else if (scale < 1.0)
+ // page is larger than paper
+ if (!noShrink) {
+ // shrink to fit
+ cairo_matrix_scale (m, scale, scale);
+ }
+}
+
+static void beginDocument(GooString *outputFileName, double w, double h)
+{
+ if (printing) {
+ if (ps || eps) {
+#if CAIRO_HAS_PS_SURFACE
+ surface = cairo_ps_surface_create(outputFileName->getCString(), w, h);
+ if (level2)
+ cairo_ps_surface_restrict_to_level (surface, CAIRO_PS_LEVEL_2);
+ if (eps)
+ cairo_ps_surface_set_eps (surface, 1);
+ if (duplex) {
+ cairo_ps_surface_dsc_comment(surface, "%%Requirements: duplex");
+ cairo_ps_surface_dsc_begin_setup(surface);
+ cairo_ps_surface_dsc_comment(surface, "%%IncludeFeature: *Duplex DuplexNoTumble");
+ }
+ cairo_ps_surface_dsc_begin_page_setup (surface);
+#endif
+ } else if (pdf) {
+#if CAIRO_HAS_PDF_SURFACE
+ surface = cairo_pdf_surface_create(outputFileName->getCString(), w, h);
+#endif
+ } else if (svg) {
+#if CAIRO_HAS_SVG_SURFACE
+ surface = cairo_svg_surface_create(outputFileName->getCString(), w, h);
+ cairo_svg_surface_restrict_to_version (surface, CAIRO_SVG_VERSION_1_2);
+#endif
+ }
+ }
+}
+
+static void beginPage(double w, double h)
+{
+ if (printing) {
+ if (ps || eps) {
+#if CAIRO_HAS_PS_SURFACE
+ if (w > h) {
+ cairo_ps_surface_dsc_comment (surface, "%%PageOrientation: Landscape");
+ cairo_ps_surface_set_size (surface, h, w);
+ } else {
+ cairo_ps_surface_dsc_comment (surface, "%%PageOrientation: Portrait");
+ cairo_ps_surface_set_size (surface, w, h);
+ }
+#endif
+ }
+
+#if CAIRO_HAS_PDF_SURFACE
+ if (pdf)
+ cairo_pdf_surface_set_size (surface, w, h);
+#endif
+
+ cairo_surface_set_fallback_resolution (surface, x_resolution, y_resolution);
+
+ } else {
+ surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, ceil(w), ceil(h));
+ }
+}
+
+static void renderPage(PDFDoc *doc, CairoOutputDev *cairoOut, int pg,
+ double page_w, double page_h,
+ double output_w, double output_h)
+{
+ cairo_t *cr;
+ cairo_status_t status;
+ cairo_matrix_t m;
+
+ cr = cairo_create(surface);
+ cairoOut->setCairo(cr);
+ cairoOut->setPrinting(printing);
+
+ cairo_save(cr);
+ if (ps && output_w > output_h) {
+ // rotate 90 deg for landscape
+ cairo_translate (cr, 0, output_w);
+ cairo_matrix_init (&m, 0, -1, 1, 0, 0, 0);
+ cairo_transform (cr, &m);
+ }
+ cairo_translate (cr, -crop_x, -crop_y);
+ if (printing) {
+ double cropped_w, cropped_h;
+ getCropSize(page_w, page_h, &cropped_w, &cropped_h);
+ getFitToPageTransform(cropped_w, cropped_h, output_w, output_h, &m);
+ cairo_transform (cr, &m);
+ cairo_rectangle(cr, crop_x, crop_y, cropped_w, cropped_h);
+ cairo_clip(cr);
+ } else {
+ cairo_scale (cr, x_resolution/72.0, y_resolution/72.0);
+ }
+ doc->displayPageSlice(cairoOut,
+ pg,
+ 72.0, 72.0,
+ 0, /* rotate */
+ !useCropBox, /* useMediaBox */
+ gFalse, /* Crop */
+ printing,
+ -1, -1, -1, -1);
+ cairo_restore(cr);
+ cairoOut->setCairo(NULL);
+
+ // Blend onto white page
+ if (!printing && !transp) {
+ cairo_save(cr);
+ cairo_set_operator(cr, CAIRO_OPERATOR_DEST_OVER);
+ cairo_set_source_rgb(cr, 1, 1, 1);
+ cairo_paint(cr);
+ cairo_restore(cr);
+ }
+
+ status = cairo_status(cr);
+ if (status)
+ error(-1, "cairo error: %s\n", cairo_status_to_string(status));
+ cairo_destroy (cr);
+}
+
+static void endPage(GooString *imageFileName)
+{
+ cairo_status_t status;
+
+ if (printing) {
+ cairo_surface_show_page(surface);
+ } else {
+ writePageImage(imageFileName);
+ cairo_surface_finish(surface);
+ status = cairo_surface_status(surface);
+ if (status)
+ error(-1, "cairo error: %s\n", cairo_status_to_string(status));
+ cairo_surface_destroy(surface);
+ }
+
+}
+
+static void endDocument()
+{
+ cairo_status_t status;
+
+ if (printing) {
+ cairo_surface_finish(surface);
+ status = cairo_surface_status(surface);
+ if (status)
+ error(-1, "cairo error: %s\n", cairo_status_to_string(status));
+ cairo_surface_destroy(surface);
+ }
+}
+
+static GBool setPSPaperSize(char *size, int &psPaperWidth, int &psPaperHeight) {
+ if (!strcmp(size, "match")) {
+ psPaperWidth = psPaperHeight = -1;
+ } else if (!strcmp(size, "letter")) {
+ psPaperWidth = 612;
+ psPaperHeight = 792;
+ } else if (!strcmp(size, "legal")) {
+ psPaperWidth = 612;
+ psPaperHeight = 1008;
+ } else if (!strcmp(size, "A4")) {
+ psPaperWidth = 595;
+ psPaperHeight = 842;
+ } else if (!strcmp(size, "A3")) {
+ psPaperWidth = 842;
+ psPaperHeight = 1190;
+ } else {
+ return gFalse;
+ }
+ return gTrue;
+}
+
+static int numberOfCharacters(unsigned int n)
+{
+ int charNum = 0;
+ while (n >= 10)
+ {
+ n = n / 10;
+ charNum++;
+ }
+ charNum++;
+ return charNum;
+}
+
+static GooString *getImageFileName(GooString *outputFileName, int numDigits, int page)
+{
+ char buf[10];
+ GooString *imageName = new GooString(outputFileName);
+ if (!singleFile) {
+ snprintf(buf, sizeof(buf), "-%0*d", numDigits, page);
+ imageName->appendf(buf);
+ }
+ if (png)
+ imageName->append(".png");
+ else if (jpeg)
+ imageName->append(".jpg");
+
+ return imageName;
+}
+
+// If (printing || singleFile) the output file name includes the
+// extension. Otherwise it is the file name base.
+static GooString *getOutputFileName(GooString *fileName, GooString *outputName)
+{
+ GooString *name;
+ char *s;
+ char *p;
+
+ if (outputName) {
+ if (outputName->cmp("-") == 0) {
+ if (!printing && !singleFile) {
+ fprintf(stderr, "Error: stdout may only be used with the ps, eps, pdf, svg output options or if -singlefile is used.\n");
+ exit(99);
+ }
+ return new GooString("fd://0");
+ }
+ return new GooString(outputName);
+ }
+
+ if (fileName->cmp("fd://0") == 0) {
+ fprintf(stderr, "Error: an output filename or '-' must be supplied when the PDF file is stdin.\n");
+ exit(99);
+ }
+
+ // be careful not to overwrite the input file when the output format is PDF
+ if (pdf && fileName->cmpN("http://", 7) != 0 && fileName->cmpN("https://", 8) != 0) {
+ fprintf(stderr, "Error: an output filename or '-' must be supplied when the output format is PDF and input PDF file is a local file.\n");
+ exit(99);
+ }
+
+ // strip everything up to last '/'
+ s = fileName->getCString();
+ p = strrchr(s, '/');
+ if (p) {
+ p++;
+ if (*p == 0) {
+ fprintf(stderr, "Error: invalid output filename.\n");
+ exit(99);
+ }
+ name = new GooString(p);
+ } else {
+ name = new GooString(s);
+ }
+
+ // remove .pdf extension
+ p = strrchr(name->getCString(), '.');
+ if (p && strcasecmp(p, ".pdf") == 0) {
+ GooString *name2 = new GooString(name->getCString(), name->getLength() - 4);
+ delete name;
+ name = name2;
+ }
+
+ // append new extension
+ if (ps)
+ name->append(".ps");
+ else if (eps)
+ name->append(".eps");
+ else if (pdf)
+ name->append(".pdf");
+ else if (svg)
+ name->append(".svg");
+
+ return name;
+}
+
+static void checkInvalidPrintOption(GBool option, char *option_name)
+{
+ if (option) {
+ fprintf(stderr, "Error: %s may only be used with the -png or -jpeg output options.\n", option_name);
+ exit(99);
+ }
+}
+
+static void checkInvalidImageOption(GBool option, char *option_name)
+{
+ if (option) {
+ fprintf(stderr, "Error: %s may only be used with the -ps, -eps, -pdf, or -svg output options.\n", option_name);
+ exit(99);
+ }
+}
+
+int main(int argc, char *argv[]) {
+ PDFDoc *doc;
+ GooString *fileName = NULL;
+ GooString *outputName = NULL;
+ GooString *outputFileName = NULL;
+ GooString *imageFileName = NULL;
+ GooString *ownerPW, *userPW;
+ CairoOutputDev *cairoOut;
+ int pg, pg_num_len;
+ double pg_w, pg_h, tmp, output_w, output_h;
+ int num_outputs;
+
+ // parse args
+ if (!parseArgs(argDesc, &argc, argv))
+ exit(99);
+
+ if ( resolution != 0.0 &&
+ (x_resolution == 150.0 ||
+ y_resolution == 150.0)) {
+ x_resolution = resolution;
+ y_resolution = resolution;
+ }
+ if (argc < 2 || argc > 3 || printVersion || printHelp) {
+ fprintf(stderr, "pdftocairo version %s\n", PACKAGE_VERSION);
+ fprintf(stderr, "%s\n", popplerCopyright);
+ fprintf(stderr, "%s\n", xpdfCopyright);
+ if (!printVersion) {
+ printUsage("pdftocairo", "<PDF-file> [<output-file>]", argDesc);
+ }
+ if (printVersion || printHelp)
+ exit(0);
+ else
+ exit(99);
+ }
+
+ num_outputs = (png ? 1 : 0) +
+ (jpeg ? 1 : 0) +
+ (ps ? 1 : 0) +
+ (eps ? 1 : 0) +
+ (pdf ? 1 : 0) +
+ (svg ? 1 : 0);
+ if (num_outputs == 0) {
+ fprintf(stderr, "Error: one of the output format options (-png, -jpeg, -ps, -eps, -pdf, -svg) must be used.\n");
+ exit(99);
+ }
+ if (num_outputs > 1) {
+ fprintf(stderr, "Error: use only one of the output format options (-png, -jpeg, -ps, -eps, -pdf, -svg).\n");
+ exit(99);
+ }
+ if (png || jpeg)
+ printing = gFalse;
+ else
+ printing = gTrue;
+
+ if (printing) {
+ checkInvalidPrintOption(mono, "-mono");
+ checkInvalidPrintOption(gray, "-gray");
+ checkInvalidPrintOption(transp, "-transp");
+ checkInvalidPrintOption(icc[0], "-icc");
+ checkInvalidPrintOption(singleFile, "-singlefile");
+ } else {
+ checkInvalidImageOption(level2, "-level2");
+ checkInvalidImageOption(level3, "-level3");
+ checkInvalidImageOption(doOrigPageSizes, "-origpagesizes");
+ checkInvalidImageOption(paperSize[0], "-paper");
+ checkInvalidImageOption(paperWidth > 0, "-paperw");
+ checkInvalidImageOption(paperHeight > 0, "-paperh");
+ checkInvalidImageOption(noCrop, "-nocrop");
+ checkInvalidImageOption(expand, "-expand");
+ checkInvalidImageOption(noShrink, "-noshrink");
+ checkInvalidImageOption(noCenter, "-nocenter");
+ checkInvalidImageOption(duplex, "-duplex");
+ }
+
+ if (icc[0] && !png) {
+ fprintf(stderr, "Error: -icc may only be used with png output.\n");
+ exit(99);
+ }
+
+ if (transp && !png) {
+ fprintf(stderr, "Error: -transp may only be used with png output.\n");
+ exit(99);
+ }
+
+ if (mono && gray) {
+ fprintf(stderr, "Error: -mono and -gray may not be used together.\n");
+ exit(99);
+ }
+
+ if (mono && !png) {
+ fprintf(stderr, "Error: -mono may only be used with png output.\n");
+ exit(99);
+ }
+
+ if (level2 && level3) {
+ fprintf(stderr, "Error: use only one of the 'level' options.\n");
+ exit(99);
+ }
+ if (!level2 && !level3)
+ level3 = gTrue;
+
+ if (eps && (doOrigPageSizes || paperSize[0] || paperWidth > 0 || paperHeight > 0)) {
+ fprintf(stderr, "Error: page size options may not be used with eps output.\n");
+ exit(99);
+ }
+
+ if (paperSize[0]) {
+ if (!setPSPaperSize(paperSize, paperWidth, paperHeight)) {
+ fprintf(stderr, "Invalid paper size\n");
+ exit(99);
+ }
+ }
+
+ globalParams = new GlobalParams();
+ if (quiet) {
+ globalParams->setErrQuiet(quiet);
+ }
+
+ // open PDF file
+ if (ownerPassword[0]) {
+ ownerPW = new GooString(ownerPassword);
+ } else {
+ ownerPW = NULL;
+ }
+ if (userPassword[0]) {
+ userPW = new GooString(userPassword);
+ } else {
+ userPW = NULL;
+ }
+
+ fileName = new GooString(argv[1]);
+ if (fileName->cmp("-") == 0) {
+ delete fileName;
+ fileName = new GooString("fd://0");
+ }
+ if (argc == 3)
+ outputName = new GooString(argv[2]);
+ else
+ outputName = NULL;
+
+ outputFileName = getOutputFileName(fileName, outputName);
+
+#if USE_CMS
+ icc_data = NULL;
+ if (icc[0]) {
+ FILE *file = fopen(icc, "rb");
+ if (!file) {
+ fprintf(stderr, "Error: unable to open icc profile %s\n", icc);
+ exit(4);
+ }
+ fseek (file, 0, SEEK_END);
+ icc_data_size = ftell(file);
+ fseek (file, 0, SEEK_SET);
+ icc_data = (unsigned char*)gmalloc(icc_data_size);
+ if (fread(icc_data, icc_data_size, 1, file) != 1) {
+ fprintf(stderr, "Error: unable to read icc profile %s\n", icc);
+ exit(4);
+ }
+ fclose(file);
+ profile = cmsOpenProfileFromMem(icc_data, icc_data_size);
+ if (!profile) {
+ fprintf(stderr, "Error: lcms error opening profile\n");
+ exit(4);
+ }
+ } else {
+ profile = cmsCreate_sRGBProfile();
+ }
+ GfxColorSpace::setDisplayProfile(profile);
+#endif
+
+ doc = PDFDocFactory().createPDFDoc(*fileName, ownerPW, userPW);
+ if (!doc->isOk()) {
+ fprintf(stderr, "Error opening PDF file.\n");
+ exit(1);
+ }
+
+#ifdef ENFORCE_PERMISSIONS
+ // check for print permission
+ if (printing && !doc->okToPrint()) {
+ fprintf(stderr, "Printing this document is not allowed.\n");
+ exit(3);
+ }
+#endif
+
+ // get page range
+ if (firstPage < 1)
+ firstPage = 1;
+ if (singleFile && lastPage < 1)
+ lastPage = firstPage;
+ if (lastPage < 1 || lastPage > doc->getNumPages())
+ lastPage = doc->getNumPages();
+
+ if (eps && firstPage != lastPage) {
+ fprintf(stderr, "EPS files can only contain one page.\n");
+ exit(99);
+ }
+
+ if (singleFile && firstPage < lastPage) {
+ if (!quiet) {
+ fprintf(stderr,
+ "Warning: Single file will write only the first of the %d pages.\n",
+ lastPage + 1 - firstPage);
+ }
+ lastPage = firstPage;
+ }
+
+ cairoOut = new CairoOutputDev();
+ cairoOut->startDoc(doc->getXRef(), doc->getCatalog());
+ if (sz != 0)
+ crop_w = crop_h = sz;
+ pg_num_len = numberOfCharacters(doc->getNumPages());
+ for (pg = firstPage; pg <= lastPage; ++pg) {
+ if (printOnlyEven && pg % 2 == 0) continue;
+ if (printOnlyOdd && pg % 2 == 1) continue;
+ if (useCropBox) {
+ pg_w = doc->getPageCropWidth(pg);
+ pg_h = doc->getPageCropHeight(pg);
+ } else {
+ pg_w = doc->getPageMediaWidth(pg);
+ pg_h = doc->getPageMediaHeight(pg);
+ }
+
+ if (printing && pg == firstPage) {
+ if (paperWidth < 0 || paperHeight < 0) {
+ paperWidth = (int)ceil(pg_w);
+ paperHeight = (int)ceil(pg_h);
+ }
+ }
+
+ if (scaleTo != 0) {
+ resolution = (72.0 * scaleTo) / (pg_w > pg_h ? pg_w : pg_h);
+ x_resolution = y_resolution = resolution;
+ } else {
+ if (x_scaleTo != 0) {
+ x_resolution = (72.0 * x_scaleTo) / pg_w;
+ }
+ if (y_scaleTo != 0) {
+ y_resolution = (72.0 * y_scaleTo) / pg_h;
+ }
+ }
+ if ((doc->getPageRotate(pg) == 90) || (doc->getPageRotate(pg) == 270)) {
+ tmp = pg_w;
+ pg_w = pg_h;
+ pg_h = tmp;
+ }
+ if (imageFileName) {
+ delete imageFileName;
+ imageFileName = NULL;
+ }
+ if (!printing)
+ imageFileName = getImageFileName(outputFileName, pg_num_len, pg);
+ getOutputSize(pg_w, pg_h, &output_w, &output_h);
+
+ if (pg == firstPage)
+ beginDocument(outputFileName, output_w, output_h);
+ beginPage(output_w, output_h);
+ renderPage(doc, cairoOut, pg, pg_w, pg_h, output_w, output_h);
+ endPage(imageFileName);
+ }
+ endDocument();
+
+ // clean up
+ delete cairoOut;
+ delete doc;
+ delete globalParams;
+ if (fileName)
+ delete fileName;
+ if (outputName)
+ delete outputName;
+ if (outputFileName)
+ delete outputFileName;
+ if (imageFileName)
+ delete imageFileName;
+ if (ownerPW)
+ delete ownerPW;
+ if (userPW)
+ delete ownerPW;
+
+#if USE_CMS
+ cmsCloseProfile(profile);
+ if (icc_data)
+ gfree(icc_data);
+#endif
+
+ // check for memory leaks
+ Object::memCheck(stderr);
+ gMemReport(stderr);
+
+ return 0;
+}
--
1.7.4.1
>From c785047015cfe5239c38ed63ec548fd7cf7ad945 Mon Sep 17 00:00:00 2001
From: Adrian Johnson <[email protected]>
Date: Fri, 12 Aug 2011 23:57:01 +0930
Subject: [PATCH 6/9] Add pdftocairo man page
---
utils/Makefile.am | 5 +-
utils/pdftocairo.1 | 254 ++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 258 insertions(+), 1 deletions(-)
create mode 100644 utils/pdftocairo.1
diff --git a/utils/Makefile.am b/utils/Makefile.am
index 5193452..1ff2fda 100644
--- a/utils/Makefile.am
+++ b/utils/Makefile.am
@@ -36,6 +36,8 @@ pdftocairo_LDADD = $(LDADD) $(CAIRO_LIBS) \
pdftocairo_binary = pdftocairo
+pdftocairo_manpage = pdftocairo.1
+
endif
AM_LDFLAGS = @auto_import_flags@
@@ -57,7 +59,8 @@ dist_man1_MANS = \
pdftops.1 \
pdftotext.1 \
pdftohtml.1 \
- $(pdftoppm_manpage)
+ $(pdftoppm_manpage) \
+ $(pdftocairo_manpage)
common = parseargs.cc parseargs.h
diff --git a/utils/pdftocairo.1 b/utils/pdftocairo.1
new file mode 100644
index 0000000..6c647ec
--- /dev/null
+++ b/utils/pdftocairo.1
@@ -0,0 +1,254 @@
+.TH pdftoppm 1
+.SH NAME
+pdftocairo \- Portable Document Format (PDF) to PNG/JPEG/PDF/PS/EPS/SVG using cairo
+.SH SYNOPSIS
+.B pdftocairo
+[options]
+.IR PDF-file
+.RI [ output-file ]
+.SH DESCRIPTION
+.B pdftocairo
+converts Portable Document Format (PDF) files, using the cairo output device of the poppler PDF library, to any of the following output formats:
+.IP \(bu
+Portable Network Graphics (PNG)
+.IP \(bu
+JPEG Interchange Format (JPEG)
+.IP \(bu
+Portable Document Format (PDF)
+.IP \(bu
+PostScript (PS)
+.IP \(bu
+Encapsulated PostScript (EPS)
+.IP \(bu
+Scalable Vector Graphics (SVG)
+.PP
+.B pdftocairo
+reads the PDF file,
+.IR PDF-file ,
+and writes to
+.IR output-file .
+The image formats (PNG and JPEG) generate one file per page with the page number and file type appended to
+.IR output-file
+(except when -singlefile is used).
+When the output format is a vector format (PDF, PS, EPS, and SVG) or when \-singlefile is used,
+.IR output-file
+is the full filename.
+
+If the
+.IR PDF-file
+is \*(lq\-\*(rq , the PDF is read from stdin.
+If the
+.IR output-file
+is \*(lq\-\*(rq , the output file will be written to stdout. Using stdout is not valid with image formats unless \-singlefile is used.
+If
+.IR output-file
+is not used, the output filename will be derived from the
+.IR PDF-file
+filename.
+.PP
+Not all options are valid with all output formats. One (and only one) of the output format options (\-png, \-jpeg, \-pdf, \-ps, \-eps, or \-svg) must be used.
+.PP
+The resolution options (\-r, \-rx, \-ry) set the resolution of the
+image output formats. The image dimensions will depend on the PDF page
+size and the resolution. For the vector outputs, regions of the page
+that can not be represented natively in the output format (eg
+translucency in PS) will be rasterized at the resolution specified by
+the resolution options.
+.PP
+The \-scale-to options may be used to set a fixed image size. The
+image resolution will vary with the page size.
+.PP
+The cropping options (\-x, \-y, \-W, and \-H) use units of pixels with
+the image formats and PostScript points (1/72 inch) with the vector
+formats. When cropping is used with vector output the cropped region is
+centered unless -nocenter is used in which case the cropped region is
+at the top left (SVG) or bottom left (PDF, PS, EPS).
+.PP
+.SH OPTIONS
+.TP
+.BI \-png
+Generates a PNG file(s)
+.TP
+.BI \-jpeg
+Generates a JPEG file(s)
+.TP
+.BI \-pdf
+Generates a PDF file
+.TP
+.BI \-ps
+Generate a PS file
+.TP
+.BI \-eps
+Generate an EPS file. An EPS file contains a single image, so if you
+use this option with a multi-page PDF file, you must use \-f and \-l
+to specify a single page. The page size options (\-origpagesizes,
+\-paper, \-paperw, \-paperh) can not be used with this option.
+.TP
+.BI \-svg
+Generate a SVG (Scalable Vector Graphics) file
+.TP
+.BI \-f " number"
+Specifies the first page to convert.
+.TP
+.BI \-l " number"
+Specifies the last page to convert.
+.TP
+.B \-o
+Generates only the odd numbered pages.
+.TP
+.B \-e
+Generates only the even numbered pages.
+.TP
+.BI \-singlefile
+Writes only the first page and does not add digits.
+.TP
+.BI \-r " number"
+Specifies the X and Y resolution, in pixels per inch of image files (or rasterized regions in vector output). The default is 150 PPI.
+.TP
+.BI \-rx " number"
+Specifies the X resolution, in pixels per inch of image files (or rasterized regions in vector output). The default is 150 PPI.
+.TP
+.BI \-ry " number"
+Specifies the Y resolution, in pixels per inch of image files (or rasterized regions in vector output). The default is 150 PPI.
+.TP
+.BI \-scale-to " number"
+Scales each page to fit in scale-to*scale-to pixel box (PNG/JPEG only).
+.TP
+.BI \-scale-to-x " number"
+Scales each page horizontally to fit in scale-to-x pixels (PNG/JPEG only).
+.TP
+.BI \-scale-to-y " number"
+Scales each page vertically to fit in scale-to-y pixels (PNG/JPEG only).
+.TP
+.BI \-x " number"
+Specifies the x-coordinate of the crop area top left corner in pixels (image output) or points (vector output)
+.TP
+.BI \-y " number"
+Specifies the y-coordinate of the crop area top left corner in pixels (image output) or points (vector output)
+.TP
+.BI \-W " number"
+Specifies the width of crop area in pixels (image output) or points (vector output) (default is 0)
+.TP
+.BI \-H " number"
+Specifies the height of crop area in pixels (image output) or points (vector output) (default is 0)
+.TP
+.BI \-sz " number"
+Specifies the size of crop square in pixels (image output) or points (vector output) (sets \-W and \-H)
+.TP
+.B \-cropbox
+Uses the crop box rather than media box when generating the files
+.TP
+.B \-mono
+Generate a monochrome file (PNG only).
+.TP
+.B \-gray
+Generate a grayscale file (PNG and JPEG only).
+.TP
+.B \-transp
+Use a transparent page color instead of white (PNG only).
+.TP
+.BI \-icc " icc-file"
+Use the specified ICC file as the output profile (PNG only). The profile will be embedded in the PNG file.
+.TP
+.B \-level2
+Generate Level 2 PostScript (PS only).
+.TP
+.B \-level3
+Generate Level 3 PostScript (PS only). This enables all Level 2 features plus
+shading patterns and masked images. This is the default setting.
+.TP
+.B \-origpagesizes
+Generate a file with variable page sizes and orientations (PS and PDF
+only). The size of each page will be the original page in the PDF
+file. If the output is PS the file will contain %%DocumentMedia and
+%%PageMedia DSC comments specifying the size of each page. Any
+specification of the page size via \-paper, \-paperw, or \-paperh will
+get overridden as long as each page of the PDF file has a defined
+paper size.
+.TP
+.BI \-paper " size"
+Set the paper size to one of "letter", "legal", "A4", or "A3"
+(PS,PDF,SVG only). This can also be set to "match", which will set
+the paper size to match the size specified in the PDF
+file. \-origpagesizes overrides this setting if the PDF file has
+defined page sizes.
+.TP
+.BI \-paperw " size"
+Set the paper width, in points (PS,PDF,SVG only). \-origpagesizes overrides this setting
+if the PDF file has defined page sizes.
+.TP
+.BI \-paperh " size"
+Set the paper height, in points (PS,PDF,SVG only). \-origpagesizes overrides this setting
+if the PDF file has defined page sizes.
+.TP
+.B \-nocrop
+By default, output is cropped to the CropBox specified in the PDF
+file. This option disables cropping.
+.TP
+.B \-expand
+Expand PDF pages smaller than the paper to fill the paper (PS,PDF,SVG only). By
+default, these pages are not scaled.
+.TP
+.B \-noshrink
+Don't scale PDF pages which are larger than the paper (PS,PDF,SVG only). By default,
+pages larger than the paper are shrunk to fit.
+.TP
+.B \-nocenter
+By default, PDF pages smaller than the paper (after any scaling) are
+centered on the paper. This option causes them to be aligned to the
+lower-left corner of the paper instead (PS,PDF,SVG only).
+.TP
+.B \-duplex
+Adds the %%IncludeFeature: *Duplex DuplexNoTumble DSC comment to the
+PostScript file (PS only). This tells the print manager to enable duplexing.
+.TP
+.BI \-opw " password"
+Specify the owner password for the PDF file. Providing this will
+bypass all security restrictions.
+.TP
+.BI \-upw " password"
+Specify the user password for the PDF file.
+.TP
+.B \-q
+Don't print any messages or errors.
+.TP
+.B \-v
+Print copyright and version information.
+.TP
+.B \-h
+Print usage information.
+.RB ( \-help
+and
+.B \-\-help
+are equivalent.)
+.SH EXIT CODES
+The poppler tools use the following exit codes:
+.TP
+0
+No error.
+.TP
+1
+Error opening a PDF file.
+.TP
+2
+Error opening an output file.
+.TP
+3
+Error related to PDF permissions.
+.TP
+4
+Error related to ICC profile.
+.TP
+99
+Other error.
+.SH AUTHOR
+The pdftocairo software and documentation are copyright 1996-2004 Glyph
+& Cog, LLC and copyright 2005-2011 The Poppler Developers.
+.SH "SEE ALSO"
+.BR pdffonts (1),
+.BR pdfimages (1),
+.BR pdfinfo (1),
+.BR pdftohtml (1),
+.BR pdftoppm (1),
+.BR pdftops (1),
+.BR pdftotext (1),
--
1.7.4.1
>From 581fb76d24aadc7b53d1644107966ae4925b5aa8 Mon Sep 17 00:00:00 2001
From: Stefan Thomas <[email protected]>
Date: Thu, 15 Jul 2010 16:24:55 +0100
Subject: [PATCH 7/9] pdftocairo: Added to CMake build system.
---
utils/CMakeLists.txt | 20 ++++++++++++++++++++
1 files changed, 20 insertions(+), 0 deletions(-)
diff --git a/utils/CMakeLists.txt b/utils/CMakeLists.txt
index 04cc617..9e92589 100644
--- a/utils/CMakeLists.txt
+++ b/utils/CMakeLists.txt
@@ -20,6 +20,26 @@ if (ENABLE_SPLASH)
install(FILES pdftoppm.1 DESTINATION share/man/man1)
endif (ENABLE_SPLASH)
+if (HAVE_CAIRO)
+ # pdftocairo
+ set(pdftocairo_SOURCES ${common_srcs}
+ pdftocairo.cc
+ ${CMAKE_SOURCE_DIR}/poppler/CairoFontEngine.cc
+ ${CMAKE_SOURCE_DIR}/poppler/CairoOutputDev.cc
+ ${CMAKE_SOURCE_DIR}/poppler/CairoRescaleBox.cc
+ )
+ include_directories(
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ ${CMAKE_CURRENT_BINARY_DIR}
+ ${CAIRO_INCLUDE_DIRS}
+ )
+ add_definitions(${CAIRO_CFLAGS})
+ add_executable(pdftocairo ${pdftocairo_SOURCES})
+ target_link_libraries(pdftocairo ${CAIRO_LIBRARIES} ${common_libs})
+ install(TARGETS pdftocairo DESTINATION bin)
+ install(FILES pdftocairo.1 DESTINATION share/man/man1)
+endif (HAVE_CAIRO)
+
# pdffonts
set(pdffonts_SOURCES ${common_srcs}
pdffonts.cc
--
1.7.4.1
>From 93c588558cf7047886bc90b4c1c5b1046088d378 Mon Sep 17 00:00:00 2001
From: Adrian Johnson <[email protected]>
Date: Fri, 5 Aug 2011 23:01:51 +0930
Subject: [PATCH 8/9] cairo: use cairo_show_text_glyphs() when printing
This will allow cairo to setup the correct toUnicode or glyph names to
ensure text can be extracted.
---
poppler/CairoOutputDev.cc | 41 ++++++++++++++++++++++++++++++++++++++---
poppler/CairoOutputDev.h | 5 +++++
2 files changed, 43 insertions(+), 3 deletions(-)
diff --git a/poppler/CairoOutputDev.cc b/poppler/CairoOutputDev.cc
index 6e7fd53..bdb6b3b 100644
--- a/poppler/CairoOutputDev.cc
+++ b/poppler/CairoOutputDev.cc
@@ -60,6 +60,7 @@
#include "CairoOutputDev.h"
#include "CairoFontEngine.h"
#include "CairoRescaleBox.h"
+#include "UTF8.h"
//------------------------------------------------------------------------
// #define LOG_CAIRO
@@ -994,6 +995,13 @@ void CairoOutputDev::beginString(GfxState *state, GooString *s)
glyphs = (cairo_glyph_t *) gmallocn (len, sizeof (cairo_glyph_t));
glyphCount = 0;
+ if (printing) {
+ clusters = (cairo_text_cluster_t *) gmallocn (len, sizeof (cairo_text_cluster_t));
+ clusterCount = 0;
+ utf8Max = len*2; // start with twice the number of glyphs. we will realloc if we need more.
+ utf8 = (char *) gmalloc (utf8Max);
+ utf8Count = 0;
+ }
}
void CairoOutputDev::drawChar(GfxState *state, double x, double y,
@@ -1006,6 +1014,24 @@ void CairoOutputDev::drawChar(GfxState *state, double x, double y,
glyphs[glyphCount].x = x - originX;
glyphs[glyphCount].y = y - originY;
glyphCount++;
+ if (printing) {
+ if (utf8Max - utf8Count < uLen*6) {
+ // utf8 encoded characters can be up to 6 bytes
+ if (utf8Max > uLen*6)
+ utf8Max *= 2;
+ else
+ utf8Max += 2*uLen*6;
+ utf8 = (char *) grealloc (utf8, utf8Max);
+ }
+ clusters[clusterCount].num_bytes = 0;
+ for (int i = 0; i < uLen; i++) {
+ int size = mapUTF8 (u[i], utf8 + utf8Count, utf8Max - utf8Count);
+ utf8Count += size;
+ clusters[clusterCount].num_bytes += size;
+ }
+ clusters[clusterCount].num_glyphs = 1;
+ clusterCount++;
+ }
}
if (!text)
@@ -1034,15 +1060,18 @@ void CairoOutputDev::endString(GfxState *state)
glyphs = NULL;
return;
}
-
+
if (!(render & 1) && !haveCSPattern) {
LOG (printf ("fill string\n"));
cairo_set_source (cairo, fill_pattern);
- cairo_show_glyphs (cairo, glyphs, glyphCount);
+ if (printing)
+ cairo_show_text_glyphs (cairo, utf8, utf8Count, glyphs, glyphCount, clusters, clusterCount, (cairo_text_cluster_flags_t)0);
+ else
+ cairo_show_glyphs (cairo, glyphs, glyphCount);
if (cairo_shape)
cairo_show_glyphs (cairo_shape, glyphs, glyphCount);
}
-
+
// stroke
if ((render & 3) == 1 || (render & 3) == 2) {
LOG (printf ("stroke string\n"));
@@ -1083,6 +1112,12 @@ void CairoOutputDev::endString(GfxState *state)
gfree (glyphs);
glyphs = NULL;
+ if (printing) {
+ gfree (clusters);
+ clusters = NULL;
+ gfree (utf8);
+ utf8 = NULL;
+ }
}
diff --git a/poppler/CairoOutputDev.h b/poppler/CairoOutputDev.h
index 730a23c..bb7a519 100644
--- a/poppler/CairoOutputDev.h
+++ b/poppler/CairoOutputDev.h
@@ -304,6 +304,11 @@ protected:
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;
--
1.7.4.1
>From 9c22de62c881edadfc8a4b7f4e83fa41ce47dfc1 Mon Sep 17 00:00:00 2001
From: Adrian Johnson <[email protected]>
Date: Sat, 13 Aug 2011 00:23:23 +0930
Subject: [PATCH 9/9] Add poppler version to PSOutputDev ouput
---
poppler/PSOutputDev.cc | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/poppler/PSOutputDev.cc b/poppler/PSOutputDev.cc
index 0a3df51..458b21e 100644
--- a/poppler/PSOutputDev.cc
+++ b/poppler/PSOutputDev.cc
@@ -1315,6 +1315,7 @@ void PSOutputDev::writeHeader(int firstPage, int lastPage,
writePS("%!PS-Adobe-3.0 Resource-Form\n");
break;
}
+ writePSFmt("% Produced by poppler pdftops version: {0:s} (http://poppler.freedesktop.org)\n", PACKAGE_VERSION);
xref->getDocInfo(&info);
if (info.isDict() && info.dictLookup("Creator", &obj1)->isString()) {
writePS("%%Creator: ");
--
1.7.4.1
_______________________________________________
poppler mailing list
[email protected]
http://lists.freedesktop.org/mailman/listinfo/poppler