Flutter Engine
The Flutter Engine
PDFJpegEmbedTest.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2014 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
12#include "include/core/SkData.h"
16#include "include/core/SkSize.h"
21#include "src/pdf/SkPDFBitmap.h"
22#include "tests/Test.h"
23#include "tools/Resources.h"
24
25#include <array>
26#include <cstdint>
27#include <cstring>
28
29static bool is_subset_of(SkData* smaller, SkData* larger) {
30 SkASSERT(smaller && larger);
31 if (smaller->size() > larger->size()) {
32 return false;
33 }
34 size_t size = smaller->size();
35 size_t size_diff = larger->size() - size;
36 for (size_t i = 0; i <= size_diff; ++i) {
37 if (0 == memcmp(larger->bytes() + i, smaller->bytes(), size)) {
38 return true;
39 }
40 }
41 return false;
42}
43
44
46 skiatest::Reporter* r, const char* test, const char* filename) {
48 if (!data) {
49 INFOF(r, "\n%s: Resource '%s' can not be found.\n",
50 test, filename);
51 }
52 return data; // May return nullptr.
53}
54
55/**
56 * Test that for Jpeg files that use the JFIF colorspace, they are
57 * directly embedded into the PDF (without re-encoding) when that
58 * makes sense.
59 */
60DEF_TEST(SkPDF_JpegEmbedTest, r) {
61 REQUIRE_PDF_DOCUMENT(SkPDF_JpegEmbedTest, r);
62 const char test[] = "SkPDF_JpegEmbedTest";
63 sk_sp<SkData> mandrillData(load_resource(r, test, "images/mandrill_512_q075.jpg"));
64 sk_sp<SkData> cmykData(load_resource(r, test, "images/CMYK.jpg"));
65 sk_sp<SkData> yuvICCData(load_resource(r, test, "images/crbug999986.jpeg"));
66 sk_sp<SkData> cmykICCData(load_resource(r, test, "images/crbug1465627.jpeg"));
67
68 if (!mandrillData || !cmykData || !yuvICCData || !cmykICCData) {
69 REPORTER_ASSERT(r, false, "Could not load all images");
70 return;
71 }
72 ////////////////////////////////////////////////////////////////////////////
74 auto document = SkPDF::MakeDocument(&pdf);
75 SkCanvas* canvas = document->beginPage(642, 2048);
76
77 canvas->clear(SK_ColorLTGRAY);
78
80 canvas->drawImage(im1, 65.0, 0.0);
82 canvas->drawImage(im2, 0.0, 512.0);
83
84 // This should be the same blue as the dark spot seen in the image.
85 SkPaint bluePaint;
86 bluePaint.setColor(SkColorSetARGB(255, 0, 59, 103));
87 canvas->drawRect(SkRect::MakeXYWH(0, 1000, 642, 24), bluePaint);
89 canvas->drawImage(im3, 0.0, 1024.0);
90
92 canvas->drawImage(im4, 0.0, 1536.0);
93
94 document->endPage();
95 document->close();
96 sk_sp<SkData> pdfData = pdf.detachAsData();
97 SkASSERT(pdfData);
98
99 #ifndef SK_PDF_BASE85_BINARY
100 REPORTER_ASSERT(r, is_subset_of(mandrillData.get(), pdfData.get()));
101 #endif
102
103 // This JPEG uses a nonstandard colorspace - it can not be
104 // embedded into the PDF directly.
105 REPORTER_ASSERT(r, !is_subset_of(cmykData.get(), pdfData.get()));
106
107 if ((false)) {
108 SkFILEWStream s("/tmp/jpegembed.pdf");
109 REPORTER_ASSERT(r, s.write(pdfData->data(), pdfData->size()));
110 s.flush();
111 s.fsync();
112 }
113}
114
115#ifdef SK_SUPPORT_PDF
116
117struct SkJFIFInfo {
118 SkISize fSize;
119 enum Type {
121 kYCbCr,
122 } fType;
123};
124bool SkIsJFIF(const SkData* data, SkJFIFInfo* info) {
125 static constexpr const SkCodecs::Decoder decoders[] = {
127 };
128
129 if (!data) {
130 return false;
131 }
132 std::unique_ptr<SkCodec> codec = SkCodec::MakeFromData(sk_ref_sp(data), decoders);
133 if (!codec) {
134 return false;
135 }
136
137 SkISize jpegSize = codec->dimensions();
139 SkEncodedOrigin exifOrientation = codec->getOrigin();
140
141 bool yuv = jpegColorType == SkEncodedInfo::kYUV_Color;
142 bool goodColorType = yuv || jpegColorType == SkEncodedInfo::kGray_Color;
143 if (goodColorType && kTopLeft_SkEncodedOrigin == exifOrientation) {
144 if (info) {
145 *info = {jpegSize, yuv ? SkJFIFInfo::kYCbCr : SkJFIFInfo::kGrayscale};
146 }
147 return true;
148 }
149 return false;
150}
151
152DEF_TEST(SkPDF_JpegIdentification, r) {
153 static struct {
154 const char* path;
155 bool isJfif;
157 } kTests[] = {{"images/CMYK.jpg", false, SkJFIFInfo::kGrayscale},
158 {"images/color_wheel.jpg", true, SkJFIFInfo::kYCbCr},
159 {"images/grayscale.jpg", true, SkJFIFInfo::kGrayscale},
160 {"images/mandrill_512_q075.jpg", true, SkJFIFInfo::kYCbCr},
161 {"images/randPixels.jpg", true, SkJFIFInfo::kYCbCr}};
162 for (size_t i = 0; i < std::size(kTests); ++i) {
163 sk_sp<SkData> data(load_resource(r, "JpegIdentification", kTests[i].path));
164 if (!data) {
165 continue;
166 }
167 SkJFIFInfo info;
168 bool isJfif = SkIsJFIF(data.get(), &info);
169 if (isJfif != kTests[i].isJfif) {
170 ERRORF(r, "%s failed isJfif test", kTests[i].path);
171 continue;
172 }
173 if (!isJfif) {
174 continue; // not applicable
175 }
176 if (kTests[i].type != info.fType) {
177 ERRORF(r, "%s failed jfif type test", kTests[i].path);
178 continue;
179 }
180 INFOF(r, "\nJpegIdentification: %s [%d x %d]\n", kTests[i].path,
181 info.fSize.width(), info.fSize.height());
182 }
183
184 // Test several malformed jpegs.
185 SkJFIFInfo info;
186 {
187 static const char goodJpeg[] =
188 "\377\330\377\340\0\20JFIF\0\1\1\0\0\1\0\1\0\0\377\333\0C\0\20\13\14"
189 "\16\14\n\20\16\r\16\22\21\20\23\30(\32\30\26\26\0301#%\35(:3=<9387"
190 "@H\\N@DWE78PmQW_bghg>Mqypdx\\egc\377\333\0C\1\21\22\22\30\25\30/\32"
191 "\32/cB8Bcccccccccccccccccccccccccccccccccccccccccccccccccc\377\300"
192 "\0\21\10\0\10\0\10\3\1\"\0\2\21\1\3\21\1\377\304\0\37\0\0\1\5\1\1\1"
193 "\1\1\1\0\0\0\0\0\0\0\0\1\2\3\4\5\6\7\10\t\n\13\377\304\0\265\20\0\2"
194 "\1\3\3\2\4\3\5\5\4\4\0\0\1}\1\2\3\0\4\21\5\22!1A\6\23Qa\7\"q\0242\201"
195 "\221\241\10#B\261\301\25R\321\360$3br\202\t\n\26\27\30\31\32%&'()*"
196 "456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz\203\204\205\206\207\210\211"
197 "\212\222\223\224\225\226\227\230\231\232\242\243\244\245\246\247\250"
198 "\251\252\262\263\264\265\266\267\270\271\272\302\303\304\305\306\307"
199 "\310\311\312\322\323\324\325\326\327\330\331\332\341\342\343\344\345"
200 "\346\347\350\351\352\361\362\363\364\365\366\367\370\371\372\377\304"
201 "\0\37\1\0\3\1\1\1\1\1\1\1\1\1\0\0\0\0\0\0\1\2\3\4\5\6\7\10\t\n\13\377"
202 "\304\0\265\21\0\2\1\2\4\4\3\4\7\5\4\4\0\1\2w\0\1\2\3\21\4\5!1\6\22"
203 "AQ\7aq\23\"2\201\10\24B\221\241\261\301\t#3R\360\25br\321\n\26$4\341"
204 "%\361\27\30\31\32&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz\202\203"
205 "\204\205\206\207\210\211\212\222\223\224\225\226\227\230\231\232\242"
206 "\243\244\245\246\247\250\251\252\262\263\264\265\266\267\270\271\272"
207 "\302\303\304\305\306\307\310\311\312\322\323\324\325\326\327\330\331"
208 "\332\342\343\344\345\346\347\350\351\352\362\363\364\365\366\367\370"
209 "\371\372\377\332\0\14\3\1\0\2\21\3\21\0?\0\216M\352\214\25\271\224"
210 "\262\310\253\363tl\22209\35O~\237\\\24QZ\306Mh\216\252i\364ml\177\377"
211 "\331";
212 size_t goodJpegLength = 659;
213 auto data = SkData::MakeWithoutCopy(goodJpeg, goodJpegLength);
214 REPORTER_ASSERT(r, SkIsJFIF(data.get(), &info));
215 REPORTER_ASSERT(r, info.fSize == (SkISize{8, 8}));
216 REPORTER_ASSERT(r, info.fType == SkJFIFInfo::kYCbCr);
217
218 // Not long enough to read first (SOI) segment marker.
219 data = SkData::MakeWithoutCopy(goodJpeg, 1);
220 REPORTER_ASSERT(r, !SkIsJFIF(data.get(), &info));
221
222 // Not long enough to read second segment (APP0) marker.
223 data = SkData::MakeWithoutCopy(goodJpeg, 3);
224 REPORTER_ASSERT(r, !SkIsJFIF(data.get(), &info));
225
226 // Not long enough to read second segment's length.
227 data = SkData::MakeWithoutCopy(goodJpeg, 5);
228 REPORTER_ASSERT(r, !SkIsJFIF(data.get(), &info));
229
230 // APP0 segment is truncated.
231 data = SkData::MakeWithoutCopy(goodJpeg, 7);
232 REPORTER_ASSERT(r, !SkIsJFIF(data.get(), &info));
233
234 // Missing SOF segment.
235 data = SkData::MakeWithoutCopy(goodJpeg, 89);
236 REPORTER_ASSERT(r, !SkIsJFIF(data.get(), &info));
237 }
238 {
239 // JFIF tag missing.
240 static const char jpeg[] =
241 "\377\330\377\340\0\20JFIX\0\1\1\0\0\1\0\1\0\0\377\333\0C\0\10\6\6\7"
242 "\6\5\10\7\7\7\t\t\10\n\14\24\r\14\13\13\14\31\22\23\17\24\35\32\37"
243 "\36\35\32\34\34 $.' \",#\34\34(7),01444\37'9=82<.342\377\333\0C\1\t"
244 "\t\t\14\13\14\30\r\r\0302!\34!222222222222222222222222222222222222"
245 "22222222222222\377\300\0\21\10\2\0\2\0\3\1\"\0\2\21\1\3\21\001";
246 size_t jpegLength = 177;
247 auto data = SkData::MakeWithoutCopy(jpeg, jpegLength);
248 REPORTER_ASSERT(r, !SkIsJFIF(data.get(), &info));
249 }
250 {
251 // APP0 segment short (byte 6 changed).
252 static const char jpeg[] =
253 "\377\330\377\340\0\5JFIF\0\1\1\0\0\1\0\1\0\0\377\333\0C\0\10\6\6\7"
254 "\6\5\10\7\7\7\t\t\10\n\14\24\r\14\13\13\14\31\22\23\17\24\35\32\37"
255 "\36\35\32\34\34 $.' \",#\34\34(7),01444\37'9=82<.342\377\333\0C\1\t"
256 "\t\t\14\13\14\30\r\r\0302!\34!222222222222222222222222222222222222"
257 "22222222222222\377\300\0\21\10\2\0\2\0\3\1\"\0\2\21\1\3\21\001";
258 size_t jpegLength = 177;
259 auto data = SkData::MakeWithoutCopy(jpeg, jpegLength);
260 REPORTER_ASSERT(r, !SkIsJFIF(data.get(), &info));
261 }
262 {
263 // SOF segment short. ('\21' replaced with '\5')
264 static const char jpeg[] =
265 "\377\330\377\340\0\20JFIF\0\1\1\0\0\1\0\1\0\0\377\333\0C\0\10\6\6\7"
266 "\6\5\10\7\7\7\t\t\10\n\14\24\r\14\13\13\14\31\22\23\17\24\35\32\37"
267 "\36\35\32\34\34 $.' \",#\34\34(7),01444\37'9=82<.342\377\333\0C\1\t"
268 "\t\t\14\13\14\30\r\r\0302!\34!222222222222222222222222222222222222"
269 "22222222222222\377\300\0\5\10\2\0\2\0\3\1\"\0\2\21\1\3\21\001";
270 size_t jpegLength = 177;
271 auto data = SkData::MakeWithoutCopy(jpeg, jpegLength);
272 REPORTER_ASSERT(r, !SkIsJFIF(data.get(), &info));
273 }
274 {
275 // Unsupported 12-bit components. ('\10' replaced with '\14')
276 static const char jpeg[] =
277 "\377\330\377\340\0\20JFIF\0\1\1\0\0\1\0\1\0\0\377\333\0C\0\10\6\6\7"
278 "\6\5\10\7\7\7\t\t\10\n\14\24\r\14\13\13\14\31\22\23\17\24\35\32\37"
279 "\36\35\32\34\34 $.' \",#\34\34(7),01444\37'9=82<.342\377\333\0C\1\t"
280 "\t\t\14\13\14\30\r\r\0302!\34!222222222222222222222222222222222222"
281 "22222222222222\377\300\0\21\14\2\0\2\0\3\1\"\0\2\21\1\3\21\001";
282 size_t jpegLength = 177;
283 auto data = SkData::MakeWithoutCopy(jpeg, jpegLength);
284 REPORTER_ASSERT(r, !SkIsJFIF(data.get(), &info));
285 }
286 {
287 // Two color channels. ('\3' replaced with '\2')
288 static const char jpeg[] =
289 "\377\330\377\340\0\20JFIF\0\1\1\0\0\1\0\1\0\0\377\333\0C\0\10\6\6\7"
290 "\6\5\10\7\7\7\t\t\10\n\14\24\r\14\13\13\14\31\22\23\17\24\35\32\37"
291 "\36\35\32\34\34 $.' \",#\34\34(7),01444\37'9=82<.342\377\333\0C\1\t"
292 "\t\t\14\13\14\30\r\r\0302!\34!222222222222222222222222222222222222"
293 "22222222222222\377\300\0\21\10\2\0\2\0\2\1\"\0\2\21\1\3\21\001";
294 size_t jpegLength = 177;
295 auto data = SkData::MakeWithoutCopy(jpeg, jpegLength);
296 REPORTER_ASSERT(r, !SkIsJFIF(data.get(), &info));
297 }
298}
299#endif
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition: DM.cpp:213
static sk_sp< SkData > load_resource(skiatest::Reporter *r, const char *test, const char *filename)
DEF_TEST(SkPDF_JpegEmbedTest, r)
static bool is_subset_of(SkData *smaller, SkData *larger)
sk_sp< SkData > GetResourceAsData(const char *resource)
Definition: Resources.cpp:42
#define SkASSERT(cond)
Definition: SkAssert.h:116
constexpr SkColor SK_ColorLTGRAY
Definition: SkColor.h:118
static constexpr SkColor SkColorSetARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b)
Definition: SkColor.h:49
SkEncodedOrigin
@ kTopLeft_SkEncodedOrigin
sk_sp< T > sk_ref_sp(T *obj)
Definition: SkRefCnt.h:381
Type
Definition: SortBench.cpp:56
#define REPORTER_ASSERT(r, cond,...)
Definition: Test.h:286
#define REQUIRE_PDF_DOCUMENT(TEST_NAME, REPORTER)
Definition: Test.h:485
#define INFOF(REPORTER,...)
Definition: Test.h:298
#define ERRORF(r,...)
Definition: Test.h:293
GLenum type
void drawRect(const SkRect &rect, const SkPaint &paint)
Definition: SkCanvas.cpp:1673
void clear(SkColor color)
Definition: SkCanvas.h:1199
void drawImage(const SkImage *image, SkScalar left, SkScalar top)
Definition: SkCanvas.h:1528
static std::unique_ptr< SkCodec > MakeFromData(sk_sp< SkData >, SkSpan< const SkCodecs::Decoder > decoders, SkPngChunkReader *=nullptr)
Definition: SkCodec.cpp:241
Definition: SkData.h:25
static sk_sp< SkData > MakeWithoutCopy(const void *data, size_t length)
Definition: SkData.h:116
const uint8_t * bytes() const
Definition: SkData.h:43
const void * data() const
Definition: SkData.h:37
size_t size() const
Definition: SkData.h:30
sk_sp< SkData > detachAsData()
Definition: SkStream.cpp:707
static const SkEncodedInfo & GetEncodedInfo(SkCodec &)
Definition: SkPDFBitmap.cpp:48
void setColor(SkColor color)
Definition: SkPaint.cpp:119
T * get() const
Definition: SkRefCnt.h:303
struct MyStruct s
SK_API sk_sp< SkImage > DeferredFromEncodedData(sk_sp< SkData > encoded, std::optional< SkAlphaType > alphaType=std::nullopt)
constexpr SkCodecs::Decoder Decoder()
Definition: SkJpegDecoder.h:38
SK_API sk_sp< SkDocument > MakeDocument(SkWStream *stream, const Metadata &metadata)
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition: switches.h:57
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
@ kGrayscale
Definition: shadowutils.cpp:46
Color color() const
Definition: SkSize.h:16
static constexpr SkRect MakeXYWH(float x, float y, float w, float h)
Definition: SkRect.h:659
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:63