blob: cd95d7f0c56f176bb67d940fdcc0419e5f6f3731 [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.
edisonn@google.comcdad30b2013-07-10 22:37:38 +0000100 * @param page -1 means there is only one page (0), and render in a file without page extension
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000101 */
edisonn@google.com222382b2013-07-10 22:33:10 +0000102
103static bool render_page(const SkString& outputDir,
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000104 const SkString& inputFilename,
edisonn@google.com222382b2013-07-10 22:33:10 +0000105 const SkPdfRenderer& renderer,
106 int page) {
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000107 if (outputDir.isEmpty()) {
edisonn@google.com222382b2013-07-10 22:33:10 +0000108 SkBitmap bitmap;
109 setup_bitmap(&bitmap, 1, 1);
110 SkAutoTUnref<SkDevice> device(SkNEW_ARGS(SkDevice, (bitmap)));
111 SkCanvas canvas(device);
112 return renderer.renderPage(page < 0 ? 0 : page, &canvas);
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000113 }
114
115 SkString outputPath;
edisonn@google.com222382b2013-07-10 22:33:10 +0000116 if (!make_output_filepath(&outputPath, outputDir, inputFilename, page)) {
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000117 return false;
118 }
119
edisonn@google.com222382b2013-07-10 22:33:10 +0000120 SkRect rect = renderer.MediaBox(page < 0 ? 0 :page);
121
122 SkBitmap bitmap;
123#ifdef PDF_DEBUG_3X
124 setup_bitmap(&bitmap, 3 * (int)SkScalarToDouble(rect.width()), 3 * (int)SkScalarToDouble(rect.height()));
125#else
126 setup_bitmap(&bitmap, (int)SkScalarToDouble(rect.width()), (int)SkScalarToDouble(rect.height()));
127#endif
128 SkAutoTUnref<SkDevice> device(SkNEW_ARGS(SkDevice, (bitmap)));
129 SkCanvas canvas(device);
130
131 gDumpBitmap = &bitmap;
132
133 gDumpCanvas = &canvas;
edisonn@google.comcdad30b2013-07-10 22:37:38 +0000134 renderer.renderPage(page < 0 ? 0 : page, &canvas);
edisonn@google.com222382b2013-07-10 22:33:10 +0000135
136 SkImageEncoder::EncodeFile(outputPath.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100);
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000137
138 return true;
139}
edisonn@google.com222382b2013-07-10 22:33:10 +0000140
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000141/** Reads an skp file, renders it to pdf and writes the output to a pdf file
142 * @param inputPath The skp file to be read.
143 * @param outputDir Output dir.
144 * @param renderer The object responsible to render the skp object into pdf.
145 */
edisonn@google.com222382b2013-07-10 22:33:10 +0000146static bool process_pdf(const SkString& inputPath, const SkString& outputDir,
147 SkPdfRenderer& renderer, bool noPageExt) {
148 SkDebugf("Loading PDF: %s\n", inputPath.c_str());
149
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000150 SkString inputFilename;
151 sk_tools::get_basename(&inputFilename, inputPath);
152
153 SkFILEStream inputStream;
154 inputStream.setPath(inputPath.c_str());
155 if (!inputStream.isValid()) {
156 SkDebugf("Could not open file %s\n", inputPath.c_str());
157 return false;
158 }
159
160 bool success = false;
161
edisonn@google.com222382b2013-07-10 22:33:10 +0000162 success = renderer.load(inputPath);
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000163
edisonn@google.com222382b2013-07-10 22:33:10 +0000164 if (success) {
165 if (!renderer.pages())
166 {
167 SkDebugf("ERROR: Empty PDF Document %s\n", inputPath.c_str());
168 return false;
169 } else {
170 for (int pn = 0; pn < renderer.pages(); ++pn) {
171 success = render_page(outputDir, inputFilename, renderer, noPageExt && renderer.pages() == 1 ? -1 : pn) && success;
172 }
173 }
174 }
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000175
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000176 return success;
177}
178
179/** For each file in the directory or for the file passed in input, call
180 * parse_pdf.
181 * @param input A directory or an pdf file.
182 * @param outputDir Output dir.
183 * @param renderer The object responsible to render the skp object into pdf.
184 */
185static int process_input(const SkString& input, const SkString& outputDir,
edisonn@google.com222382b2013-07-10 22:33:10 +0000186 SkPdfRenderer& renderer, bool noPageExt) {
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000187 int failures = 0;
188 if (sk_isdir(input.c_str())) {
189 SkOSFile::Iter iter(input.c_str(), PDF_FILE_EXTENSION);
190 SkString inputFilename;
191 while (iter.next(&inputFilename)) {
192 SkString inputPath;
193 sk_tools::make_filepath(&inputPath, input, inputFilename);
edisonn@google.com222382b2013-07-10 22:33:10 +0000194 if (!process_pdf(inputPath, outputDir, renderer, noPageExt)) {
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000195 ++failures;
196 }
197 }
198 } else {
199 SkString inputPath(input);
edisonn@google.com222382b2013-07-10 22:33:10 +0000200 if (!process_pdf(inputPath, outputDir, renderer, noPageExt)) {
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000201 ++failures;
202 }
203 }
204 return failures;
205}
206
207static void parse_commandline(int argc, char* const argv[],
208 SkTArray<SkString>* inputs,
edisonn@google.com222382b2013-07-10 22:33:10 +0000209 SkString* outputDir, bool* noPageExt) {
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000210 const char* argv0 = argv[0];
211 char* const* stop = argv + argc;
212
213 for (++argv; argv < stop; ++argv) {
214 if ((0 == strcmp(*argv, "-h")) || (0 == strcmp(*argv, "--help"))) {
215 usage(argv0);
216 exit(-1);
edisonn@google.com222382b2013-07-10 22:33:10 +0000217 } else if ((0 == strcmp(*argv, "-n")) || (0 == strcmp(*argv, "--no-page-ext"))) {
218 *noPageExt = true;
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000219 } else if (0 == strcmp(*argv, "-w")) {
220 ++argv;
221 if (argv >= stop) {
222 SkDebugf("Missing outputDir for -w\n");
223 usage(argv0);
224 exit(-1);
225 }
226 *outputDir = SkString(*argv);
227 } else {
228 inputs->push_back(SkString(*argv));
229 }
230 }
231
232 if (inputs->count() < 1) {
233 usage(argv0);
234 exit(-1);
235 }
236}
237
238int tool_main(int argc, char** argv);
239int tool_main(int argc, char** argv) {
240 SkAutoGraphics ag;
241 SkTArray<SkString> inputs;
242
edisonn@google.com222382b2013-07-10 22:33:10 +0000243 SkPdfRenderer renderer;
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000244
245 SkString outputDir;
edisonn@google.com222382b2013-07-10 22:33:10 +0000246 bool noPageExt = false;
247 parse_commandline(argc, argv, &inputs, &outputDir, &noPageExt);
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000248
249 int failures = 0;
250 for (int i = 0; i < inputs.count(); i ++) {
edisonn@google.com222382b2013-07-10 22:33:10 +0000251 failures += process_input(inputs[i], outputDir, renderer, noPageExt);
252 renderer.unload();
edisonn@google.com01cd4d52013-06-10 20:44:45 +0000253 }
254
255 reportPdfRenderStats();
256
257 if (failures != 0) {
258 SkDebugf("Failed to render %i PDFs.\n", failures);
259 return 1;
260 }
261
262 return 0;
263}
264
265#if !defined SK_BUILD_FOR_IOS
266int main(int argc, char * const argv[]) {
267 return tool_main(argc, (char**) argv);
268}
269#endif