blob: 45bfff3990719c9ce6572525eb2f89eef87beaff [file] [log] [blame]
edisonn@google.com01cd4d52013-06-10 20:44:45 +00001#include "SkCanvas.h"
2#include "SkDevice.h"
edisonn@google.comafe5e9e2013-06-19 17:42:17 +00003#include "SkForceLinking.h"
edisonn@google.com01cd4d52013-06-10 20:44:45 +00004#include "SkGraphics.h"
5#include "SkImageDecoder.h"
6#include "SkImageEncoder.h"
7#include "SkOSFile.h"
8#include "SkPicture.h"
9#include "SkStream.h"
10#include "SkTypeface.h"
11#include "SkTArray.h"
12#include "picture_utils.h"
13
edisonn@google.com222382b2013-07-10 22:33:10 +000014#include "SkPdfRenderer.h"
edisonn@google.comb857a0c2013-06-25 20:45:40 +000015
edisonn@google.com01cd4d52013-06-10 20:44:45 +000016/**
17 * Given list of directories and files to use as input, expects to find .pdf
18 * files and it will convert them to .png files writing them in the same directory
19 * one file for each page.
20 *
21 * Returns zero exit code if all .pdf files were converted successfully,
22 * otherwise returns error code 1.
23 */
24
25static const char PDF_FILE_EXTENSION[] = "pdf";
26static const char PNG_FILE_EXTENSION[] = "png";
27
28// TODO(edisonn): add ability to write to a new directory.
29static void usage(const char* argv0) {
30 SkDebugf("PDF to PNG rendering tool\n");
31 SkDebugf("\n"
32"Usage: \n"
edisonn@google.com222382b2013-07-10 22:33:10 +000033" %s <input>... [-w <outputDir>] [-n | --no-page-ext] \n"
edisonn@google.com01cd4d52013-06-10 20:44:45 +000034, argv0);
35 SkDebugf("\n\n");
36 SkDebugf(
37" input: A list of directories and files to use as input. Files are\n"
38" expected to have the .skp extension.\n\n");
39 SkDebugf(
40" outputDir: directory to write the rendered pdfs.\n\n");
edisonn@google.com222382b2013-07-10 22:33:10 +000041 SkDebugf(
42" -n: no page extension if only one page.\n\n");
edisonn@google.com01cd4d52013-06-10 20:44:45 +000043 SkDebugf("\n");
44}
45
46/** Replaces the extension of a file.
47 * @param path File name whose extension will be changed.
48 * @param old_extension The old extension.
49 * @param new_extension The new extension.
50 * @returns false if the file did not has the expected extension.
51 * if false is returned, contents of path are undefined.
52 */
edisonn@google.com222382b2013-07-10 22:33:10 +000053static bool add_page_and_replace_filename_extension(SkString* path, int page,
edisonn@google.com01cd4d52013-06-10 20:44:45 +000054 const char old_extension[],
55 const char new_extension[]) {
56 if (path->endsWith(old_extension)) {
57 path->remove(path->size() - strlen(old_extension),
58 strlen(old_extension));
59 if (!path->endsWith(".")) {
60 return false;
61 }
edisonn@google.com222382b2013-07-10 22:33:10 +000062 if (page >= 0) {
63 path->appendf("%i.", page);
64 }
edisonn@google.com01cd4d52013-06-10 20:44:45 +000065 path->append(new_extension);
66 return true;
67 }
68 return false;
69}
edisonn@google.com222382b2013-07-10 22:33:10 +000070
edisonn@google.com01cd4d52013-06-10 20:44:45 +000071/** Builds the output filename. path = dir/name, and it replaces expected
72 * .skp extension with .pdf extention.
73 * @param path Output filename.
74 * @param name The name of the file.
75 * @returns false if the file did not has the expected extension.
76 * if false is returned, contents of path are undefined.
77 */
edisonn@google.com596d2e22013-07-10 17:44:55 +000078
edisonn@google.com222382b2013-07-10 22:33:10 +000079
edisonn@google.com01cd4d52013-06-10 20:44:45 +000080static bool make_output_filepath(SkString* path, const SkString& dir,
edisonn@google.com222382b2013-07-10 22:33:10 +000081 const SkString& name,
82 int page) {
edisonn@google.com01cd4d52013-06-10 20:44:45 +000083 sk_tools::make_filepath(path, dir, name);
edisonn@google.com222382b2013-07-10 22:33:10 +000084 return add_page_and_replace_filename_extension(path, page,
85 PDF_FILE_EXTENSION,
86 PNG_FILE_EXTENSION);
edisonn@google.com01cd4d52013-06-10 20:44:45 +000087}
edisonn@google.com222382b2013-07-10 22:33:10 +000088
89static void setup_bitmap(SkBitmap* bitmap, int width, int height, SkColor color = SK_ColorWHITE) {
90 bitmap->setConfig(SkBitmap::kARGB_8888_Config, width, height);
91
92 bitmap->allocPixels();
93 bitmap->eraseColor(color);
94}
95
edisonn@google.com01cd4d52013-06-10 20:44:45 +000096/** Write the output of pdf renderer to a file.
97 * @param outputDir Output dir.
98 * @param inputFilename The skp file that was read.
99 * @param renderer The object responsible to write the pdf file.
100 */
edisonn@google.com222382b2013-07-10 22:33:10 +0000101
102static bool render_page(const SkString& outputDir,
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000103 const SkString& inputFilename,
edisonn@google.com222382b2013-07-10 22:33:10 +0000104 const SkPdfRenderer& renderer,
105 int page) {
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000106 if (outputDir.isEmpty()) {
edisonn@google.com222382b2013-07-10 22:33:10 +0000107 SkBitmap bitmap;
108 setup_bitmap(&bitmap, 1, 1);
109 SkAutoTUnref<SkDevice> device(SkNEW_ARGS(SkDevice, (bitmap)));
110 SkCanvas canvas(device);
111 return renderer.renderPage(page < 0 ? 0 : page, &canvas);
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000112 }
113
114 SkString outputPath;
edisonn@google.com222382b2013-07-10 22:33:10 +0000115 if (!make_output_filepath(&outputPath, outputDir, inputFilename, page)) {
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000116 return false;
117 }
118
edisonn@google.com222382b2013-07-10 22:33:10 +0000119 SkRect rect = renderer.MediaBox(page < 0 ? 0 :page);
120
121 SkBitmap bitmap;
122#ifdef PDF_DEBUG_3X
123 setup_bitmap(&bitmap, 3 * (int)SkScalarToDouble(rect.width()), 3 * (int)SkScalarToDouble(rect.height()));
124#else
125 setup_bitmap(&bitmap, (int)SkScalarToDouble(rect.width()), (int)SkScalarToDouble(rect.height()));
126#endif
127 SkAutoTUnref<SkDevice> device(SkNEW_ARGS(SkDevice, (bitmap)));
128 SkCanvas canvas(device);
129
130 gDumpBitmap = &bitmap;
131
132 gDumpCanvas = &canvas;
133 renderer.renderPage(page, &canvas);
134
135 SkImageEncoder::EncodeFile(outputPath.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100);
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000136
137 return true;
138}
edisonn@google.com222382b2013-07-10 22:33:10 +0000139
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000140/** Reads an skp file, renders it to pdf and writes the output to a pdf file
141 * @param inputPath The skp file to be read.
142 * @param outputDir Output dir.
143 * @param renderer The object responsible to render the skp object into pdf.
144 */
edisonn@google.com222382b2013-07-10 22:33:10 +0000145static bool process_pdf(const SkString& inputPath, const SkString& outputDir,
146 SkPdfRenderer& renderer, bool noPageExt) {
147 SkDebugf("Loading PDF: %s\n", inputPath.c_str());
148
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000149 SkString inputFilename;
150 sk_tools::get_basename(&inputFilename, inputPath);
151
152 SkFILEStream inputStream;
153 inputStream.setPath(inputPath.c_str());
154 if (!inputStream.isValid()) {
155 SkDebugf("Could not open file %s\n", inputPath.c_str());
156 return false;
157 }
158
159 bool success = false;
160
edisonn@google.com222382b2013-07-10 22:33:10 +0000161 success = renderer.load(inputPath);
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000162
edisonn@google.com222382b2013-07-10 22:33:10 +0000163 if (success) {
164 if (!renderer.pages())
165 {
166 SkDebugf("ERROR: Empty PDF Document %s\n", inputPath.c_str());
167 return false;
168 } else {
169 for (int pn = 0; pn < renderer.pages(); ++pn) {
170 success = render_page(outputDir, inputFilename, renderer, noPageExt && renderer.pages() == 1 ? -1 : pn) && success;
171 }
172 }
173 }
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000174
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000175 return success;
176}
177
178/** For each file in the directory or for the file passed in input, call
179 * parse_pdf.
180 * @param input A directory or an pdf file.
181 * @param outputDir Output dir.
182 * @param renderer The object responsible to render the skp object into pdf.
183 */
184static int process_input(const SkString& input, const SkString& outputDir,
edisonn@google.com222382b2013-07-10 22:33:10 +0000185 SkPdfRenderer& renderer, bool noPageExt) {
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000186 int failures = 0;
187 if (sk_isdir(input.c_str())) {
188 SkOSFile::Iter iter(input.c_str(), PDF_FILE_EXTENSION);
189 SkString inputFilename;
190 while (iter.next(&inputFilename)) {
191 SkString inputPath;
192 sk_tools::make_filepath(&inputPath, input, inputFilename);
edisonn@google.com222382b2013-07-10 22:33:10 +0000193 if (!process_pdf(inputPath, outputDir, renderer, noPageExt)) {
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000194 ++failures;
195 }
196 }
197 } else {
198 SkString inputPath(input);
edisonn@google.com222382b2013-07-10 22:33:10 +0000199 if (!process_pdf(inputPath, outputDir, renderer, noPageExt)) {
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000200 ++failures;
201 }
202 }
203 return failures;
204}
205
206static void parse_commandline(int argc, char* const argv[],
207 SkTArray<SkString>* inputs,
edisonn@google.com222382b2013-07-10 22:33:10 +0000208 SkString* outputDir, bool* noPageExt) {
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000209 const char* argv0 = argv[0];
210 char* const* stop = argv + argc;
211
212 for (++argv; argv < stop; ++argv) {
213 if ((0 == strcmp(*argv, "-h")) || (0 == strcmp(*argv, "--help"))) {
214 usage(argv0);
215 exit(-1);
edisonn@google.com222382b2013-07-10 22:33:10 +0000216 } else if ((0 == strcmp(*argv, "-n")) || (0 == strcmp(*argv, "--no-page-ext"))) {
217 *noPageExt = true;
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000218 } else if (0 == strcmp(*argv, "-w")) {
219 ++argv;
220 if (argv >= stop) {
221 SkDebugf("Missing outputDir for -w\n");
222 usage(argv0);
223 exit(-1);
224 }
225 *outputDir = SkString(*argv);
226 } else {
227 inputs->push_back(SkString(*argv));
228 }
229 }
230
231 if (inputs->count() < 1) {
232 usage(argv0);
233 exit(-1);
234 }
235}
236
237int tool_main(int argc, char** argv);
238int tool_main(int argc, char** argv) {
239 SkAutoGraphics ag;
240 SkTArray<SkString> inputs;
241
edisonn@google.com222382b2013-07-10 22:33:10 +0000242 SkPdfRenderer renderer;
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000243
244 SkString outputDir;
edisonn@google.com222382b2013-07-10 22:33:10 +0000245 bool noPageExt = false;
246 parse_commandline(argc, argv, &inputs, &outputDir, &noPageExt);
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000247
248 int failures = 0;
249 for (int i = 0; i < inputs.count(); i ++) {
edisonn@google.com222382b2013-07-10 22:33:10 +0000250 failures += process_input(inputs[i], outputDir, renderer, noPageExt);
251 renderer.unload();
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000252 }
253
254 reportPdfRenderStats();
255
256 if (failures != 0) {
257 SkDebugf("Failed to render %i PDFs.\n", failures);
258 return 1;
259 }
260
261 return 0;
262}
263
264#if !defined SK_BUILD_FOR_IOS
265int main(int argc, char * const argv[]) {
266 return tool_main(argc, (char**) argv);
267}
268#endif