[poppler] 10 commits - configure.ac goo/gtypes.h goo/PNGWriter.cc goo/PNGWriter.h poppler/CairoOutputDev.cc poppler/CairoOutputDev.h poppler/CairoRescaleBox.cc poppler/PSOutputDev.cc utils/CMakeLists.txt utils/.gitignore utils/Makefile.am utils/pdftocairo.1 utils/pdftocairo.cc

Albert Astals Cid aacid at kemper.freedesktop.org
Sun Aug 21 14:26:25 PDT 2011


 configure.ac               |    1 
 goo/PNGWriter.cc           |   61 ++
 goo/PNGWriter.h            |   19 
 goo/gtypes.h               |   18 
 poppler/CairoOutputDev.cc  |   43 +
 poppler/CairoOutputDev.h   |    5 
 poppler/CairoRescaleBox.cc |    2 
 poppler/PSOutputDev.cc     |    1 
 utils/.gitignore           |    2 
 utils/CMakeLists.txt       |   20 
 utils/Makefile.am          |   25 +
 utils/pdftocairo.1         |  254 +++++++++++
 utils/pdftocairo.cc        |  987 +++++++++++++++++++++++++++++++++++++++++++++
 13 files changed, 1420 insertions(+), 18 deletions(-)

New commits:
commit eb5b228c4c24152e632c931b63c64ffb1e10d45d
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Sat Aug 20 21:13:18 2011 +0930

    pdftocairo: fix writing to stdout for ps/pdf/svg

diff --git a/utils/pdftocairo.cc b/utils/pdftocairo.cc
index 46cec36..193854d 100644
--- a/utils/pdftocairo.cc
+++ b/utils/pdftocairo.cc
@@ -228,6 +228,7 @@ static const ArgDesc argDesc[] = {
 
 static  cairo_surface_t *surface;
 static  GBool printing;
+static  FILE *output_file;
 
 #if USE_CMS
 static unsigned char *icc_data;
@@ -405,12 +406,27 @@ static void getFitToPageTransform(double page_w, double page_h,
     }
 }
 
+static cairo_status_t writeStream(void *closure, const unsigned char *data, unsigned int length)
+{
+  FILE *file = (FILE *)closure;
+
+  if (fwrite(data, length, 1, file) == 1)
+    return CAIRO_STATUS_SUCCESS;
+  else
+    return CAIRO_STATUS_WRITE_ERROR;
+}
+
 static void beginDocument(GooString *outputFileName, double w, double h)
 {
   if (printing) {
+    if (outputFileName->cmp("fd://0") == 0)
+      output_file = stdout;
+    else
+      output_file = fopen(outputFileName->getCString(), "wb");
+
     if (ps || eps) {
 #if CAIRO_HAS_PS_SURFACE
-      surface = cairo_ps_surface_create(outputFileName->getCString(), w, h);
+      surface = cairo_ps_surface_create_for_stream(writeStream, output_file, w, h);
       if (level2)
 	cairo_ps_surface_restrict_to_level (surface, CAIRO_PS_LEVEL_2);
       if (eps)
@@ -424,11 +440,11 @@ static void beginDocument(GooString *outputFileName, double w, double h)
 #endif
     } else if (pdf) {
 #if CAIRO_HAS_PDF_SURFACE
-      surface = cairo_pdf_surface_create(outputFileName->getCString(), w, h);
+      surface = cairo_pdf_surface_create_for_stream(writeStream, output_file, w, h);
 #endif
     } else if (svg) {
 #if CAIRO_HAS_SVG_SURFACE
-      surface = cairo_svg_surface_create(outputFileName->getCString(), w, h);
+      surface = cairo_svg_surface_create_for_stream(writeStream, output_file, w, h);
       cairo_svg_surface_restrict_to_version (surface, CAIRO_SVG_VERSION_1_2);
 #endif
     }
@@ -545,6 +561,7 @@ static void endDocument()
     if (status)
       error(-1, "cairo error: %s\n", cairo_status_to_string(status));
     cairo_surface_destroy(surface);
+    fclose(output_file);
   }
 }
 
commit 1beac5896a301be68de22240017fef11e7d27d40
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Sat Aug 13 00:23:23 2011 +0930

    Add poppler version to PSOutputDev ouput

