pdf viewer: refactor, add -w parameter to pdfviewer, add -n to remove extenassion page when there is a single page (so skdiff can be run easily) and fix a bug (SkPdfobject should not reset on destruct)

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

git-svn-id: http://skia.googlecode.com/svn/trunk@9983 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/experimental/PdfViewer/SkPdfFont.cpp b/experimental/PdfViewer/SkPdfFont.cpp
index 4f2b279..2574ce4 100644
--- a/experimental/PdfViewer/SkPdfFont.cpp
+++ b/experimental/PdfViewer/SkPdfFont.cpp
@@ -1,5 +1,4 @@
 #include "SkPdfFont.h"
-#include "SkPdfParser.h"
 
 #include "SkStream.h"
 #include "SkTypeface.h"
diff --git a/experimental/PdfViewer/SkPdfParser.h b/experimental/PdfViewer/SkPdfParser.h
deleted file mode 100644
index 00556dc..0000000
--- a/experimental/PdfViewer/SkPdfParser.h
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright 2013 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-
-#ifndef SkPdfParser_DEFINED
-#define SkPdfParser_DEFINED
-
-#include "SkPdfBasics.h"
-#include "SkPdfNativeTokenizer.h"
-
-extern "C" PdfContext* gPdfContext;
-extern "C" SkBitmap* gDumpBitmap;
-extern "C" SkCanvas* gDumpCanvas;
-
-// TODO(edisonn): Document PdfTokenLooper and subclasses.
-class PdfTokenLooper {
-protected:
-    PdfTokenLooper* fParent;
-    SkPdfNativeTokenizer* fTokenizer;
-    PdfContext* fPdfContext;
-    SkCanvas* fCanvas;
-
-public:
-    PdfTokenLooper(PdfTokenLooper* parent,
-                   SkPdfNativeTokenizer* tokenizer,
-                   PdfContext* pdfContext,
-                   SkCanvas* canvas)
-        : fParent(parent), fTokenizer(tokenizer), fPdfContext(pdfContext), fCanvas(canvas) {}
-
-    virtual ~PdfTokenLooper() {}
-
-    virtual PdfResult consumeToken(PdfToken& token) = 0;
-    virtual void loop() = 0;
-
-    void setUp(PdfTokenLooper* parent) {
-        fParent = parent;
-        fTokenizer = parent->fTokenizer;
-        fPdfContext = parent->fPdfContext;
-        fCanvas = parent->fCanvas;
-    }
-};
-
-class PdfMainLooper : public PdfTokenLooper {
-public:
-    PdfMainLooper(PdfTokenLooper* parent,
-                  SkPdfNativeTokenizer* tokenizer,
-                  PdfContext* pdfContext,
-                  SkCanvas* canvas)
-        : PdfTokenLooper(parent, tokenizer, pdfContext, canvas) {}
-
-    virtual PdfResult consumeToken(PdfToken& token);
-    virtual void loop();
-};
-
-class PdfInlineImageLooper : public PdfTokenLooper {
-public:
-    PdfInlineImageLooper()
-        : PdfTokenLooper(NULL, NULL, NULL, NULL) {}
-
-    virtual PdfResult consumeToken(PdfToken& token);
-    virtual void loop();
-    PdfResult done();
-};
-
-class PdfCompatibilitySectionLooper : public PdfTokenLooper {
-public:
-    PdfCompatibilitySectionLooper()
-        : PdfTokenLooper(NULL, NULL, NULL, NULL) {}
-
-    virtual PdfResult consumeToken(PdfToken& token);
-    virtual void loop();
-};
-
-// TODO(edisonn): move in another file
-class SkPdfViewer : public SkRefCnt {
-public:
-
-  bool load(const SkString inputFileName, SkPicture* out);
-  bool write(void*) const { return false; }
-};
-
-void reportPdfRenderStats();
-
-#endif  // SkPdfParser_DEFINED
diff --git a/experimental/PdfViewer/SkPdfParser.cpp b/experimental/PdfViewer/SkPdfRenderer.cpp
similarity index 93%
rename from experimental/PdfViewer/SkPdfParser.cpp
rename to experimental/PdfViewer/SkPdfRenderer.cpp
index 805b877..39cae9c 100644
--- a/experimental/PdfViewer/SkPdfParser.cpp
+++ b/experimental/PdfViewer/SkPdfRenderer.cpp
@@ -45,7 +45,7 @@
 
 #include "SkPdfHeaders_autogen.h"
 #include "SkPdfMapper_autogen.h"
