1 // Copyright 2018 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "printing/metafile_skia.h"
9 #include "build/build_config.h"
10 #include "cc/paint/paint_record.h"
11 #include "printing/common/metafile_utils.h"
12 #include "printing/mojom/print.mojom.h"
13 #include "testing/gtest/include/gtest/gtest.h"
14 #include "third_party/skia/include/core/SkBitmap.h"
15 #include "third_party/skia/include/core/SkCanvas.h"
16 #include "third_party/skia/include/core/SkPaint.h"
17 #include "third_party/skia/include/core/SkPicture.h"
18 #include "third_party/skia/include/core/SkPictureRecorder.h"
19 #include "third_party/skia/include/core/SkRect.h"
20 #include "third_party/skia/include/core/SkRefCnt.h"
21 #include "third_party/skia/include/core/SkSerialProcs.h"
22 #include "third_party/skia/include/core/SkSize.h"
23 #include "third_party/skia/include/core/SkStream.h"
24 #include "third_party/skia/include/core/SkSurfaceProps.h"
25 #include "third_party/skia/include/core/SkTextBlob.h"
29 TEST(MetafileSkiaTest, TestFrameContent) {
30 constexpr int kPictureSideLen = 100;
31 constexpr int kPageSideLen = 150;
33 // Create a placeholder picture.
34 sk_sp<SkPicture> pic_holder = SkPicture::MakePlaceholder(
35 SkRect::MakeXYWH(0, 0, kPictureSideLen, kPictureSideLen));
37 // Create the page with nested content which is the placeholder and will be
39 sk_sp<cc::PaintRecord> record = sk_make_sp<cc::PaintRecord>();
41 flags.setColor(SK_ColorWHITE);
42 const SkRect page_rect = SkRect::MakeXYWH(0, 0, kPageSideLen, kPageSideLen);
43 record->push<cc::DrawRectOp>(page_rect, flags);
44 const uint32_t content_id = pic_holder->uniqueID();
45 record->push<cc::CustomDataOp>(content_id);
46 SkSize page_size = SkSize::Make(kPageSideLen, kPageSideLen);
48 // Finish creating the entire metafile.
49 MetafileSkia metafile(mojom::SkiaDocumentType::kMSKP, 1);
50 metafile.AppendPage(page_size, std::move(record));
51 metafile.AppendSubframeInfo(content_id, base::UnguessableToken::Create(),
52 std::move(pic_holder));
53 metafile.FinishFrameContent();
54 SkStreamAsset* metafile_stream = metafile.GetPdfData();
55 ASSERT_TRUE(metafile_stream);
57 // Draw a 100 by 100 red square which will be the actual content of
59 SkPictureRecorder recorder;
60 SkCanvas* canvas = recorder.beginRecording(kPictureSideLen, kPictureSideLen);
62 paint.setStyle(SkPaint::kFill_Style);
63 paint.setColor(SK_ColorRED);
64 paint.setAlpha(SK_AlphaOPAQUE);
65 canvas->drawRect(SkRect::MakeXYWH(0, 0, kPictureSideLen, kPictureSideLen),
67 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
70 // Get the complete picture by replacing the placeholder.
71 PictureDeserializationContext subframes;
72 subframes[content_id] = picture;
73 SkDeserialProcs procs = DeserializationProcs(&subframes, nullptr);
74 sk_sp<SkPicture> pic = SkPicture::MakeFromStream(metafile_stream, &procs);
77 // Verify the resultant picture is as expected by comparing the sizes and
78 // detecting the color inside and outside of the square area.
79 EXPECT_TRUE(pic->cullRect() == page_rect);
81 bitmap.allocN32Pixels(kPageSideLen, kPageSideLen);
82 SkCanvas bitmap_canvas(bitmap, SkSurfaceProps{});
83 pic->playback(&bitmap_canvas);
84 // Check top left pixel color of the red square.
85 EXPECT_EQ(bitmap.getColor(0, 0), SK_ColorRED);
86 // Check bottom right pixel of the red square.
87 EXPECT_EQ(bitmap.getColor(kPictureSideLen - 1, kPictureSideLen - 1),
89 // Check inside of the red square.
90 EXPECT_EQ(bitmap.getColor(kPictureSideLen / 2, kPictureSideLen / 2),
92 // Check outside of the red square.
93 EXPECT_EQ(bitmap.getColor(kPictureSideLen, kPictureSideLen), SK_ColorWHITE);
96 TEST(MetafileSkiaTest, TestMultiPictureDocumentTypefaces) {
97 constexpr int kPictureSideLen = 100;
98 constexpr int kPageSideLen = 150;
99 constexpr int kDocumentCookie = 1;
100 constexpr int kNumDocumentPages = 2;
102 // The content tracking for serialization/deserialization.
103 ContentProxySet serialize_typeface_ctx;
104 PictureDeserializationContext subframes;
105 TypefaceDeserializationContext typefaces;
106 SkDeserialProcs procs = DeserializationProcs(&subframes, &typefaces);
108 // The typefaces which will be reused across the multiple (duplicate) pages.
109 constexpr char kTypefaceName1[] = "sans-serif";
110 #if BUILDFLAG(IS_WIN)
111 constexpr char kTypefaceName2[] = "Courier New";
113 constexpr char kTypefaceName2[] = "monospace";
115 constexpr size_t kNumTypefaces = 2;
116 sk_sp<SkTypeface> typeface1 =
117 SkTypeface::MakeFromName(kTypefaceName1, SkFontStyle());
118 sk_sp<SkTypeface> typeface2 =
119 SkTypeface::MakeFromName(kTypefaceName2, SkFontStyle());
120 const SkFont font1 = SkFont(typeface1, 10);
121 const SkFont font2 = SkFont(typeface2, 12);
123 // Node IDs for the text, which will increase for each text blob added.
124 cc::NodeId node_id = 7;
126 // All text can just be black.
127 cc::PaintFlags flags_text;
128 flags_text.setColor(SK_ColorBLACK);
130 // Mark the text on white pages, each of the same size.
131 cc::PaintFlags flags;
132 flags.setColor(SK_ColorWHITE);
133 const SkRect page_rect = SkRect::MakeXYWH(0, 0, kPageSideLen, kPageSideLen);
134 SkSize page_size = SkSize::Make(kPageSideLen, kPageSideLen);
136 for (int i = 0; i < kNumDocumentPages; i++) {
137 MetafileSkia metafile(mojom::SkiaDocumentType::kMSKP, kDocumentCookie);
139 // When the stream is serialized inside FinishFrameContent(), any typeface
140 // which is used on any page will be serialized only once by the first
141 // page's metafile which needed it. Any subsequent page that reuses the
142 // same typeface will rely upon `serialize_typeface_ctx` which is used by
143 // printing::SerializeOopTypeface() to optimize away the need to resend.
144 metafile.UtilizeTypefaceContext(&serialize_typeface_ctx);
146 sk_sp<SkPicture> pic_holder = SkPicture::MakePlaceholder(
147 SkRect::MakeXYWH(0, 0, kPictureSideLen, kPictureSideLen));
149 // Create the page for the text content.
150 sk_sp<cc::PaintRecord> record = sk_make_sp<cc::PaintRecord>();
151 record->push<cc::DrawRectOp>(page_rect, flags);
152 const uint32_t content_id = pic_holder->uniqueID();
153 record->push<cc::CustomDataOp>(content_id);
155 // Mark the page with some text using multiple fonts.
156 // Use the first font.
157 sk_sp<SkTextBlob> text_blob1 = SkTextBlob::MakeFromString("foo", font1);
158 record->push<cc::DrawTextBlobOp>(text_blob1, 0.0f, 0.0f, ++node_id,
161 // Use the second font.
162 sk_sp<SkTextBlob> text_blob2 = SkTextBlob::MakeFromString("bar", font2);
163 record->push<cc::DrawTextBlobOp>(text_blob2, 0.0f, 0.0f, ++node_id,
166 // Reuse the first font again on same page.
167 sk_sp<SkTextBlob> text_blob3 = SkTextBlob::MakeFromString("bar", font2);
168 record->push<cc::DrawTextBlobOp>(text_blob3, 0.0f, 0.0f, ++node_id,
171 metafile.AppendPage(page_size, std::move(record));
172 metafile.AppendSubframeInfo(content_id, base::UnguessableToken::Create(),
173 std::move(pic_holder));
174 metafile.FinishFrameContent();
175 SkStreamAsset* metafile_stream = metafile.GetPdfData();
176 ASSERT_TRUE(metafile_stream);
178 // Deserialize the stream. Any given typeface is expected to appear only
179 // once in the stream, so the deserialization context of `typefaces` bundled
180 // with `procs` should be empty the first time through, and afterwards
181 // there should never be more than the number of unique typefaces we used,
182 // regardless of number of pages.
183 EXPECT_EQ(typefaces.size(), i ? kNumTypefaces : 0);
184 ASSERT_TRUE(SkPicture::MakeFromStream(metafile_stream, &procs));
185 EXPECT_EQ(typefaces.size(), kNumTypefaces);
189 } // namespace printing