diff --git a/poppler/PSOutputDev.cc b/poppler/PSOutputDev.cc
index 8387c7e..3e5cf08 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: ");
commit b35fd3651fd3dbaa876fc64b8c5cfe77ae67335f
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Fri Aug 5 23:01:51 2011 +0930

    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.

diff --git a/poppler/CairoOutputDev.cc b/poppler/CairoOutputDev.cc
index e832892..29d8f95 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
@@ -999,6 +1000,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,
@@ -1011,6 +1019,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)
@@ -1039,15 +1065,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"));
@@ -1088,6 +1117,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 809bd78..67f7810 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;
commit 51ade078bc1fa737e20120ae4cb3bf693a219823
Author: Stefan Thomas <thomas at txtbear.com>
Date:   Thu Jul 15 16:24:55 2010 +0100

    pdftocairo: Added to CMake build system.

diff --git a/utils/CMakeLists.txt b/utils/CMakeLists.txt
index 04cc617..2643e3d 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
commit 5a8745cdf428e22641937977eedfc1d605f6ff07
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Fri Aug 12 23:57:01 2011 +0930

    Add pdftocairo man page

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),
commit b67a12b2b26692e2ccec7ff2e6df18fee05be535
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Thu Aug 11 21:34:11 2011 +0930

    pdftocairo - utility for creating png/jpeg/ps/eps/pdf/svg using CairoOutputDev

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 <ilmari.heikkinen at gmail.com>
+// Copyright (C) 2008 Richard Airlie <richard.airlie at maglabs.net>
+// Copyright (C) 2009 Michael K. Johnson <a1237 at danlj.org>
+// Copyright (C) 2009 Shen Liang <shenzhuxi at gmail.com>
+// Copyright (C) 2009 Stefan Thomas <thomas at eload24.com>
+// Copyright (C) 2009, 2010 Albert Astals Cid <aacid at kde.org>
+// Copyright (C) 2010, 2011 Adrian Johnson <ajohnson at redneon.com>
+// Copyright (C) 2010 Hib Eris <hib at hiberis.nl>
+// Copyright (C) 2010 Jonathan Liu <net147 at gmail.com>
+// Copyright (C) 2010 William Bader <williambader at hotmail.com>
+// Copyright (C) 2011 Thomas Freitag <Thomas.Freitag at 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
+//
+//========================================================================
+
+#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;
+}
commit 4f2d774826bf7bb7b3825e02c5ca4c2928643950
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Thu Aug 11 21:32:53 2011 +0930

    png: add support for embedding ICC profile

diff --git a/goo/PNGWriter.cc b/goo/PNGWriter.cc
index 7883c56..1e78de5 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);
+	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 9f13ca2..1f0eee1 100644
--- a/goo/PNGWriter.h
+++ b/goo/PNGWriter.h
@@ -36,6 +36,10 @@ class PNGWriter : public ImgWriter
 
 		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);
 		
@@ -48,6 +52,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
commit 1091f47310bf0fc71bac5dd4ec81dad50b2f2537
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Wed Aug 10 18:48:15 2011 +0930

    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.

diff --git a/goo/PNGWriter.cc b/goo/PNGWriter.cc
index 400c515..7883c56 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..9f13ca2 100644
--- a/goo/PNGWriter.h
+++ b/goo/PNGWriter.h
@@ -26,7 +26,15 @@
 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);
@@ -37,6 +45,7 @@ class PNGWriter : public ImgWriter
 		bool close();
 	
 	private:
+		Format format;
 		png_structp png_ptr;
 		png_infop info_ptr;
 };
commit c6f26915db568f12892d48005746ad2922c19000
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Wed Aug 10 18:45:24 2011 +0930

    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

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))) {
commit 2667d2a5b34e1bbf322aea42876e7e81aa06dc29
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Tue Aug 9 22:05:53 2011 +0930

    Use stdint.h instead of assuming the size of types

diff --git a/configure.ac b/configure.ac
index 7595b71..c8b6bdd 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..cb1cb89 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,20 @@ 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;
+#endif
+
 #endif
diff --git a/poppler/CairoOutputDev.cc b/poppler/CairoOutputDev.cc
index d560bea..e832892 100644
--- a/poppler/CairoOutputDev.cc
+++ b/poppler/CairoOutputDev.cc
@@ -1348,8 +1348,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


More information about the poppler mailing list