-#include "SkPdfParser.h"
+#include "SkPdfRenderer.h"
 
 #include "SkPdfBasics.h"
 #include "SkPdfUtils.h"
@@ -72,6 +72,67 @@
 
 using namespace std;
 
+
+
+// TODO(edisonn): Document PdfTokenLooper and subclasses.
+class PdfTokenLooper {
+protected:
+    PdfTokenLooper* fParent;
+    SkPdfNativeTokenizer* fTokenizer;
+    PdfContext* fPdfContext;
+    SkCanvas* fCanvas;
+
+public:
+    PdfTokenLooper(PdfTokenLooper* parent,
+                   SkPdfNativeTokenizer* tokenizer,
+                   PdfContext* pdfContext,
+                   SkCanvas* canvas)
+        : fParent(parent), fTokenizer(tokenizer), fPdfContext(pdfContext), fCanvas(canvas) {}
+
+    virtual ~PdfTokenLooper() {}
+
+    virtual PdfResult consumeToken(PdfToken& token) = 0;
+    virtual void loop() = 0;
+
+    void setUp(PdfTokenLooper* parent) {
+        fParent = parent;
+        fTokenizer = parent->fTokenizer;
+        fPdfContext = parent->fPdfContext;
+        fCanvas = parent->fCanvas;
+    }
+};
+
+class PdfMainLooper : public PdfTokenLooper {
+public:
+    PdfMainLooper(PdfTokenLooper* parent,
+                  SkPdfNativeTokenizer* tokenizer,
+                  PdfContext* pdfContext,
+                  SkCanvas* canvas)
+        : PdfTokenLooper(parent, tokenizer, pdfContext, canvas) {}
+
+    virtual PdfResult consumeToken(PdfToken& token);
+    virtual void loop();
+};
+
+class PdfInlineImageLooper : public PdfTokenLooper {
+public:
+    PdfInlineImageLooper()
+        : PdfTokenLooper(NULL, NULL, NULL, NULL) {}
+
+    virtual PdfResult consumeToken(PdfToken& token);
+    virtual void loop();
+    PdfResult done();
+};
+
+class PdfCompatibilitySectionLooper : public PdfTokenLooper {
+public:
+    PdfCompatibilitySectionLooper()
+        : PdfTokenLooper(NULL, NULL, NULL, NULL) {}
+
+    virtual PdfResult consumeToken(PdfToken& token);
+    virtual void loop();
+};
+
 // Utilities
 static void setup_bitmap(SkBitmap* bitmap, int width, int height, SkColor color = SK_ColorWHITE) {
     bitmap->setConfig(SkBitmap::kARGB_8888_Config, width, height);
@@ -127,6 +188,8 @@
     return SkMatrixFromPdfMatrix(array);
 }
 
+
+extern "C" SkNativeParsedPDF* gDoc;
 SkBitmap* gDumpBitmap = NULL;
 SkCanvas* gDumpCanvas = NULL;
 char gLastKeyword[100] = "";
@@ -148,6 +211,8 @@
 }
 #endif  // PDF_TRACE_DIFF_IN_PNG
 
