| Index: gm/gmmain.cpp | 
| diff --git a/gm/gmmain.cpp b/gm/gmmain.cpp | 
| index 09dd96eb03f7f74fb30c4b96e598f14e562edf7d..cffe5c4738334b30dbf9c6eac88550db4c225e78 100644 | 
| --- a/gm/gmmain.cpp | 
| +++ b/gm/gmmain.cpp | 
| @@ -30,6 +30,7 @@ | 
| #include "SkImageDecoder.h" | 
| #include "SkImageEncoder.h" | 
| #include "SkOSFile.h" | 
| +#include "SkPDFRasterizer.h" | 
| #include "SkPicture.h" | 
| #include "SkRefCnt.h" | 
| #include "SkStream.h" | 
| @@ -83,9 +84,6 @@ extern bool gSkSuppressFontCachePurgeSpew; | 
|  | 
| #ifdef SK_BUILD_FOR_MAC | 
| #include "SkCGUtils.h" | 
| -    #define CAN_IMAGE_PDF   1 | 
| -#else | 
| -    #define CAN_IMAGE_PDF   0 | 
| #endif | 
|  | 
| using namespace skiagm; | 
| @@ -160,6 +158,12 @@ struct ConfigData { | 
| bool                            fRunByDefault; | 
| }; | 
|  | 
| +struct PDFRasterizerData { | 
| +    bool        (*fRasterizerFunction)(SkStream*, SkBitmap*); | 
| +    const char* fName; | 
| +    bool        fRunByDefault; | 
| +}; | 
| + | 
| class BWTextDrawFilter : public SkDrawFilter { | 
| public: | 
| virtual bool filter(SkPaint*, Type) SK_OVERRIDE; | 
| @@ -282,14 +286,19 @@ public: | 
| } | 
| } | 
|  | 
| -    static bool write_bitmap(const SkString& path, const SkBitmap& bitmap) { | 
| +    static ErrorCombination write_bitmap(const SkString& path, const SkBitmap& bitmap) { | 
| // TODO(epoger): Now that we have removed force_all_opaque() | 
| // from this method, we should be able to get rid of the | 
| // transformation to 8888 format also. | 
| SkBitmap copy; | 
| bitmap.copyTo(©, SkBitmap::kARGB_8888_Config); | 
| -        return SkImageEncoder::EncodeFile(path.c_str(), copy, | 
| -                                          SkImageEncoder::kPNG_Type, 100); | 
| +        if (!SkImageEncoder::EncodeFile(path.c_str(), copy, | 
| +                                        SkImageEncoder::kPNG_Type, | 
| +                                        100)) { | 
| +            gm_fprintf(stderr, "FAILED to write bitmap: %s\n", path.c_str()); | 
| +            return ErrorCombination(kWritingReferenceImage_ErrorType); | 
| +        } | 
| +        return kEmpty_ErrorCombination; | 
| } | 
|  | 
| /** | 
| @@ -410,9 +419,13 @@ public: | 
| gm_fprintf(stdout, "(results marked with [*] will cause nonzero return value)\n"); | 
| } | 
|  | 
| -    static bool write_document(const SkString& path, SkStreamAsset* asset) { | 
| +    static ErrorCombination write_document(const SkString& path, SkStreamAsset* asset) { | 
| SkFILEWStream stream(path.c_str()); | 
| -        return stream.writeStream(asset, asset->getLength()); | 
| +        if (!stream.writeStream(asset, asset->getLength())) { | 
| +            gm_fprintf(stderr, "FAILED to write document: %s\n", path.c_str()); | 
| +            return ErrorCombination(kWritingReferenceImage_ErrorType); | 
| +        } | 
| +        return kEmpty_ErrorCombination; | 
| } | 
|  | 
| /** | 
| @@ -651,51 +664,6 @@ public: | 
| #endif | 
| } | 
|  | 
| -    ErrorCombination write_reference_image(const ConfigData& gRec, const char writePath [], | 
| -                                           const char renderModeDescriptor [], | 
| -                                           const char *shortName, | 
| -                                           const BitmapAndDigest* bitmapAndDigest, | 
| -                                           SkStreamAsset* document) { | 
| -        SkString path; | 
| -        bool success = false; | 
| -        if (gRec.fBackend == kRaster_Backend || | 
| -            gRec.fBackend == kGPU_Backend || | 
| -            (gRec.fBackend == kPDF_Backend && CAN_IMAGE_PDF)) { | 
| - | 
| -            path = make_bitmap_filename(writePath, shortName, gRec.fName, renderModeDescriptor, | 
| -                                        bitmapAndDigest->fDigest); | 
| -            success = write_bitmap(path, bitmapAndDigest->fBitmap); | 
| -        } | 
| -        if (kPDF_Backend == gRec.fBackend) { | 
| -            path = make_filename(writePath, shortName, gRec.fName, renderModeDescriptor, | 
| -                                 "pdf"); | 
| -            success = write_document(path, document); | 
| -        } | 
| -        if (kXPS_Backend == gRec.fBackend) { | 
| -            path = make_filename(writePath, shortName, gRec.fName, renderModeDescriptor, | 
| -                                 "xps"); | 
| -            success = write_document(path, document); | 
| -        } | 
| -        if (success) { | 
| -            return kEmpty_ErrorCombination; | 
| -        } else { | 
| -            gm_fprintf(stderr, "FAILED to write %s\n", path.c_str()); | 
| -            ErrorCombination errors(kWritingReferenceImage_ErrorType); | 
| -            // TODO(epoger): Don't call RecordTestResults() here... | 
| -            // Instead, we should make sure to call RecordTestResults | 
| -            // exactly ONCE per test.  (Otherwise, gmmain.fTestsRun | 
| -            // will be incremented twice for this test: once in | 
| -            // compare_test_results_to_stored_expectations() before | 
| -            // that method calls this one, and again here.) | 
| -            // | 
| -            // When we make that change, we should probably add a | 
| -            // WritingReferenceImage test to the gm self-tests.) | 
| -            RecordTestResults(errors, make_shortname_plus_config(shortName, gRec.fName), | 
| -                              renderModeDescriptor); | 
| -            return errors; | 
| -        } | 
| -    } | 
| - | 
| /** | 
| * Log more detail about the mistmatch between expectedBitmap and | 
| * actualBitmap. | 
| @@ -824,7 +792,6 @@ public: | 
| completeName); | 
| } | 
| } | 
| -        RecordTestResults(errors, shortNamePlusConfig, renderModeDescriptor); | 
|  | 
| if (addToJsonSummary) { | 
| add_actual_results_to_json_summary(completeName, actualBitmapAndDigest.fDigest, errors, | 
| @@ -893,20 +860,14 @@ public: | 
| * | 
| * @param gm which test generated the actualBitmap | 
| * @param gRec | 
| -     * @param writePath unless this is NULL, write out actual images into this | 
| -     *        directory | 
| * @param actualBitmapAndDigest ptr to bitmap generated by this run, or NULL | 
| *        if we don't have a usable bitmap representation | 
| -     * @param document pdf or xps representation, if appropriate | 
| */ | 
| ErrorCombination compare_test_results_to_stored_expectations( | 
| -        GM* gm, const ConfigData& gRec, const char writePath[], | 
| -        const BitmapAndDigest* actualBitmapAndDigest, SkStreamAsset* document) { | 
| +        GM* gm, const ConfigData& gRec, | 
| +        const BitmapAndDigest* actualBitmapAndDigest) { | 
|  | 
| SkString shortNamePlusConfig = make_shortname_plus_config(gm->shortName(), gRec.fName); | 
| -        SkString nameWithExtension(shortNamePlusConfig); | 
| -        nameWithExtension.append("."); | 
| -        nameWithExtension.append(kPNG_FileExtension); | 
|  | 
| ErrorCombination errors; | 
|  | 
| @@ -914,18 +875,20 @@ public: | 
| // Note that we intentionally skipped validating the results for | 
| // this test, because we don't know how to generate an SkBitmap | 
| // version of the output. | 
| -            RecordTestResults(ErrorCombination(kIntentionallySkipped_ErrorType), | 
| -                              shortNamePlusConfig, ""); | 
| +            errors.add(ErrorCombination(kIntentionallySkipped_ErrorType)); | 
| } else if (!(gRec.fFlags & kWrite_ConfigFlag)) { | 
| // We don't record the results for this test or compare them | 
| // against any expectations, because the output image isn't | 
| // meaningful. | 
| // See https://code.google.com/p/skia/issues/detail?id=1410 ('some | 
| // GM result images not available for download from Google Storage') | 
| -            RecordTestResults(ErrorCombination(kIntentionallySkipped_ErrorType), | 
| -                              shortNamePlusConfig, ""); | 
| +            errors.add(ErrorCombination(kIntentionallySkipped_ErrorType)); | 
| } else { | 
| ExpectationsSource *expectationsSource = this->fExpectationsSource.get(); | 
| +            SkString nameWithExtension(shortNamePlusConfig); | 
| +            nameWithExtension.append("."); | 
| +            nameWithExtension.append(kPNG_FileExtension); | 
| + | 
| if (expectationsSource && (gRec.fFlags & kRead_ConfigFlag)) { | 
| /* | 
| * Get the expected results for this test, as one or more allowed | 
| @@ -950,20 +913,9 @@ public: | 
| actualBitmapAndDigest->fDigest, | 
| ErrorCombination(kMissingExpectations_ErrorType), | 
| false); | 
| -                RecordTestResults(ErrorCombination(kMissingExpectations_ErrorType), | 
| -                                  shortNamePlusConfig, ""); | 
| +                errors.add(ErrorCombination(kMissingExpectations_ErrorType)); | 
| } | 
| } | 
| - | 
| -        // TODO: Consider moving this into compare_to_expectations(), | 
| -        // similar to fMismatchPath... for now, we don't do that, because | 
| -        // we don't want to write out the actual bitmaps for all | 
| -        // renderModes of all tests!  That would be a lot of files. | 
| -        if (writePath && (gRec.fFlags & kWrite_ConfigFlag)) { | 
| -            errors.add(write_reference_image(gRec, writePath, "", gm->shortName(), | 
| -                                             actualBitmapAndDigest, document)); | 
| -        } | 
| - | 
| return errors; | 
| } | 
|  | 
| @@ -983,8 +935,19 @@ public: | 
| SkASSERT(referenceBitmap); | 
| Expectations expectations(*referenceBitmap); | 
| BitmapAndDigest actualBitmapAndDigest(actualBitmap); | 
| -        return compare_to_expectations(expectations, actualBitmapAndDigest, shortName, | 
| -                                       configName, renderModeDescriptor, false); | 
| + | 
| +        // TODO: Eliminate RecordTestResults from here. | 
| +        // Results recording code for the test_drawing path has been refactored so that | 
| +        // RecordTestResults is only called once, at the topmost level. However, the | 
| +        // other paths have not yet been refactored, and RecordTestResults has been added | 
| +        // here to maintain proper behavior for calls not coming from the test_drawing path. | 
| +        ErrorCombination errors; | 
| +        errors.add(compare_to_expectations(expectations, actualBitmapAndDigest, shortName, | 
| +                                           configName, renderModeDescriptor, false)); | 
| +        SkString shortNamePlusConfig = make_shortname_plus_config(shortName, configName); | 
| +        RecordTestResults(errors, shortNamePlusConfig, renderModeDescriptor); | 
| + | 
| +        return errors; | 
| } | 
|  | 
| static SkPicture* generate_new_picture(GM* gm, BbhType bbhType, uint32_t recordFlags, | 
| @@ -1037,46 +1000,89 @@ public: | 
|  | 
| // Test: draw into a bitmap or pdf. | 
| // Depending on flags, possibly compare to an expected image. | 
| -    ErrorCombination test_drawing(GM* gm, | 
| -                                  const ConfigData& gRec, | 
| +    // If writePath is not NULL, also write images (or documents) to the specified path. | 
| +    ErrorCombination test_drawing(GM* gm, const ConfigData& gRec, | 
| +                                  const SkTDArray<const PDFRasterizerData*> &pdfRasterizers, | 
| const char writePath [], | 
| GrSurface* gpuTarget, | 
| SkBitmap* bitmap) { | 
| +        ErrorCombination errors; | 
| SkDynamicMemoryWStream document; | 
| +        SkString path; | 
|  | 
| if (gRec.fBackend == kRaster_Backend || | 
| gRec.fBackend == kGPU_Backend) { | 
| // Early exit if we can't generate the image. | 
| -            ErrorCombination errors = generate_image(gm, gRec, gpuTarget, bitmap, false); | 
| +            errors.add(generate_image(gm, gRec, gpuTarget, bitmap, false)); | 
| if (!errors.isEmpty()) { | 
| // TODO: Add a test to exercise what the stdout and | 
| // JSON look like if we get an "early error" while | 
| // trying to generate the image. | 
| return errors; | 
| } | 
| +            BitmapAndDigest bitmapAndDigest(*bitmap); | 
| +            errors.add(compare_test_results_to_stored_expectations( | 
| +                           gm, gRec, &bitmapAndDigest)); | 
| + | 
| +            if (writePath && (gRec.fFlags & kWrite_ConfigFlag)) { | 
| +                path = make_bitmap_filename(writePath, gm->shortName(), gRec.fName, | 
| +                                            "", bitmapAndDigest.fDigest); | 
| +                errors.add(write_bitmap(path, bitmapAndDigest.fBitmap)); | 
| +            } | 
| } else if (gRec.fBackend == kPDF_Backend) { | 
| generate_pdf(gm, document); | 
| -#if CAN_IMAGE_PDF | 
| -            SkAutoDataUnref data(document.copyToData()); | 
| -            SkMemoryStream stream(data->data(), data->size()); | 
| -            SkPDFDocumentToBitmap(&stream, bitmap); | 
| -#else | 
| -            bitmap = NULL;  // we don't generate a bitmap rendering of the PDF file | 
| -#endif | 
| + | 
| +            SkAutoTUnref<SkStreamAsset> documentStream(document.detachAsStream()); | 
| +            if (writePath && (gRec.fFlags & kWrite_ConfigFlag)) { | 
| +                path = make_filename(writePath, gm->shortName(), gRec.fName, "", "pdf"); | 
| +                errors.add(write_document(path, documentStream)); | 
| +            } | 
| + | 
| +            if (!(gm->getFlags() & GM::kSkipPDFRasterization_Flag)) { | 
| +                for (int i = 0; i < pdfRasterizers.count(); i++) { | 
| +                    SkBitmap pdfBitmap; | 
| +                    SkASSERT(documentStream->rewind()); | 
| +                    bool success = (*pdfRasterizers[i]->fRasterizerFunction)( | 
| +                            documentStream.get(), &pdfBitmap); | 
| +                    if (!success) { | 
| +                        gm_fprintf(stderr, "FAILED to render PDF for %s using renderer %s\n", | 
| +                                   gm->shortName(), | 
| +                                   pdfRasterizers[i]->fName); | 
| +                        continue; | 
| +                    } | 
| + | 
| +                    BitmapAndDigest bitmapAndDigest(pdfBitmap); | 
| +                    errors.add(compare_test_results_to_stored_expectations( | 
| +                               gm, gRec, &bitmapAndDigest)); | 
| + | 
| +                    SkString configName(gRec.fName); | 
| +                    configName.append("_"); | 
| +                    configName.append(pdfRasterizers[i]->fName); | 
| + | 
| +                    if (writePath && (gRec.fFlags & kWrite_ConfigFlag)) { | 
| +                        path = make_bitmap_filename(writePath, gm->shortName(), configName.c_str(), | 
| +                                                    "", bitmapAndDigest.fDigest); | 
| +                        errors.add(write_bitmap(path, bitmapAndDigest.fBitmap)); | 
| +                    } | 
| +                } | 
| +            } else { | 
| +                errors.add(kIntentionallySkipped_ErrorType); | 
| +            } | 
| } else if (gRec.fBackend == kXPS_Backend) { | 
| generate_xps(gm, document); | 
| -            bitmap = NULL;  // we don't generate a bitmap rendering of the XPS file | 
| -        } | 
| +            SkAutoTUnref<SkStreamAsset> documentStream(document.detachAsStream()); | 
| + | 
| +            errors.add(compare_test_results_to_stored_expectations( | 
| +                           gm, gRec, NULL)); | 
|  | 
| -        SkAutoTUnref<SkStreamAsset> documentStream(document.detachAsStream()); | 
| -        if (NULL == bitmap) { | 
| -            return compare_test_results_to_stored_expectations( | 
| -                gm, gRec, writePath, NULL, documentStream); | 
| +            if (writePath && (gRec.fFlags & kWrite_ConfigFlag)) { | 
| +                path = make_filename(writePath, gm->shortName(), gRec.fName, "", "xps"); | 
| +                errors.add(write_document(path, documentStream)); | 
| +            } | 
| } else { | 
| -            BitmapAndDigest bitmapAndDigest(*bitmap); | 
| -            return compare_test_results_to_stored_expectations( | 
| -                gm, gRec, writePath, &bitmapAndDigest, documentStream); | 
| +            SkASSERT(false); | 
| } | 
| +        return errors; | 
| } | 
|  | 
| ErrorCombination test_deferred_drawing(GM* gm, | 
| @@ -1230,11 +1236,6 @@ static const GLContextType kDontCare_GLContextType = GrContextFactory::kNative_G | 
| static const GLContextType kDontCare_GLContextType = 0; | 
| #endif | 
|  | 
| -// If the platform does not support writing PNGs of PDFs then there will be no | 
| -// reference images to read. However, we can always write the .pdf files | 
| -static const ConfigFlags kPDFConfigFlags = CAN_IMAGE_PDF ? kRW_ConfigFlag : | 
| -                                                           kWrite_ConfigFlag; | 
| - | 
| static const ConfigData gRec[] = { | 
| { SkBitmap::kARGB_8888_Config, kRaster_Backend, kDontCare_GLContextType,                  0, kRW_ConfigFlag,    "8888",         true }, | 
| #if 0   // stop testing this (for now at least) since we want to remove support for it (soon please!!!) | 
| @@ -1266,10 +1267,19 @@ static const ConfigData gRec[] = { | 
| { SkBitmap::kARGB_8888_Config, kXPS_Backend,    kDontCare_GLContextType,                  0, kWrite_ConfigFlag, "xps",          true }, | 
| #endif // SK_SUPPORT_XPS | 
| #ifdef SK_SUPPORT_PDF | 
| -    { SkBitmap::kARGB_8888_Config, kPDF_Backend,    kDontCare_GLContextType,                  0, kPDFConfigFlags,   "pdf",          true }, | 
| +    { SkBitmap::kARGB_8888_Config, kPDF_Backend,    kDontCare_GLContextType,                  0, kRW_ConfigFlag,    "pdf",          true }, | 
| #endif // SK_SUPPORT_PDF | 
| }; | 
|  | 
| +static const PDFRasterizerData kPDFRasterizers[] = { | 
| +#ifdef SK_BUILD_FOR_MAC | 
| +    { &SkPDFDocumentToBitmap, "mac",     true }, | 
| +#endif | 
| +#ifdef SK_BUILD_POPPLER | 
| +    { &SkPopplerRasterizePDF, "poppler", true }, | 
| +#endif | 
| +}; | 
| + | 
| static const char kDefaultsConfigStr[] = "defaults"; | 
| static const char kExcludeConfigChar = '~'; | 
|  | 
| @@ -1321,6 +1331,32 @@ static SkString configUsage() { | 
| return result; | 
| } | 
|  | 
| +static SkString pdfRasterizerUsage() { | 
| +    SkString result; | 
| +    result.appendf("Space delimited list of which PDF rasterizers to run. Possible options: ["); | 
| +    // For this (and further) loops through kPDFRasterizers, there is a typecast to int to avoid | 
| +    // the compiler giving an "comparison of unsigned expression < 0 is always false" warning | 
| +    // and turning it into a build-breaking error. | 
| +    for (int i = 0; i < (int)SK_ARRAY_COUNT(kPDFRasterizers); ++i) { | 
| +        if (i > 0) { | 
| +            result.append(" "); | 
| +        } | 
| +        result.append(kPDFRasterizers[i].fName); | 
| +    } | 
| +    result.append("]\n"); | 
| +    result.append("The default value is: \""); | 
| +    for (int i = 0; i < (int)SK_ARRAY_COUNT(kPDFRasterizers); ++i) { | 
| +        if (kPDFRasterizers[i].fRunByDefault) { | 
| +            if (i > 0) { | 
| +                result.append(" "); | 
| +            } | 
| +            result.append(kPDFRasterizers[i].fName); | 
| +        } | 
| +    } | 
| +    result.append("\""); | 
| +    return result; | 
| +} | 
| + | 
| // Macro magic to convert a numeric preprocessor token into a string. | 
| // Adapted from http://stackoverflow.com/questions/240353/convert-a-preprocessor-token-to-a-string | 
| // This should probably be moved into one of our common headers... | 
| @@ -1329,6 +1365,7 @@ static SkString configUsage() { | 
|  | 
| // Alphabetized ignoring "no" prefix ("readPath", "noreplay", "resourcePath"). | 
| DEFINE_string(config, "", configUsage().c_str()); | 
| +DEFINE_string(pdfRasterizers, "", pdfRasterizerUsage().c_str()); | 
| DEFINE_bool(deferred, true, "Exercise the deferred rendering test pass."); | 
| DEFINE_string(excludeConfig, "", "Space delimited list of configs to skip."); | 
| DEFINE_bool(forceBWtext, false, "Disable text anti-aliasing."); | 
| @@ -1417,6 +1454,15 @@ static int findConfig(const char config[]) { | 
| return -1; | 
| } | 
|  | 
| +static const PDFRasterizerData* findPDFRasterizer(const char rasterizer[]) { | 
| +    for (int i = 0; i < (int)SK_ARRAY_COUNT(kPDFRasterizers); i++) { | 
| +        if (!strcmp(rasterizer, kPDFRasterizers[i].fName)) { | 
| +            return &kPDFRasterizers[i]; | 
| +        } | 
| +    } | 
| +    return NULL; | 
| +} | 
| + | 
| namespace skiagm { | 
| #if SK_SUPPORT_GPU | 
| SkAutoTUnref<GrContext> gGrContext; | 
| @@ -1473,9 +1519,13 @@ template <typename T> void appendUnique(SkTDArray<T>* array, const T& value) { | 
| * | 
| * Returns all errors encountered while doing so. | 
| */ | 
| -ErrorCombination run_multiple_configs(GMMain &gmmain, GM *gm, const SkTDArray<size_t> &configs, | 
| +ErrorCombination run_multiple_configs(GMMain &gmmain, GM *gm, | 
| +                                      const SkTDArray<size_t> &configs, | 
| +                                      const SkTDArray<const PDFRasterizerData*> &pdfRasterizers, | 
| GrContextFactory *grFactory); | 
| -ErrorCombination run_multiple_configs(GMMain &gmmain, GM *gm, const SkTDArray<size_t> &configs, | 
| +ErrorCombination run_multiple_configs(GMMain &gmmain, GM *gm, | 
| +                                      const SkTDArray<size_t> &configs, | 
| +                                      const SkTDArray<const PDFRasterizerData*> &pdfRasterizers, | 
| GrContextFactory *grFactory) { | 
| const char renderModeDescriptor[] = ""; | 
| ErrorCombination errorsForAllConfigs; | 
| @@ -1566,9 +1616,12 @@ ErrorCombination run_multiple_configs(GMMain &gmmain, GM *gm, const SkTDArray<si | 
| } else { | 
| writePath = NULL; | 
| } | 
| + | 
| if (errorsForThisConfig.isEmpty()) { | 
| -            errorsForThisConfig.add(gmmain.test_drawing(gm,config, writePath, gpuTarget, | 
| +            errorsForThisConfig.add(gmmain.test_drawing(gm, config, pdfRasterizers, | 
| +                                                        writePath, gpuTarget, | 
| &comparisonBitmap)); | 
| +            gmmain.RecordTestResults(errorsForThisConfig, shortNamePlusConfig, ""); | 
| } | 
|  | 
| if (FLAGS_deferred && errorsForThisConfig.isEmpty() && | 
| @@ -1754,10 +1807,9 @@ SkString list_all_config_names(const SkTDArray<size_t> &configs) { | 
| return total; | 
| } | 
|  | 
| -bool prepare_subdirectories(const char *root, bool useFileHierarchy, | 
| -                            const SkTDArray<size_t> &configs); | 
| -bool prepare_subdirectories(const char *root, bool useFileHierarchy, | 
| -                            const SkTDArray<size_t> &configs) { | 
| +static bool prepare_subdirectories(const char *root, bool useFileHierarchy, | 
| +                                   const SkTDArray<size_t> &configs, | 
| +                                   const SkTDArray<const PDFRasterizerData*>& pdfRasterizers) { | 
| if (!sk_mkdir(root)) { | 
| return false; | 
| } | 
| @@ -1769,6 +1821,16 @@ bool prepare_subdirectories(const char *root, bool useFileHierarchy, | 
| if (!sk_mkdir(subdir.c_str())) { | 
| return false; | 
| } | 
| + | 
| +            if (config.fBackend == kPDF_Backend) { | 
| +                for (int j = 0; j < pdfRasterizers.count(); j++) { | 
| +                    SkString pdfSubdir = subdir; | 
| +                    pdfSubdir.appendf("_%s", pdfRasterizers[j]->fName); | 
| +                    if (!sk_mkdir(pdfSubdir.c_str())) { | 
| +                        return false; | 
| +                    } | 
| +                } | 
| +            } | 
| } | 
| } | 
| return true; | 
| @@ -1877,6 +1939,53 @@ static bool parse_flags_configs(SkTDArray<size_t>* outConfigs, | 
| return true; | 
| } | 
|  | 
| +static bool parse_flags_pdf_rasterizers(const SkTDArray<size_t>& configs, | 
| +                                        SkTDArray<const PDFRasterizerData*>* outRasterizers) { | 
| +    // No need to run this check (and display the PDF rasterizers message) | 
| +    // if no PDF backends are in the configs. | 
| +    bool configHasPDF = false; | 
| +    for (int i = 0; i < configs.count(); i++) { | 
| +        if (gRec[configs[i]].fBackend == kPDF_Backend) { | 
| +            configHasPDF = true; | 
| +            break; | 
| +        } | 
| +    } | 
| +    if (!configHasPDF) { | 
| +        return true; | 
| +    } | 
| + | 
| +    for (int i = 0; i < FLAGS_pdfRasterizers.count(); i++) { | 
| +        const char* rasterizer = FLAGS_pdfRasterizers[i]; | 
| +        const PDFRasterizerData* rasterizerPtr = findPDFRasterizer(rasterizer); | 
| + | 
| +        if (rasterizerPtr == NULL) { | 
| +            gm_fprintf(stderr, "unrecognized rasterizer %s\n", rasterizer); | 
| +            return false; | 
| +        } | 
| +        appendUnique<const PDFRasterizerData*>(outRasterizers, | 
| +                                               rasterizerPtr); | 
| +    } | 
| + | 
| +    if (outRasterizers->count() == 0) { | 
| +        // if no config is specified by user, add the defaults | 
| +        for (int i = 0; i < (int)SK_ARRAY_COUNT(kPDFRasterizers); ++i) { | 
| +            if (kPDFRasterizers[i].fRunByDefault) { | 
| +                *outRasterizers->append() = &kPDFRasterizers[i]; | 
| +            } | 
| +        } | 
| +    } | 
| + | 
| +    // now show the user the set of configs that will be run. | 
| +    SkString configStr("These PDF rasterizers will be run:"); | 
| +    // show the user the config that will run. | 
| +    for (int i = 0; i < outRasterizers->count(); ++i) { | 
| +        configStr.appendf(" %s", (*outRasterizers)[i]->fName); | 
| +    } | 
| +    gm_fprintf(stdout, "%s\n", configStr.c_str()); | 
| + | 
| +    return true; | 
| +} | 
| + | 
| static bool parse_flags_ignore_error_types(ErrorCombination* outErrorTypes) { | 
| if (FLAGS_ignoreErrorTypes.count() > 0) { | 
| *outErrorTypes = ErrorCombination(); | 
| @@ -2019,8 +2128,10 @@ int tool_main(int argc, char** argv) { | 
| SkCommandLineFlags::Parse(argc, argv); | 
|  | 
| SkTDArray<size_t> configs; | 
| + | 
| int moduloRemainder = -1; | 
| int moduloDivisor = -1; | 
| +    SkTDArray<const PDFRasterizerData*> pdfRasterizers; | 
| SkTDArray<SkScalar> tileGridReplayScales; | 
| #if SK_SUPPORT_GPU | 
| GrContextFactory* grFactory = new GrContextFactory; | 
| @@ -2039,6 +2150,7 @@ int tool_main(int argc, char** argv) { | 
| !parse_flags_match_strs(&matchStrs) || | 
| !parse_flags_jpeg_quality() || | 
| !parse_flags_configs(&configs, grFactory) || | 
| +        !parse_flags_pdf_rasterizers(configs, &pdfRasterizers) || | 
| !parse_flags_gmmain_paths(&gmmain)) { | 
| return -1; | 
| } | 
| @@ -2068,18 +2180,20 @@ int tool_main(int argc, char** argv) { | 
|  | 
| // If we will be writing out files, prepare subdirectories. | 
| if (FLAGS_writePath.count() == 1) { | 
| -        if (!prepare_subdirectories(FLAGS_writePath[0], gmmain.fUseFileHierarchy, configs)) { | 
| +        if (!prepare_subdirectories(FLAGS_writePath[0], gmmain.fUseFileHierarchy, | 
| +                                    configs, pdfRasterizers)) { | 
| return -1; | 
| } | 
| } | 
| if (NULL != gmmain.fMismatchPath) { | 
| -        if (!prepare_subdirectories(gmmain.fMismatchPath, gmmain.fUseFileHierarchy, configs)) { | 
| +        if (!prepare_subdirectories(gmmain.fMismatchPath, gmmain.fUseFileHierarchy, | 
| +                                    configs, pdfRasterizers)) { | 
| return -1; | 
| } | 
| } | 
| if (NULL != gmmain.fMissingExpectationsPath) { | 
| if (!prepare_subdirectories(gmmain.fMissingExpectationsPath, gmmain.fUseFileHierarchy, | 
| -                                    configs)) { | 
| +                                    configs, pdfRasterizers)) { | 
| return -1; | 
| } | 
| } | 
| @@ -2107,7 +2221,7 @@ int tool_main(int argc, char** argv) { | 
| gm_fprintf(stdout, "%sdrawing... %s [%d %d]\n", moduloStr.c_str(), shortName, | 
| size.width(), size.height()); | 
|  | 
| -        run_multiple_configs(gmmain, gm, configs, grFactory); | 
| +        run_multiple_configs(gmmain, gm, configs, pdfRasterizers, grFactory); | 
|  | 
| SkBitmap comparisonBitmap; | 
| const ConfigData compareConfig = | 
|  |