@@ -46,6 +46,8 @@ This file is part of the iText (R) project.
4646import com .itextpdf .io .IoExceptionMessage ;
4747
4848import java .io .IOException ;
49+ import java .nio .file .Paths ;
50+ import java .util .regex .Pattern ;
4951
5052/**
5153 * A utility class that is used as an interface to run 3rd-party tool Ghostscript.
@@ -67,8 +69,14 @@ public class GhostscriptHelper {
6769 static final String GHOSTSCRIPT_ENVIRONMENT_VARIABLE_LEGACY = "gsExec" ;
6870
6971 static final String GHOSTSCRIPT_KEYWORD = "GPL Ghostscript" ;
72+ private static final String TEMP_FILE_PREFIX = "itext_gs_io_temp" ;
7073
71- private static final String GHOSTSCRIPT_PARAMS = " -dSAFER -dNOPAUSE -dBATCH -sDEVICE=png16m -r150 {0} -sOutputFile=\" {1}\" \" {2}\" " ;
74+ private static final String RENDERED_IMAGE_EXTENSION = "png" ;
75+ private static final String GHOSTSCRIPT_PARAMS = " -dSAFER -dNOPAUSE -dBATCH -sDEVICE="
76+ + RENDERED_IMAGE_EXTENSION + "16m -r150 {0} -sOutputFile=\" {1}\" \" {2}\" " ;
77+ private static final String PAGE_NUMBER_PATTERN = "%03d" ;
78+
79+ private static final Pattern PAGE_LIST_REGEX = Pattern .compile ("^(\\ d+,)*\\ d+$" );
7280
7381 private String gsExec ;
7482
@@ -112,11 +120,17 @@ public String getCliExecutionCommand() {
112120 }
113121
114122 /**
115- * Runs ghostscript to create images of pdfs .
123+ * Runs Ghostscript to render the PDF's pages as PNG images .
116124 *
117- * @param pdf Path to the pdf file.
118- * @param outDir Path to the output directory
119- * @param image Path to the generated image
125+ * @param pdf Path to the PDF file to be rendered
126+ * @param outDir Path to the output directory, in which the rendered pages will be stored
127+ * @param image String which defines the name of the resultant images. This string will be
128+ * concatenated with the number of the rendered page from the start of the
129+ * PDF in "-%03d" format, e.g. "-011" for the eleventh rendered page and so on.
130+ * This number may not correspond to the actual page number: for example,
131+ * if the passed pageList equals to "5,3", then images with postfixes "-001.png"
132+ * and "-002.png" will be created: the former for the third page, the latter
133+ * for the fifth page. "%" sign in the passed name is prohibited.
120134 *
121135 * @throws IOException if there are file's reading/writing issues
122136 * @throws InterruptedException if there is thread interruption while executing GhostScript.
@@ -127,14 +141,20 @@ public void runGhostScriptImageGeneration(String pdf, String outDir, String imag
127141 }
128142
129143 /**
130- * Runs ghostscript to create images of specified pages of pdfs .
144+ * Runs Ghostscript to render the PDF's pages as PNG images .
131145 *
132- * @param pdf Path to the pdf file.
133- * @param outDir Path to the output directory
134- * @param image Path to the generated image
135- * @param pageList String with numbers of the required pages to extract as image. Should be formatted as string with
136- * numbers, separated by commas, without whitespaces. Can be null, if it is required to extract
137- * all pages as images.
146+ * @param pdf Path to the PDF file to be rendered
147+ * @param outDir Path to the output directory, in which the rendered pages will be stored
148+ * @param image String which defines the name of the resultant images. This string will be
149+ * concatenated with the number of the rendered page from the start of the
150+ * PDF in "-%03d" format, e.g. "-011" for the eleventh rendered page and so on.
151+ * This number may not correspond to the actual page number: for example,
152+ * if the passed pageList equals to "5,3", then images with postfixes "-001.png"
153+ * and "-002.png" will be created: the former for the third page, the latter
154+ * for the fifth page. "%" sign in the passed name is prohibited.
155+ * @param pageList String with numbers of the required pages to be rendered as images.
156+ * This string should be formatted as a string with numbers, separated by commas,
157+ * without whitespaces. Can be null, if it is required to render all the PDF's pages.
138158 *
139159 * @throws IOException if there are file's reading/writing issues
140160 * @throws InterruptedException if there is thread interruption while executing GhostScript.
@@ -145,12 +165,48 @@ public void runGhostScriptImageGeneration(String pdf, String outDir, String imag
145165 throw new IllegalArgumentException (
146166 IoExceptionMessage .CANNOT_OPEN_OUTPUT_DIRECTORY .replace ("<filename>" , pdf ));
147167 }
168+ if (!validateImageFilePattern (image )) {
169+ throw new IllegalArgumentException ("Invalid output image pattern: " + image );
170+ }
171+ if (!validatePageList (pageList )) {
172+ throw new IllegalArgumentException ("Invalid page list: " + pageList );
173+ }
174+ String formattedPageList = (pageList == null ) ? "" : "-sPageList=<pagelist>" .replace ("<pagelist>" , pageList );
175+
176+ String replacementPdf = null ;
177+ String replacementImagesDirectory = null ;
178+ String [] temporaryOutputImages = null ;
179+ try {
180+ replacementPdf = FileUtil .createTempCopy (pdf , TEMP_FILE_PREFIX , null );
181+ replacementImagesDirectory = FileUtil .createTempDirectory (TEMP_FILE_PREFIX );
182+ String currGsParams = MessageFormatUtil .format (GHOSTSCRIPT_PARAMS , formattedPageList ,
183+ Paths .get (replacementImagesDirectory ,
184+ TEMP_FILE_PREFIX + PAGE_NUMBER_PATTERN + "." + RENDERED_IMAGE_EXTENSION ).toString (),
185+ replacementPdf );
186+
187+ if (!SystemUtil .runProcessAndWait (gsExec , currGsParams )) {
188+ temporaryOutputImages = FileUtil
189+ .listFilesInDirectory (replacementImagesDirectory , false );
190+ throw new GhostscriptExecutionException (
191+ IoExceptionMessage .GHOSTSCRIPT_FAILED .replace ("<filename>" , pdf ));
192+ }
148193
149- pageList = (pageList == null ) ? "" : "-sPageList=<pagelist>" .replace ("<pagelist>" , pageList );
150-
151- String currGsParams = MessageFormatUtil .format (GHOSTSCRIPT_PARAMS , pageList , outDir + image , pdf );
152- if (!SystemUtil .runProcessAndWait (gsExec , currGsParams )) {
153- throw new GhostscriptExecutionException (IoExceptionMessage .GHOSTSCRIPT_FAILED .replace ("<filename>" , pdf ));
194+ temporaryOutputImages = FileUtil
195+ .listFilesInDirectory (replacementImagesDirectory , false );
196+ if (null != temporaryOutputImages ) {
197+ for (int i = 0 ; i < temporaryOutputImages .length ; i ++) {
198+ FileUtil .copy (temporaryOutputImages [i ],
199+ Paths .get (
200+ outDir ,
201+ image + "-" + formatImageNumber (i + 1 ) + "." + RENDERED_IMAGE_EXTENSION
202+ ).toString ());
203+ }
204+ }
205+ } finally {
206+ if (null != temporaryOutputImages ) {
207+ FileUtil .removeFiles (temporaryOutputImages );
208+ }
209+ FileUtil .removeFiles (new String [] {replacementImagesDirectory , replacementPdf });
154210 }
155211 }
156212
@@ -168,4 +224,26 @@ public GhostscriptExecutionException(String msg) {
168224 super (msg );
169225 }
170226 }
227+
228+ static boolean validatePageList (String pageList ) {
229+ return null == pageList
230+ || PAGE_LIST_REGEX .matcher (pageList ).matches ();
231+ }
232+
233+ static boolean validateImageFilePattern (String imageFilePattern ) {
234+ return null != imageFilePattern
235+ && !imageFilePattern .trim ().isEmpty ()
236+ && !imageFilePattern .contains ("%" );
237+ }
238+
239+ static String formatImageNumber (int pageNumber ) {
240+ StringBuilder stringBuilder = new StringBuilder ();
241+ int zeroFiller = pageNumber ;
242+ while (0 == zeroFiller / 100 ) {
243+ stringBuilder .append ('0' );
244+ zeroFiller *= 10 ;
245+ }
246+ stringBuilder .append (pageNumber );
247+ return stringBuilder .toString ();
248+ }
171249}
0 commit comments