// Copyright 2018 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "printing/metafile_skia.h" #include #include "build/build_config.h" #include "cc/paint/paint_record.h" #include "printing/common/metafile_utils.h" #include "printing/mojom/print.mojom.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/skia/include/core/SkPictureRecorder.h" namespace printing { TEST(MetafileSkiaTest, TestFrameContent) { constexpr int kPictureSideLen = 100; constexpr int kPageSideLen = 150; // Create a placeholder picture. sk_sp pic_holder = SkPicture::MakePlaceholder( SkRect::MakeXYWH(0, 0, kPictureSideLen, kPictureSideLen)); // Create the page with nested content which is the placeholder and will be // replaced later. sk_sp record = sk_make_sp(); cc::PaintFlags flags; flags.setColor(SK_ColorWHITE); const SkRect page_rect = SkRect::MakeXYWH(0, 0, kPageSideLen, kPageSideLen); record->push(page_rect, flags); const uint32_t content_id = pic_holder->uniqueID(); record->push(content_id); SkSize page_size = SkSize::Make(kPageSideLen, kPageSideLen); // Finish creating the entire metafile. MetafileSkia metafile(mojom::SkiaDocumentType::kMSKP, 1); metafile.AppendPage(page_size, std::move(record)); metafile.AppendSubframeInfo(content_id, base::UnguessableToken::Create(), std::move(pic_holder)); metafile.FinishFrameContent(); SkStreamAsset* metafile_stream = metafile.GetPdfData(); ASSERT_TRUE(metafile_stream); // Draw a 100 by 100 red square which will be the actual content of // the placeholder. SkPictureRecorder recorder; SkCanvas* canvas = recorder.beginRecording(kPictureSideLen, kPictureSideLen); SkPaint paint; paint.setStyle(SkPaint::kFill_Style); paint.setColor(SK_ColorRED); paint.setAlpha(SK_AlphaOPAQUE); canvas->drawRect(SkRect::MakeXYWH(0, 0, kPictureSideLen, kPictureSideLen), paint); sk_sp picture(recorder.finishRecordingAsPicture()); EXPECT_TRUE(picture); // Get the complete picture by replacing the placeholder. PictureDeserializationContext subframes; subframes[content_id] = picture; SkDeserialProcs procs = DeserializationProcs(&subframes, nullptr); sk_sp pic = SkPicture::MakeFromStream(metafile_stream, &procs); ASSERT_TRUE(pic); // Verify the resultant picture is as expected by comparing the sizes and // detecting the color inside and outside of the square area. EXPECT_TRUE(pic->cullRect() == page_rect); SkBitmap bitmap; bitmap.allocN32Pixels(kPageSideLen, kPageSideLen); SkCanvas bitmap_canvas(bitmap, SkSurfaceProps{}); pic->playback(&bitmap_canvas); // Check top left pixel color of the red square. EXPECT_EQ(bitmap.getColor(0, 0), SK_ColorRED); // Check bottom right pixel of the red square. EXPECT_EQ(bitmap.getColor(kPictureSideLen - 1, kPictureSideLen - 1), SK_ColorRED); // Check inside of the red square. EXPECT_EQ(bitmap.getColor(kPictureSideLen / 2, kPictureSideLen / 2), SK_ColorRED); // Check outside of the red square. EXPECT_EQ(bitmap.getColor(kPictureSideLen, kPictureSideLen), SK_ColorWHITE); } TEST(MetafileSkiaTest, TestMultiPictureDocumentTypefaces) { constexpr int kPictureSideLen = 100; constexpr int kPageSideLen = 150; constexpr int kDocumentCookie = 1; constexpr int kNumDocumentPages = 2; // The content tracking for serialization/deserialization. ContentProxySet serialize_typeface_ctx; PictureDeserializationContext subframes; TypefaceDeserializationContext typefaces; SkDeserialProcs procs = DeserializationProcs(&subframes, &typefaces); // The typefaces which will be reused across the multiple (duplicate) pages. constexpr char kTypefaceName1[] = "sans-serif"; #if defined(OS_WIN) constexpr char kTypefaceName2[] = "Courier New"; #else constexpr char kTypefaceName2[] = "monospace"; #endif constexpr size_t kNumTypefaces = 2; sk_sp typeface1 = SkTypeface::MakeFromName(kTypefaceName1, SkFontStyle()); sk_sp typeface2 = SkTypeface::MakeFromName(kTypefaceName2, SkFontStyle()); const SkFont font1 = SkFont(typeface1, 10); const SkFont font2 = SkFont(typeface2, 12); // Node IDs for the text, which will increase for each text blob added. cc::NodeId node_id = 7; // All text can just be black. cc::PaintFlags flags_text; flags_text.setColor(SK_ColorBLACK); // Mark the text on white pages, each of the same size. cc::PaintFlags flags; flags.setColor(SK_ColorWHITE); const SkRect page_rect = SkRect::MakeXYWH(0, 0, kPageSideLen, kPageSideLen); SkSize page_size = SkSize::Make(kPageSideLen, kPageSideLen); for (int i = 0; i < kNumDocumentPages; i++) { MetafileSkia metafile(mojom::SkiaDocumentType::kMSKP, kDocumentCookie); // When the stream is serialized inside FinishFrameContent(), any typeface // which is used on any page will be serialized only once by the first // page's metafile which needed it. Any subsequent page that reuses the // same typeface will rely upon |serialize_typeface_ctx| which is used by // printing::SerializeOopTypeface() to optimize away the need to resend. metafile.UtilizeTypefaceContext(&serialize_typeface_ctx); sk_sp pic_holder = SkPicture::MakePlaceholder( SkRect::MakeXYWH(0, 0, kPictureSideLen, kPictureSideLen)); // Create the page for the text content. sk_sp record = sk_make_sp(); record->push(page_rect, flags); const uint32_t content_id = pic_holder->uniqueID(); record->push(content_id); // Mark the page with some text using multiple fonts. // Use the first font. sk_sp text_blob1 = SkTextBlob::MakeFromString("foo", font1); record->push(text_blob1, 0, 0, ++node_id, flags_text); // Use the second font. sk_sp text_blob2 = SkTextBlob::MakeFromString("bar", font2); record->push(text_blob2, 0, 0, ++node_id, flags_text); // Reuse the first font again on same page. sk_sp text_blob3 = SkTextBlob::MakeFromString("bar", font2); record->push(text_blob3, 0, 0, ++node_id, flags_text); metafile.AppendPage(page_size, std::move(record)); metafile.AppendSubframeInfo(content_id, base::UnguessableToken::Create(), std::move(pic_holder)); metafile.FinishFrameContent(); SkStreamAsset* metafile_stream = metafile.GetPdfData(); ASSERT_TRUE(metafile_stream); // Deserialize the stream. Any given typeface is expected to appear only // once in the stream, so the deserialization context of |typefaces| bundled // with |procs| should be empty the first time through, and afterwards // there should never be more than the number of unique typefaces we used, // regardless of number of pages. EXPECT_EQ(typefaces.size(), i ? kNumTypefaces : 0); ASSERT_TRUE(SkPicture::MakeFromStream(metafile_stream, &procs)); EXPECT_EQ(typefaces.size(), kNumTypefaces); } } } // namespace printing