diff --git a/experimental/PdfViewer/SkPdfRenderer.cpp b/experimental/PdfViewer/SkPdfRenderer.cpp index 33798cb343..c51c2390f8 100644 --- a/experimental/PdfViewer/SkPdfRenderer.cpp +++ b/experimental/PdfViewer/SkPdfRenderer.cpp @@ -2030,6 +2030,20 @@ bool SkPdfRenderer::load(const SkString inputFileName) { return fPdfDoc != NULL; } +bool SkPdfRenderer::load(SkStream* stream) { + unload(); + + // TODO(edisonn): create static function that could return NULL if there are errors + fPdfDoc = new SkNativeParsedPDF(stream); + if (fPdfDoc->pages() == 0) { + delete fPdfDoc; + fPdfDoc = NULL; + } + + return fPdfDoc != NULL; +} + + int SkPdfRenderer::pages() const { return fPdfDoc != NULL ? fPdfDoc->pages() : 0; } @@ -2047,3 +2061,30 @@ SkRect SkPdfRenderer::MediaBox(int page) const { size_t SkPdfRenderer::bytesUsed() const { return fPdfDoc ? fPdfDoc->bytesUsed() : 0; } + +bool SkPDFNativeRenderToBitmap(SkStream* stream, + SkBitmap* output, + int page, + SkPdfContent content, + double dpi) { + SkASSERT(page >= 0); + SkPdfRenderer renderer; + renderer.load(stream); + if (!renderer.loaded() || page >= renderer.pages() || page < 0) { + return false; + } + + SkRect rect = renderer.MediaBox(page < 0 ? 0 :page); + + SkScalar width = SkScalarMul(rect.width(), SkDoubleToScalar(sqrt(dpi / 72.0))); + SkScalar height = SkScalarMul(rect.height(), SkDoubleToScalar(sqrt(dpi / 72.0))); + + rect = SkRect::MakeWH(width, height); + + setup_bitmap(output, (int)SkScalarToDouble(width), (int)SkScalarToDouble(height)); + + SkAutoTUnref device(SkNEW_ARGS(SkDevice, (*output))); + SkCanvas canvas(device); + + return renderer.renderPage(page, &canvas, rect); +} diff --git a/experimental/PdfViewer/SkPdfRenderer.h b/experimental/PdfViewer/SkPdfRenderer.h index 8bbc4f1eb2..b3c4ae3e89 100644 --- a/experimental/PdfViewer/SkPdfRenderer.h +++ b/experimental/PdfViewer/SkPdfRenderer.h @@ -9,9 +9,16 @@ #ifndef SkPdfRenderer_DEFINED #define SkPdfRenderer_DEFINED +class SkBitmap; class SkCanvas; class SkNativeParsedPDF; class SkRect; +class SkStream; + +enum SkPdfContent { + kNoForms_SkPdfContent, + kAll_SkPdfContent, +}; // TODO(edisonn): move in another file class SkPdfRenderer : public SkRefCnt { @@ -24,6 +31,7 @@ public: bool renderPage(int page, SkCanvas* canvas, const SkRect& dst) const; bool load(const SkString inputFileName); + bool load(SkStream* stream); bool loaded() const {return fPdfDoc != NULL;} int pages() const; void unload(); @@ -33,4 +41,10 @@ public: void reportPdfRenderStats(); +bool SkPDFNativeRenderToBitmap(SkStream* stream, + SkBitmap* output, + int page = 0, + SkPdfContent content = kAll_SkPdfContent, + double dpi = 72.0); + #endif // SkPdfRenderer_DEFINED diff --git a/experimental/PdfViewer/pdf_viewer_main.cpp b/experimental/PdfViewer/pdf_viewer_main.cpp index 7541cdfae6..cfea97411d 100644 --- a/experimental/PdfViewer/pdf_viewer_main.cpp +++ b/experimental/PdfViewer/pdf_viewer_main.cpp @@ -9,7 +9,6 @@ #include "SkStream.h" #include "SkTypeface.h" #include "SkTArray.h" -#include "picture_utils.h" #include "SkNulCanvas.h" #include "SkPdfRenderer.h" @@ -74,6 +73,56 @@ static bool add_page_and_replace_filename_extension(SkString* path, int page, return false; } +void make_filepath(SkString* path, const SkString& dir, const SkString& name) { + size_t len = dir.size(); + path->set(dir); + if (0 < len && '/' != dir[len - 1]) { + path->append("/"); + } + path->append(name); +} + +bool is_path_seperator(const char chr) { +#if defined(SK_BUILD_FOR_WIN) + return chr == '\\' || chr == '/'; +#else + return chr == '/'; +#endif +} + +void get_basename(SkString* basename, const SkString& path) { + if (path.size() == 0) { + basename->reset(); + return; + } + + size_t end = path.size() - 1; + + // Paths pointing to directories often have a trailing slash, + // we remove it so the name is not empty + if (is_path_seperator(path[end])) { + if (end == 0) { + basename->reset(); + return; + } + + end -= 1; + } + + size_t i = end; + do { + --i; + if (is_path_seperator(path[i])) { + const char* basenameStart = path.c_str() + i + 1; + size_t basenameLength = end - i; + basename->set(basenameStart, basenameLength); + return; + } + } while (i > 0); + + basename->set(path.c_str(), end + 1); +} + /** Builds the output filename. path = dir/name, and it replaces expected * .skp extension with .pdf extention. * @param path Output filename. @@ -81,12 +130,10 @@ static bool add_page_and_replace_filename_extension(SkString* path, int page, * @returns false if the file did not has the expected extension. * if false is returned, contents of path are undefined. */ - - static bool make_output_filepath(SkString* path, const SkString& dir, const SkString& name, int page) { - sk_tools::make_filepath(path, dir, name); + make_filepath(path, dir, name); return add_page_and_replace_filename_extension(path, page, PDF_FILE_EXTENSION, PNG_FILE_EXTENSION); @@ -167,7 +214,7 @@ static bool process_pdf(const SkString& inputPath, const SkString& outputDir, SkDebugf("Loading PDF: %s\n", inputPath.c_str()); SkString inputFilename; - sk_tools::get_basename(&inputFilename, inputPath); + get_basename(&inputFilename, inputPath); SkFILEStream inputStream; inputStream.setPath(inputPath.c_str()); @@ -241,7 +288,7 @@ static int process_input(const char* input, const SkString& outputDir, SkString inputPath; SkString _input; _input.append(input); - sk_tools::make_filepath(&inputPath, _input, inputFilename); + make_filepath(&inputPath, _input, inputFilename); if (!process_pdf(inputPath, outputDir, renderer)) { ++failures; } diff --git a/experimental/PdfViewer/pdfparser/native/SkNativeParsedPDF.cpp b/experimental/PdfViewer/pdfparser/native/SkNativeParsedPDF.cpp index 4fb7f8895b..8f92a0fcfe 100644 --- a/experimental/PdfViewer/pdfparser/native/SkNativeParsedPDF.cpp +++ b/experimental/PdfViewer/pdfparser/native/SkNativeParsedPDF.cpp @@ -14,6 +14,7 @@ #include "SkPdfPageTreeNodeDictionary_autogen.h" #include "SkPdfMapper_autogen.h" +#include "SkStream.h" static long getFileSize(const char* filename) @@ -67,25 +68,48 @@ SkNativeParsedPDF* gDoc = NULL; // 1) run on a lot of file // 2) recoverable corupt file: remove endobj, endsteam, remove other keywords, use other white spaces, insert comments randomly, ... // 3) irrecoverable corrupt file + +SkNativeParsedPDF::SkNativeParsedPDF(SkStream* stream) + : fAllocator(new SkPdfAllocator()) + , fFileContent(NULL) + , fContentLength(0) + , fRootCatalogRef(NULL) + , fRootCatalog(NULL) { + size_t size = stream->getLength(); + void* ptr = sk_malloc_throw(size); + stream->read(ptr, size); + + init(ptr, size); +} + SkNativeParsedPDF::SkNativeParsedPDF(const char* path) : fAllocator(new SkPdfAllocator()) + , fFileContent(NULL) + , fContentLength(0) , fRootCatalogRef(NULL) , fRootCatalog(NULL) { gDoc = this; FILE* file = fopen(path, "r"); - fContentLength = getFileSize(path); - unsigned char* content = new unsigned char[fContentLength + 1]; - bool ok = (0 != fread(content, fContentLength, 1, file)); - content[fContentLength] = '\0'; - fFileContent = content; + size_t size = getFileSize(path); + void* content = sk_malloc_throw(size); + bool ok = (0 != fread(content, size, 1, file)); fclose(file); file = NULL; if (!ok) { + sk_free(content); // TODO(edisonn): report read error + // TODO(edisonn): not nice to return like this from constructor, create a static + // function that can report NULL for failures. return; // Doc will have 0 pages } + init(content, size); +} + +void SkNativeParsedPDF::init(const void* bytes, size_t length) { + fFileContent = (const unsigned char*)bytes; + fContentLength = length; const unsigned char* eofLine = lineHome(fFileContent, fFileContent + fContentLength - 1); const unsigned char* xrefByteOffsetLine = previousLineHome(fFileContent, eofLine); const unsigned char* xrefstartKeywordLine = previousLineHome(fFileContent, xrefByteOffsetLine); @@ -126,7 +150,7 @@ SkNativeParsedPDF::SkNativeParsedPDF(const char* path) // TODO(edisonn): NYI SkNativeParsedPDF::~SkNativeParsedPDF() { - delete[] fFileContent; + sk_free((void*)fFileContent); delete fAllocator; } diff --git a/experimental/PdfViewer/pdfparser/native/SkNativeParsedPDF.h b/experimental/PdfViewer/pdfparser/native/SkNativeParsedPDF.h index c520b0394a..d55d808a2c 100644 --- a/experimental/PdfViewer/pdfparser/native/SkNativeParsedPDF.h +++ b/experimental/PdfViewer/pdfparser/native/SkNativeParsedPDF.h @@ -19,6 +19,8 @@ class SkPdfPageTreeNodeDictionary; class SkPdfNativeTokenizer; +class SkStream; + class SkNativeParsedPDF { private: struct PublicObjectEntry { @@ -35,7 +37,10 @@ public: // TODO(edisonn): read page N asap, read all file // TODO(edisonn): allow corruptions of file (e.g. missing endobj, missing stream length, ...) // TODO(edisonn): encryption + SkNativeParsedPDF(const char* path); + SkNativeParsedPDF(SkStream* stream); + ~SkNativeParsedPDF(); int pages() const; @@ -65,6 +70,9 @@ public: private: + // Takes ownership of bytes. + void init(const void* bytes, size_t length); + const unsigned char* readCrossReferenceSection(const unsigned char* xrefStart, const unsigned char* trailerEnd); long readTrailer(const unsigned char* trailerStart, const unsigned char* trailerEnd, bool storeCatalog); diff --git a/gyp/pdfviewer.gyp b/gyp/pdfviewer.gyp index ae746fdc83..c138d9ff6d 100644 --- a/gyp/pdfviewer.gyp +++ b/gyp/pdfviewer.gyp @@ -11,10 +11,6 @@ { 'target_name': 'libpdfviewer', 'type': 'static_library', - 'cflags': ['-fexceptions'], - 'cflags_cc': ['-fexceptions'], - 'cflags!': [ '-fno-exceptions' ], - 'cflags_cc!': [ '-fno-exceptions' ], 'sources': [ '../experimental/PdfViewer/SkPdfBasics.cpp', '../experimental/PdfViewer/SkPdfFont.cpp', @@ -91,7 +87,6 @@ 'core.gyp:core', 'flags.gyp:flags', 'libpdfviewer', - 'tools.gyp:picture_utils', ], }, ],