Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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) {
47 sk_sp<SkData> data = GetResourceAsData(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;
156 SkJFIFInfo::Type type;
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)
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
#define DEF_TEST(name, reporter)
Definition Test.h:312
#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
void drawRect(const SkRect &rect, const SkPaint &paint)
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
static sk_sp< SkData > MakeWithoutCopy(const void *data, size_t length)
Definition SkData.h:116
const uint8_t * bytes() const
Definition SkData.h:43
size_t size() const
Definition SkData.h:30
static const SkEncodedInfo & GetEncodedInfo(SkCodec &)
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()
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
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot data
Definition switches.h:41
-comparison
@ kGrayscale
Color color() const
static constexpr SkRect MakeXYWH(float x, float y, float w, float h)
Definition SkRect.h:659