@@ -46,6 +46,8 @@ This file is part of the iText (R) project.
46
46
import com .itextpdf .io .IoExceptionMessage ;
47
47
48
48
import java .io .IOException ;
49
+ import java .nio .file .Paths ;
50
+ import java .util .regex .Pattern ;
49
51
50
52
/**
51
53
* A utility class that is used as an interface to run 3rd-party tool Ghostscript.
@@ -67,8 +69,14 @@ public class GhostscriptHelper {
67
69
static final String GHOSTSCRIPT_ENVIRONMENT_VARIABLE_LEGACY = "gsExec" ;
68
70
69
71
static final String GHOSTSCRIPT_KEYWORD = "GPL Ghostscript" ;
72
+ private static final String TEMP_FILE_PREFIX = "itext_gs_io_temp" ;
70
73
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+$" );
72
80
73
81
private String gsExec ;
74
82
@@ -112,11 +120,17 @@ public String getCliExecutionCommand() {
112
120
}
113
121
114
122
/**
115
- * Runs ghostscript to create images of pdfs .
123
+ * Runs Ghostscript to render the PDF's pages as PNG images .
116
124
*
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.
120
134
*
121
135
* @throws IOException if there are file's reading/writing issues
122
136
* @throws InterruptedException if there is thread interruption while executing GhostScript.
@@ -127,14 +141,20 @@ public void runGhostScriptImageGeneration(String pdf, String outDir, String imag
127
141
}
128
142
129
143
/**
130
- * Runs ghostscript to create images of specified pages of pdfs .
144
+ * Runs Ghostscript to render the PDF's pages as PNG images .
131
145
*
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.
138
158
*
139
159
* @throws IOException if there are file's reading/writing issues
140
160
* @throws InterruptedException if there is thread interruption while executing GhostScript.
@@ -145,12 +165,48 @@ public void runGhostScriptImageGeneration(String pdf, String outDir, String imag
145
165
throw new IllegalArgumentException (
146
166
IoExceptionMessage .CANNOT_OPEN_OUTPUT_DIRECTORY .replace ("<filename>" , pdf ));
147
167
}
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
+ }
148
193
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 });
154
210
}
155
211
}
156
212
@@ -168,4 +224,26 @@ public GhostscriptExecutionException(String msg) {
168
224
super (msg );
169
225
}
170
226
}
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
+ }
171
249
}
0 commit comments