pdfviewer: remove dependency on picture_utils. add utility function to render SkStream into bitmap.

Review URL: https://codereview.chromium.org/20087003

git-svn-id: http://skia.googlecode.com/svn/trunk@10314 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/experimental/PdfViewer/SkPdfRenderer.cpp b/experimental/PdfViewer/SkPdfRenderer.cpp
index 33798cb..c51c239 100644
--- a/experimental/PdfViewer/SkPdfRenderer.cpp
+++ b/experimental/PdfViewer/SkPdfRenderer.cpp
@@ -2030,6 +2030,20 @@
     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 @@
 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<SkDevice> 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 8bbc4f1..b3c4ae3 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 @@
     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 @@
 
 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 7541cdf..cfea974 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 @@
     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 @@
  * @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 @@
     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 @@
             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 4fb7f88..8f92a0f 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 @@
 // 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 @@
 
 // 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 c520b03..d55d808 100644
--- a/experimental/PdfViewer/pdfparser/native/SkNativeParsedPDF.h
+++ b/experimental/PdfViewer/pdfparser/native/SkNativeParsedPDF.h
@@ -19,6 +19,8 @@
 
 class SkPdfNativeTokenizer;
 
+class SkStream;
+
 class SkNativeParsedPDF {
 private:
     struct PublicObjectEntry {
@@ -35,7 +37,10 @@
     // 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 @@
 
 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);