+
+
 // TODO(edisonn): Pass PdfContext and SkCanvasd only with the define for instrumentation.
 static bool readToken(SkPdfNativeTokenizer* fTokenizer, PdfToken* token) {
     bool ret = fTokenizer->readToken(token);
@@ -1856,54 +1921,101 @@
 // TODO (edisonn): hide parser/tokenizer behind and interface and a query language, and resolve
 // references automatically.
 
-bool SkPdfViewer::load(const SkString inputFileName, SkPicture* out) {
-    std::cout << "PDF Loaded: " << inputFileName.c_str() << std::endl;
+PdfContext* gPdfContext = NULL;
 
-    SkNativeParsedPDF* doc = new SkNativeParsedPDF(inputFileName.c_str());
-    if (!doc->pages())
-    {
-        std::cout << "ERROR: Empty PDF Document" << inputFileName.c_str() << std::endl;
+bool SkPdfRenderer::renderPage(int page, SkCanvas* canvas) const {
+    if (!fPdfDoc) {
         return false;
-    } else {
-
-        for (int pn = 0; pn < doc->pages(); ++pn) {
-            // TODO(edisonn): implement inheritance properties as per PDF spec
-            //SkRect rect = page->MediaBox();
-            SkRect rect = doc->MediaBox(pn);
-
-#ifdef PDF_TRACE
-            printf("Page Width: %f, Page Height: %f\n", SkScalarToDouble(rect.width()), SkScalarToDouble(rect.height()));
-#endif
-
-            // TODO(edisonn): page->GetCropBox(), page->GetTrimBox() ... how to use?
-
-            SkBitmap bitmap;
-#ifdef PDF_DEBUG_3X
-            setup_bitmap(&bitmap, 3 * (int)SkScalarToDouble(rect.width()), 3 * (int)SkScalarToDouble(rect.height()));
-#else
-            setup_bitmap(&bitmap, (int)SkScalarToDouble(rect.width()), (int)SkScalarToDouble(rect.height()));
-#endif
-            SkAutoTUnref<SkDevice> device(SkNEW_ARGS(SkDevice, (bitmap)));
-            SkCanvas canvas(device);
-
-            gDumpBitmap = &bitmap;
-
-            gDumpCanvas = &canvas;
-            doc->drawPage(pn, &canvas);
-
-            SkString out;
-            if (doc->pages() > 1) {
-                out.appendf("%s-%i.png", inputFileName.c_str(), pn);
-            } else {
-                out = inputFileName;
-                // .pdf -> .png
-                out[out.size() - 2] = 'n';
-                out[out.size() - 1] = 'g';
-            }
-            SkImageEncoder::EncodeFile(out.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100);
-        }
-        return true;
     }
 
+    if (page < 0 || page >= pages()) {
+        return false;
+    }
+
+    SkPdfNativeTokenizer* tokenizer = fPdfDoc->tokenizerOfPage(page);
+
+    PdfContext pdfContext(fPdfDoc);
+    pdfContext.fOriginalMatrix = SkMatrix::I();
+    pdfContext.fGraphicsState.fResources = fPdfDoc->pageResources(page);
+
+    gPdfContext = &pdfContext;
+
+    // TODO(edisonn): get matrix stuff right.
+    // TODO(edisonn): add DPI/scale/zoom.
+    SkScalar z = SkIntToScalar(0);
+    SkRect rect = fPdfDoc->MediaBox(page);
+    SkScalar w = rect.width();
+    SkScalar h = rect.height();
+
+    SkPoint pdfSpace[4] = {SkPoint::Make(z, z), SkPoint::Make(w, z), SkPoint::Make(w, h), SkPoint::Make(z, h)};
+//                SkPoint skiaSpace[4] = {SkPoint::Make(z, h), SkPoint::Make(w, h), SkPoint::Make(w, z), SkPoint::Make(z, z)};
+
+    // TODO(edisonn): add flag for this app to create sourunding buffer zone
+    // TODO(edisonn): add flagg for no clipping.
+    // Use larger image to make sure we do not draw anything outside of page
+    // could be used in tests.
+
+#ifdef PDF_DEBUG_3X
+    SkPoint skiaSpace[4] = {SkPoint::Make(w+z, h+h), SkPoint::Make(w+w, h+h), SkPoint::Make(w+w, h+z), SkPoint::Make(w+z, h+z)};
+#else
+    SkPoint skiaSpace[4] = {SkPoint::Make(z, h), SkPoint::Make(w, h), SkPoint::Make(w, z), SkPoint::Make(z, z)};
+#endif
+    //SkPoint pdfSpace[2] = {SkPoint::Make(z, z), SkPoint::Make(w, h)};
+    //SkPoint skiaSpace[2] = {SkPoint::Make(w, z), SkPoint::Make(z, h)};
+
+    //SkPoint pdfSpace[2] = {SkPoint::Make(z, z), SkPoint::Make(z, h)};
+    //SkPoint skiaSpace[2] = {SkPoint::Make(z, h), SkPoint::Make(z, z)};
+
+    //SkPoint pdfSpace[3] = {SkPoint::Make(z, z), SkPoint::Make(z, h), SkPoint::Make(w, h)};
+    //SkPoint skiaSpace[3] = {SkPoint::Make(z, h), SkPoint::Make(z, z), SkPoint::Make(w, 0)};
+
+    SkAssertResult(pdfContext.fOriginalMatrix.setPolyToPoly(pdfSpace, skiaSpace, 4));
+    SkTraceMatrix(pdfContext.fOriginalMatrix, "Original matrix");
+
+
+    pdfContext.fGraphicsState.fMatrix = pdfContext.fOriginalMatrix;
+    pdfContext.fGraphicsState.fMatrixTm = pdfContext.fGraphicsState.fMatrix;
+    pdfContext.fGraphicsState.fMatrixTlm = pdfContext.fGraphicsState.fMatrix;
+
+    canvas->setMatrix(pdfContext.fOriginalMatrix);
+
+#ifndef PDF_DEBUG_NO_PAGE_CLIPING
+    canvas->clipRect(SkRect::MakeXYWH(z, z, w, h), SkRegion::kIntersect_Op, true);
+#endif
+
+// erase with red before?
+//        SkPaint paint;
+//        paint.setColor(SK_ColorRED);
+//        canvas->drawRect(rect, paint);
+
+    PdfMainLooper looper(NULL, tokenizer, &pdfContext, canvas);
+    looper.loop();
+
+    delete tokenizer;
+
+    canvas->flush();
     return true;
 }
+
+bool SkPdfRenderer::load(const SkString inputFileName) {
+    unload();
+
+    // TODO(edisonn): create static function that could return NULL if there are errors
+    fPdfDoc = new SkNativeParsedPDF(inputFileName.c_str());
+
+    return fPdfDoc != NULL;
+}
+
+int SkPdfRenderer::pages() const {
+    return fPdfDoc != NULL ? fPdfDoc->pages() : 0;
+}
+
+void SkPdfRenderer::unload() {
+    delete fPdfDoc;
+    fPdfDoc = NULL;
+}
+
+SkRect SkPdfRenderer::MediaBox(int page) const {
+    SkASSERT(fPdfDoc);
+    return fPdfDoc->MediaBox(page);
+}
diff --git a/experimental/PdfViewer/SkPdfRenderer.h b/experimental/PdfViewer/SkPdfRenderer.h
new file mode 100644
index 0000000..ba556ff
--- /dev/null
+++ b/experimental/PdfViewer/SkPdfRenderer.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#ifndef SkPdfParser_DEFINED
+#define SkPdfParser_DEFINED
+
+#include "SkPdfBasics.h"
+#include "SkPdfNativeTokenizer.h"
+
+extern "C" PdfContext* gPdfContext;
+extern "C" SkBitmap* gDumpBitmap;
+extern "C" SkCanvas* gDumpCanvas;
+
+class SkCanvas;
+class SkNativeParsedPDF;
+
+// TODO(edisonn): move in another file
+class SkPdfRenderer : public SkRefCnt {
+    SkNativeParsedPDF* fPdfDoc;
+public:
+    SkPdfRenderer() : fPdfDoc(NULL) {}
+    virtual ~SkPdfRenderer() {unload();}
+
+    // TODO(edisonn): add options to render forms, or not
+    bool renderPage(int page, SkCanvas* canvas) const;
+
+    bool load(const SkString inputFileName);
+    bool loaded() const {return fPdfDoc != NULL;}
+    int pages() const;
+    void unload();
+    SkRect MediaBox(int page) const;
+};
+
+void reportPdfRenderStats();
+
+#endif  // SkPdfParser_DEFINED
diff --git a/experimental/PdfViewer/pdf_viewer_main.cpp b/experimental/PdfViewer/pdf_viewer_main.cpp
index ac1aef0..45bfff3 100644
--- a/experimental/PdfViewer/pdf_viewer_main.cpp
+++ b/experimental/PdfViewer/pdf_viewer_main.cpp
@@ -11,7 +11,7 @@
 #include "SkTArray.h"
 #include "picture_utils.h"
 
-#include "SkPdfParser.h"
+#include "SkPdfRenderer.h"
 
 /**
  * Given list of directories and files to use as input, expects to find .pdf
@@ -30,7 +30,7 @@
     SkDebugf("PDF to PNG rendering tool\n");
     SkDebugf("\n"
 "Usage: \n"
-"     %s <input>... -w <outputDir> \n"
+"     %s <input>... [-w <outputDir>] [-n | --no-page-ext] \n"
 , argv0);
     SkDebugf("\n\n");
     SkDebugf(
@@ -38,6 +38,8 @@
 "                expected to have the .skp extension.\n\n");
     SkDebugf(
 "     outputDir: directory to write the rendered pdfs.\n\n");
+    SkDebugf(
+"     -n:        no page extension if only one page.\n\n");
     SkDebugf("\n");
 }
 
@@ -48,8 +50,7 @@
  * @returns false if the file did not has the expected extension.
  *  if false is returned, contents of path are undefined.
  */
-/*
-static bool replace_filename_extension(SkString* path,
+static bool add_page_and_replace_filename_extension(SkString* path, int page,
                                        const char old_extension[],
                                        const char new_extension[]) {
     if (path->endsWith(old_extension)) {
@@ -58,12 +59,15 @@
         if (!path->endsWith(".")) {
             return false;
         }
+        if (page >= 0) {
+            path->appendf("%i.", page);
+        }
         path->append(new_extension);
         return true;
     }
     return false;
 }
-*/
+
 /** Builds the output filename. path = dir/name, and it replaces expected
  * .skp extension with .pdf extention.
  * @param path Output filename.
@@ -72,52 +76,76 @@
  *  if false is returned, contents of path are undefined.
  */
 
-/*
+
 static bool make_output_filepath(SkString* path, const SkString& dir,
-                                 const SkString& name) {
+                                 const SkString& name,
+                                 int page) {
     sk_tools::make_filepath(path, dir, name);
-    return replace_filename_extension(path,
-                                      PDF_FILE_EXTENSION,
-                                      PNG_FILE_EXTENSION);
+    return add_page_and_replace_filename_extension(path, page,
+                                                   PDF_FILE_EXTENSION,
+                                                   PNG_FILE_EXTENSION);
 }
-*/
+
+static void setup_bitmap(SkBitmap* bitmap, int width, int height, SkColor color = SK_ColorWHITE) {
+    bitmap->setConfig(SkBitmap::kARGB_8888_Config, width, height);
+
+    bitmap->allocPixels();
+    bitmap->eraseColor(color);
+}
+
 /** Write the output of pdf renderer to a file.
  * @param outputDir Output dir.
  * @param inputFilename The skp file that was read.
  * @param renderer The object responsible to write the pdf file.
  */
-/*
-static bool write_output(const SkString& outputDir,
+
+static bool render_page(const SkString& outputDir,
                          const SkString& inputFilename,
-                         const SkPdfViewer& renderer) {
+                         const SkPdfRenderer& renderer,
+                         int page) {
     if (outputDir.isEmpty()) {
-        SkDynamicMemoryWStream stream;
-        renderer.write(&stream);
-        return true;
+        SkBitmap bitmap;
+        setup_bitmap(&bitmap, 1, 1);
+        SkAutoTUnref<SkDevice> device(SkNEW_ARGS(SkDevice, (bitmap)));
+        SkCanvas canvas(device);
+        return renderer.renderPage(page < 0 ? 0 : page, &canvas);
     }
 
     SkString outputPath;
-    if (!make_output_filepath(&outputPath, outputDir, inputFilename)) {
+    if (!make_output_filepath(&outputPath, outputDir, inputFilename, page)) {
         return false;
     }
 
-    SkFILEWStream stream(outputPath.c_str());
-    if (!stream.isValid()) {
-        SkDebugf("Could not write to file %s\n", outputPath.c_str());
-        return false;
-    }
-    renderer.write(&stream);
+    SkRect rect = renderer.MediaBox(page < 0 ? 0 :page);
+
+    SkBitmap bitmap;
+#ifdef PDF_DEBUG_3X
+    setup_bitmap(&bitmap, 3 * (int)SkScalarToDouble(rect.width()), 3 * (int)SkScalarToDouble(rect.height()));
+#else
+    setup_bitmap(&bitmap, (int)SkScalarToDouble(rect.width()), (int)SkScalarToDouble(rect.height()));
+#endif
+    SkAutoTUnref<SkDevice> device(SkNEW_ARGS(SkDevice, (bitmap)));
+    SkCanvas canvas(device);
+
+    gDumpBitmap = &bitmap;
+
+    gDumpCanvas = &canvas;
+    renderer.renderPage(page, &canvas);
+
+    SkImageEncoder::EncodeFile(outputPath.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100);
 
     return true;
 }
-*/
+
 /** Reads an skp file, renders it to pdf and writes the output to a pdf file
  * @param inputPath The skp file to be read.
  * @param outputDir Output dir.
  * @param renderer The object responsible to render the skp object into pdf.
  */
-static bool parse_pdf(const SkString& inputPath, const SkString& outputDir,
-                       SkPdfViewer& renderer) {
+static bool process_pdf(const SkString& inputPath, const SkString& outputDir,
+                        SkPdfRenderer& renderer, bool noPageExt) {
+    SkDebugf("Loading PDF:  %s\n", inputPath.c_str());
+
     SkString inputFilename;
     sk_tools::get_basename(&inputFilename, inputPath);
 
@@ -130,12 +158,20 @@
 
     bool success = false;
 
-    success = renderer.load(inputPath, NULL);
+    success = renderer.load(inputPath);
 
+    if (success) {
+        if (!renderer.pages())
+        {
+            SkDebugf("ERROR: Empty PDF Document %s\n", inputPath.c_str());
+            return false;
+        } else {
+            for (int pn = 0; pn < renderer.pages(); ++pn) {
+                success = render_page(outputDir, inputFilename, renderer, noPageExt && renderer.pages() == 1 ? -1 : pn) && success;
+            }
+        }
+    }
 
-//    success = write_output(outputDir, inputFilename, renderer);
-
-    //renderer.end();
     return success;
 }
 
@@ -146,7 +182,7 @@
  * @param renderer The object responsible to render the skp object into pdf.
  */
 static int process_input(const SkString& input, const SkString& outputDir,
-                         SkPdfViewer& renderer) {
+                         SkPdfRenderer& renderer, bool noPageExt) {
     int failures = 0;
     if (sk_isdir(input.c_str())) {
         SkOSFile::Iter iter(input.c_str(), PDF_FILE_EXTENSION);
@@ -154,13 +190,13 @@
         while (iter.next(&inputFilename)) {
             SkString inputPath;
             sk_tools::make_filepath(&inputPath, input, inputFilename);
-            if (!parse_pdf(inputPath, outputDir, renderer)) {
+            if (!process_pdf(inputPath, outputDir, renderer, noPageExt)) {
                 ++failures;
             }
         }
     } else {
         SkString inputPath(input);
-        if (!parse_pdf(inputPath, outputDir, renderer)) {
+        if (!process_pdf(inputPath, outputDir, renderer, noPageExt)) {
             ++failures;
         }
     }
@@ -169,7 +205,7 @@
 
 static void parse_commandline(int argc, char* const argv[],
                               SkTArray<SkString>* inputs,
-                              SkString* outputDir) {
+                              SkString* outputDir, bool* noPageExt) {
     const char* argv0 = argv[0];
     char* const* stop = argv + argc;
 
@@ -177,6 +213,8 @@
         if ((0 == strcmp(*argv, "-h")) || (0 == strcmp(*argv, "--help"))) {
             usage(argv0);
             exit(-1);
+        } else if ((0 == strcmp(*argv, "-n")) || (0 == strcmp(*argv, "--no-page-ext"))) {
+            *noPageExt = true;
         } else if (0 == strcmp(*argv, "-w")) {
             ++argv;
             if (argv >= stop) {
@@ -201,16 +239,16 @@
     SkAutoGraphics ag;
     SkTArray<SkString> inputs;
 
-    SkAutoTUnref<SkPdfViewer>
-        renderer(SkNEW(SkPdfViewer));
-    SkASSERT(renderer.get());
+    SkPdfRenderer renderer;
 
     SkString outputDir;
-    parse_commandline(argc, argv, &inputs, &outputDir);
+    bool noPageExt = false;
+    parse_commandline(argc, argv, &inputs, &outputDir, &noPageExt);
 
     int failures = 0;
     for (int i = 0; i < inputs.count(); i ++) {
-        failures += process_input(inputs[i], outputDir, *renderer);
+        failures += process_input(inputs[i], outputDir, renderer, noPageExt);
+        renderer.unload();
     }
 
     reportPdfRenderStats();
diff --git a/experimental/PdfViewer/pdfparser/native/SkNativeParsedPDF.cpp b/experimental/PdfViewer/pdfparser/native/SkNativeParsedPDF.cpp
index b359a8d..5f16176 100644
--- a/experimental/PdfViewer/pdfparser/native/SkNativeParsedPDF.cpp
+++ b/experimental/PdfViewer/pdfparser/native/SkNativeParsedPDF.cpp
@@ -1,7 +1,6 @@
 #include "SkNativeParsedPDF.h"
 #include "SkPdfNativeTokenizer.h"
 #include "SkPdfBasics.h"
-#include "SkPdfParser.h"
 #include "SkPdfObject.h"
 
 #include <stdio.h>
@@ -59,6 +58,7 @@
     return current;
 }
 
+SkNativeParsedPDF* gDoc = NULL;
 
 // TODO(edisonn): NYI
 // TODO(edisonn): 3 constructuctors from URL, from stream, from file ...
@@ -68,10 +68,12 @@
 // 2) recoverable corupt file: remove endobj, endsteam, remove other keywords, use other white spaces, insert comments randomly, ...
 // 3) irrecoverable corrupt file
 SkNativeParsedPDF::SkNativeParsedPDF(const char* path) : fAllocator(new SkPdfAllocator()) {
+    gDoc = this;
     FILE* file = fopen(path, "r");
     fContentLength = getFileSize(path);
-    fFileContent = new unsigned char[fContentLength];
+    fFileContent = new unsigned char[fContentLength + 1];
     fread(fFileContent, fContentLength, 1, file);
+    fFileContent[fContentLength] = '\0';
     fclose(file);
     file = NULL;
 
@@ -355,73 +357,6 @@
     return (SkPdfString*)obj;
 }
 
-PdfContext* gPdfContext = NULL;
-
-void SkNativeParsedPDF::drawPage(int page, SkCanvas* canvas) {
-    SkPdfNativeTokenizer* tokenizer = tokenizerOfPage(page);
-
-    PdfContext pdfContext(this);
-    pdfContext.fOriginalMatrix = SkMatrix::I();
-    pdfContext.fGraphicsState.fResources = pageResources(page);
-
-    gPdfContext = &pdfContext;
-
-    // TODO(edisonn): get matrix stuff right.
-    // TODO(edisonn): add DPI/scale/zoom.
-    SkScalar z = SkIntToScalar(0);
-    SkRect rect = MediaBox(page);
-    SkScalar w = rect.width();
-    SkScalar h = rect.height();
-
-    SkPoint pdfSpace[4] = {SkPoint::Make(z, z), SkPoint::Make(w, z), SkPoint::Make(w, h), SkPoint::Make(z, h)};
-//                SkPoint skiaSpace[4] = {SkPoint::Make(z, h), SkPoint::Make(w, h), SkPoint::Make(w, z), SkPoint::Make(z, z)};
-
-    // TODO(edisonn): add flag for this app to create sourunding buffer zone
-    // TODO(edisonn): add flagg for no clipping.
-    // Use larger image to make sure we do not draw anything outside of page
-    // could be used in tests.
-
-#ifdef PDF_DEBUG_3X
-    SkPoint skiaSpace[4] = {SkPoint::Make(w+z, h+h), SkPoint::Make(w+w, h+h), SkPoint::Make(w+w, h+z), SkPoint::Make(w+z, h+z)};
-#else
-    SkPoint skiaSpace[4] = {SkPoint::Make(z, h), SkPoint::Make(w, h), SkPoint::Make(w, z), SkPoint::Make(z, z)};
-#endif
-    //SkPoint pdfSpace[2] = {SkPoint::Make(z, z), SkPoint::Make(w, h)};
-    //SkPoint skiaSpace[2] = {SkPoint::Make(w, z), SkPoint::Make(z, h)};
-
-    //SkPoint pdfSpace[2] = {SkPoint::Make(z, z), SkPoint::Make(z, h)};
-    //SkPoint skiaSpace[2] = {SkPoint::Make(z, h), SkPoint::Make(z, z)};
-
-    //SkPoint pdfSpace[3] = {SkPoint::Make(z, z), SkPoint::Make(z, h), SkPoint::Make(w, h)};
-    //SkPoint skiaSpace[3] = {SkPoint::Make(z, h), SkPoint::Make(z, z), SkPoint::Make(w, 0)};
-
-    SkAssertResult(pdfContext.fOriginalMatrix.setPolyToPoly(pdfSpace, skiaSpace, 4));
-    SkTraceMatrix(pdfContext.fOriginalMatrix, "Original matrix");
-
-
-    pdfContext.fGraphicsState.fMatrix = pdfContext.fOriginalMatrix;
-    pdfContext.fGraphicsState.fMatrixTm = pdfContext.fGraphicsState.fMatrix;
-    pdfContext.fGraphicsState.fMatrixTlm = pdfContext.fGraphicsState.fMatrix;
-
-    canvas->setMatrix(pdfContext.fOriginalMatrix);
-
-#ifndef PDF_DEBUG_NO_PAGE_CLIPING
-    canvas->clipRect(SkRect::MakeXYWH(z, z, w, h), SkRegion::kIntersect_Op, true);
-#endif
-
-// erase with red before?
-//        SkPaint paint;
-//        paint.setColor(SK_ColorRED);
-//        canvas->drawRect(rect, paint);
-
-    PdfMainLooper looper(NULL, tokenizer, &pdfContext, canvas);
-    looper.loop();
-
-    delete tokenizer;
-
-    canvas->flush();
-}
-
 SkPdfAllocator* SkNativeParsedPDF::allocator() const {
     return fAllocator;
 }
diff --git a/experimental/PdfViewer/pdfparser/native/SkPdfNativeTokenizer.cpp b/experimental/PdfViewer/pdfparser/native/SkPdfNativeTokenizer.cpp
index 53987fa..fc3e0b9 100644
--- a/experimental/PdfViewer/pdfparser/native/SkPdfNativeTokenizer.cpp
+++ b/experimental/PdfViewer/pdfparser/native/SkPdfNativeTokenizer.cpp
@@ -656,8 +656,14 @@
         free(fHandles[i]);
     }
     for (int i = 0 ; i < fHistory.count(); i++) {
+        for (int j = 0 ; j < BUFFER_SIZE; j++) {
+            fHistory[i][j].reset();
+        }
         delete[] fHistory[i];
     }
+    for (int j = 0 ; j < BUFFER_SIZE; j++) {
+        fCurrent[j].reset();
+    }
     delete[] fCurrent;
 }
 
@@ -667,7 +673,6 @@
         fCurrent = allocBlock();
         fCurrentUsed = 0;
     }
-
     fCurrentUsed++;
     return &fCurrent[fCurrentUsed - 1];
 }
@@ -677,26 +682,28 @@
     unsigned char* buffer = NULL;
     size_t len = 0;
     objWithStream->GetFilteredStreamRef(&buffer, &len, fAllocator);
+    // TODO(edisonn): hack, find end of object
+    char* endobj = strstr((char*)buffer, "endobj");
+    if (endobj) {
+        len = endobj - (char*)buffer + strlen("endobj");
+    }
     fUncompressedStreamStart = fUncompressedStream = (unsigned char*)fAllocator->alloc(len);
     fUncompressedStreamEnd = fUncompressedStream + len;
-    memcpy(fUncompressedStream, buffer, len);}
+    memcpy(fUncompressedStream, buffer, len);
+}
 
 SkPdfNativeTokenizer::SkPdfNativeTokenizer(unsigned char* buffer, int len, const SkPdfMapper* mapper, SkPdfAllocator* allocator) : fMapper(mapper), fAllocator(allocator), fEmpty(false), fHasPutBack(false) {
+    // TODO(edisonn): hack, find end of object
+    char* endobj = strstr((char*)buffer, "endobj");
+    if (endobj) {
+        len = endobj - (char*)buffer + strlen("endobj");
+    }
     fUncompressedStreamStart = fUncompressedStream = (unsigned char*)fAllocator->alloc(len);
     fUncompressedStreamEnd = fUncompressedStream + len;
     memcpy(fUncompressedStream, buffer, len);
 }
 
 SkPdfNativeTokenizer::~SkPdfNativeTokenizer() {
-    // free the unparsed stream, we don't need it.
-    // the parsed one is locked as it contains the strings and keywords referenced in objects
-    if (fUncompressedStream) {
-        void* dummy = realloc(fUncompressedStreamStart, fUncompressedStream - fUncompressedStreamStart);
-        //SkASSERT(dummy == fUncompressedStreamStart);
-        fUncompressedStreamStart = (unsigned char*)dummy;  // suppress compiler warning
-    } else {
-        SkASSERT(false);
-    }
 }
 
 bool SkPdfNativeTokenizer::readTokenCore(PdfToken* token) {
@@ -726,7 +733,7 @@
 #ifdef PDF_TRACE
     static int read_op = 0;
     read_op++;
-    if (182749 == read_op) {
+    if (548 == read_op) {
         printf("break;\n");
     }
     printf("%i READ %s %s\n", read_op, token->fType == kKeyword_TokenType ? "Keyword" : "Object", token->fKeyword ? std::string(token->fKeyword, token->fKeywordLength).c_str() : token->fObject->toString().c_str());
@@ -763,3 +770,4 @@
 
     return readTokenCore(token);
 }
+
diff --git a/experimental/PdfViewer/pdfparser/native/SkPdfObject.h b/experimental/PdfViewer/pdfparser/native/SkPdfObject.h
index 86963b0..a215501 100644
--- a/experimental/PdfViewer/pdfparser/native/SkPdfObject.h
+++ b/experimental/PdfViewer/pdfparser/native/SkPdfObject.h
@@ -86,7 +86,7 @@
 
 public:
 
-    SkPdfObject() : fObjectType(kInvalid_PdfObjectType), fData(NULL) {}
+    SkPdfObject() : fObjectType(kInvalid_PdfObjectType), fMap(NULL), fData(NULL) {}
 
     inline void* data() {
         return fData;
@@ -96,9 +96,9 @@
         fData = data;
     }
 
-    ~SkPdfObject() {
-        reset();
-    }
+//    ~SkPdfObject() {
+//        //reset();  must be called manually!
+//    }
 
     void reset() {
         switch (fObjectType) {