experimental/skp_to_pdf_md5 optionally also outputs pdf files

TBR=mtklein@google.com

Review URL: https://codereview.chromium.org/868333002
diff --git a/experimental/tools/SkDmuxWStream.cpp b/experimental/tools/SkDmuxWStream.cpp
new file mode 100644
index 0000000..6dde5bf
--- /dev/null
+++ b/experimental/tools/SkDmuxWStream.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkDmuxWStream.h"
+
+SkDmuxWStream::SkDmuxWStream(SkWStream* const streamArray[], size_t count)
+    : fWStreams(streamArray, static_cast<int>(count)), fBytesWritten(0) {}
+
+SkDmuxWStream::~SkDmuxWStream() {
+    for (int i = 0; i < fWStreams.count(); ++i) {
+        fWStreams[i]->flush();
+    }
+}
+
+bool SkDmuxWStream::write(const void* buffer, size_t size) {
+    for (int i = 0; i < fWStreams.count(); ++i) {
+        if (!fWStreams[i]->write(buffer, size)) {
+            return false;
+        }
+    }
+    fBytesWritten += size;
+    return true;
+}
+
+void SkDmuxWStream::newline() {
+    for (int i = 0; i < fWStreams.count(); ++i) {
+        fWStreams[i]->newline();
+    }
+    fBytesWritten += 1;  // This may be a lie.
+}
+
+void SkDmuxWStream::flush() {
+    for (int i = 0; i < fWStreams.count(); ++i) {
+        fWStreams[i]->flush();
+    }
+}
+
+size_t SkDmuxWStream::bytesWritten() const { return fBytesWritten; }
diff --git a/experimental/tools/SkDmuxWStream.h b/experimental/tools/SkDmuxWStream.h
new file mode 100644
index 0000000..52376c5
--- /dev/null
+++ b/experimental/tools/SkDmuxWStream.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#ifndef SkDmuxWStream_DEFINED
+#define SkDmuxWStream_DEFINED
+
+#include "SkStream.h"
+#include "SkTDArray.h"
+
+/**
+ *  A SkWStream Demultiplexer.  If initialized with
+ *      SkDmuxWStream dmuxWStream(NULL, 0);
+ *  then it becomes a /dev/null.
+ */
+class SkDmuxWStream : public SkWStream {
+public:
+    SkDmuxWStream(SkWStream* const streamArray[], size_t count);
+    ~SkDmuxWStream();
+    virtual bool write(const void* buffer, size_t size) SK_OVERRIDE;
+    virtual void newline() SK_OVERRIDE;
+    virtual void flush() SK_OVERRIDE;
+    virtual size_t bytesWritten() const SK_OVERRIDE;
+
+private:
+    SkTDArray<SkWStream*> fWStreams;
+    size_t fBytesWritten;
+};
+
+#endif  // SkDmuxWStream_DEFINED
diff --git a/experimental/tools/skp_to_pdf_md5.cpp b/experimental/tools/skp_to_pdf_md5.cpp
index 3232c57..87f5045 100644
--- a/experimental/tools/skp_to_pdf_md5.cpp
+++ b/experimental/tools/skp_to_pdf_md5.cpp
@@ -16,6 +16,8 @@
 #include "SkTArray.h"
 #include "SkTSort.h"
 
+#include "SkDmuxWStream.h"
+
 static const char kUsage[] =
     "This program takes a list of Skia Picture (SKP) files and renders\n"
     "each as a multipage PDF, then prints out the MD5 checksum of the\n"
@@ -30,7 +32,10 @@
                "A list of directories and files to use as input.\n"
                "Files are expected to have the .skp extension.");
 
+DEFINE_string2(outputDirectoryPath, w, "", "TODO: document this");
+
 static const char SKP_FILE_EXTENSION[] = ".skp";
+static const char PDF_FILE_EXTENSION[] = ".pdf";
 
 // Used by SkTQSort<SkString>()
 static bool operator<(const SkString& a, const SkString& b) {
@@ -111,6 +116,24 @@
     return true;
 }
 
+static bool skp_to_pdf_and_md5(SkStream* input,
+                               const char* path,
+                               SkMD5::Digest* digest) {
+    SkAutoTUnref<SkPicture> picture(SkPicture::CreateFromStream(input));
+    if (NULL == picture.get()) {
+        return false;
+    }
+
+    SkMD5 checksumWStream;
+    SkFILEWStream fileWStream(path);
+    SkWStream* wStreamArray[] = {&checksumWStream, &fileWStream};
+    SkDmuxWStream dmuxWStream(wStreamArray, SK_ARRAY_COUNT(wStreamArray));
+
+    picture_to_pdf(*picture, &dmuxWStream);
+    checksumWStream.finish(*digest);
+    return true;
+}
+
 SkString digest_to_hex(const SkMD5::Digest& digest) {
     static const char kHex[] = "0123456789ABCDEF";
     SkString string(2 * sizeof(digest.data));
@@ -123,9 +146,25 @@
     return string;
 }
 
+static void str_replace_ending(SkString* str,
+                               const char* oldExt,
+                               const char* newExt) {
+    SkASSERT(str->endsWith(oldExt));
+    SkASSERT(str->size() >= strlen(oldExt));
+    str->remove(str->size() - strlen(oldExt), strlen(oldExt));
+    str->append(newExt);
+}
+
 int main(int argc, char** argv) {
     SkCommandLineFlags::SetUsage(kUsage);
     SkCommandLineFlags::Parse(argc, argv);
+    const char* outputDir = FLAGS_outputDirectoryPath.count() > 0
+                                    ? FLAGS_outputDirectoryPath[0]
+                                    : NULL;
+    if (outputDir) {
+        sk_mkdir(outputDir);
+    }
+
     SkAutoGraphics ag;
     int successCount = 0;
     SkTArray<SkString> files;
@@ -141,9 +180,19 @@
             continue;
         }
         SkMD5::Digest digest;
-        if (!skp_to_pdf_md5(&inputStream, &digest)) {
-            SkDebugf("invalid_skp %s\n", basename.c_str());
-            continue;
+
+        if (outputDir) {
+            SkString path = SkOSPath::Join(outputDir, basename.c_str());
+            str_replace_ending(&path, SKP_FILE_EXTENSION, PDF_FILE_EXTENSION);
+            if (!skp_to_pdf_and_md5(&inputStream, path.c_str(), &digest)) {
+                SkDebugf("invalid_skp %s\n", basename.c_str());
+                continue;
+            }
+        } else {
+            if (!skp_to_pdf_md5(&inputStream, &digest)) {
+                SkDebugf("invalid_skp %s\n", basename.c_str());
+                continue;
+            }
         }
         SkString hexDigest = digest_to_hex(digest);
         printf("%s %s\n", hexDigest.c_str(), basename.c_str());
@@ -151,3 +200,4 @@
     }
     return successCount == files.count() ? 0 : 1;
 }
+
diff --git a/gyp/experimental.gyp b/gyp/experimental.gyp
index 8ad3bff..6405e81 100644
--- a/gyp/experimental.gyp
+++ b/gyp/experimental.gyp
@@ -93,6 +93,7 @@
       'type': 'executable',
       'sources': [
         '../experimental/tools/skp_to_pdf_md5.cpp',
+        '../experimental/tools/SkDmuxWStream.cpp',
       ],
       'include_dirs': [
         '../src/core',