1//========================================================================
2//
3// DCTStream.cc
4//
5// This file is licensed under the GPLv2 or later
6//
7// Copyright 2005 Jeff Muizelaar <jeff@infidigm.net>
8// Copyright 2005-2010, 2012, 2017, 2020-2023 Albert Astals Cid <aacid@kde.org>
9// Copyright 2009 Ryszard Trojnacki <rysiek@menel.com>
10// Copyright 2010 Carlos Garcia Campos <carlosgc@gnome.org>
11// Copyright 2011 Daiki Ueno <ueno@unixuser.org>
12// Copyright 2011 Tomas Hoger <thoger@redhat.com>
13// Copyright 2012, 2013 Thomas Freitag <Thomas.Freitag@alfa.de>
14// Copyright 2017 Adrian Johnson <ajohnson@redneon.com>
15// Copyright 2020 LluĂ­s Batlle i Rossell <viric@viric.name>
16//
17//========================================================================
18
19#include "DCTStream.h"
20
21static void str_init_source(j_decompress_ptr cinfo) { }
22
23static boolean str_fill_input_buffer(j_decompress_ptr cinfo)
24{
25 int c;
26 struct str_src_mgr *src = (struct str_src_mgr *)cinfo->src;
27 if (src->index == 0) {
28 c = 0xFF;
29 src->index++;
30 } else if (src->index == 1) {
31 c = 0xD8;
32 src->index++;
33 } else {
34 c = src->str->getChar();
35 }
36 src->buffer = c;
37 src->pub.next_input_byte = &src->buffer;
38 src->pub.bytes_in_buffer = 1;
39 if (c != EOF) {
40 return TRUE;
41 } else {
42 return FALSE;
43 }
44}
45
46static void str_skip_input_data(j_decompress_ptr cinfo, long num_bytes)
47{
48 struct str_src_mgr *src = (struct str_src_mgr *)cinfo->src;
49 if (num_bytes > 0) {
50 while (num_bytes > (long)src->pub.bytes_in_buffer) {
51 num_bytes -= (long)src->pub.bytes_in_buffer;
52 str_fill_input_buffer(cinfo);
53 }
54 src->pub.next_input_byte += (size_t)num_bytes;
55 src->pub.bytes_in_buffer -= (size_t)num_bytes;
56 }
57}
58
59static void str_term_source(j_decompress_ptr cinfo) { }
60
61DCTStream::DCTStream(Stream *strA, int colorXformA, Dict *dict, int recursion) : FilterStream(strA)
62{
63 colorXform = colorXformA;
64 if (dict != nullptr) {
65 Object obj = dict->lookup(key: "Width", recursion);
66 err.width = (obj.isInt() && obj.getInt() <= JPEG_MAX_DIMENSION) ? obj.getInt() : 0;
67 obj = dict->lookup(key: "Height", recursion);
68 err.height = (obj.isInt() && obj.getInt() <= JPEG_MAX_DIMENSION) ? obj.getInt() : 0;
69 } else {
70 err.height = err.width = 0;
71 }
72 init();
73}
74
75DCTStream::~DCTStream()
76{
77 jpeg_destroy_decompress(cinfo: &cinfo);
78 delete str;
79}
80
81static void exitErrorHandler(jpeg_common_struct *error)
82{
83 j_decompress_ptr cinfo = (j_decompress_ptr)error;
84 str_error_mgr *err = (struct str_error_mgr *)cinfo->err;
85 if (cinfo->err->msg_code == JERR_IMAGE_TOO_BIG && err->width != 0 && err->height != 0) {
86 cinfo->image_height = err->height;
87 cinfo->image_width = err->width;
88 } else {
89 longjmp(env: err->setjmp_buffer, val: 1);
90 }
91}
92
93void DCTStream::init()
94{
95 jpeg_std_error(err: &err.pub);
96 err.pub.error_exit = &exitErrorHandler;
97 src.pub.init_source = str_init_source;
98 src.pub.fill_input_buffer = str_fill_input_buffer;
99 src.pub.skip_input_data = str_skip_input_data;
100 src.pub.resync_to_restart = jpeg_resync_to_restart;
101 src.pub.term_source = str_term_source;
102 src.pub.bytes_in_buffer = 0;
103 src.pub.next_input_byte = nullptr;
104 src.str = str;
105 src.index = 0;
106 current = nullptr;
107 limit = nullptr;
108
109 cinfo.err = &err.pub;
110 if (!setjmp(err.setjmp_buffer)) {
111 jpeg_create_decompress(&cinfo);
112 cinfo.src = (jpeg_source_mgr *)&src;
113 }
114 row_buffer = nullptr;
115}
116
117void DCTStream::reset()
118{
119 int row_stride;
120
121 str->reset();
122
123 if (row_buffer) {
124 jpeg_destroy_decompress(cinfo: &cinfo);
125 init();
126 }
127
128 // JPEG data has to start with 0xFF 0xD8
129 // but some pdf like the one on
130 // https://bugs.freedesktop.org/show_bug.cgi?id=3299
131 // does have some garbage before that this seeks for
132 // the start marker...
133 bool startFound = false;
134 int c = 0, c2 = 0;
135 while (!startFound) {
136 if (!c) {
137 c = str->getChar();
138 if (c == -1) {
139 error(category: errSyntaxError, pos: -1, msg: "Could not find start of jpeg data");
140 return;
141 }
142 if (c != 0xFF) {
143 c = 0;
144 }
145 } else {
146 c2 = str->getChar();
147 if (c2 != 0xD8) {
148 c = 0;
149 c2 = 0;
150 } else {
151 startFound = true;
152 }
153 }
154 }
155
156 if (!setjmp(err.setjmp_buffer)) {
157 if (jpeg_read_header(cinfo: &cinfo, TRUE) != JPEG_SUSPENDED) {
158 // figure out color transform
159 if (colorXform == -1 && !cinfo.saw_Adobe_marker) {
160 if (cinfo.num_components == 3) {
161 if (cinfo.saw_JFIF_marker) {
162 colorXform = 1;
163 } else if (cinfo.cur_comp_info[0]->component_id == 82 && cinfo.cur_comp_info[1]->component_id == 71 && cinfo.cur_comp_info[2]->component_id == 66) { // ASCII "RGB"
164 colorXform = 0;
165 } else {
166 colorXform = 1;
167 }
168 } else {
169 colorXform = 0;
170 }
171 } else if (cinfo.saw_Adobe_marker) {
172 colorXform = cinfo.Adobe_transform;
173 }
174
175 switch (cinfo.num_components) {
176 case 3:
177 cinfo.jpeg_color_space = colorXform ? JCS_YCbCr : JCS_RGB;
178 break;
179 case 4:
180 cinfo.jpeg_color_space = colorXform ? JCS_YCCK : JCS_CMYK;
181 break;
182 }
183
184 jpeg_start_decompress(cinfo: &cinfo);
185
186 row_stride = cinfo.output_width * cinfo.output_components;
187 row_buffer = cinfo.mem->alloc_sarray((j_common_ptr)&cinfo, JPOOL_IMAGE, row_stride, 1);
188 }
189 }
190}
191
192bool DCTStream::readLine()
193{
194 if (cinfo.output_scanline < cinfo.output_height) {
195 if (!setjmp(err.setjmp_buffer)) {
196 if (!jpeg_read_scanlines(cinfo: &cinfo, scanlines: row_buffer, max_lines: 1)) {
197 return false;
198 } else {
199 current = &row_buffer[0][0];
200 limit = &row_buffer[0][(cinfo.output_width - 1) * cinfo.output_components] + cinfo.output_components;
201 return true;
202 }
203 } else {
204 return false;
205 }
206 } else {
207 return false;
208 }
209}
210
211int DCTStream::getChar()
212{
213 if (current == limit) {
214 if (!readLine()) {
215 return EOF;
216 }
217 }
218
219 return *current++;
220}
221
222int DCTStream::getChars(int nChars, unsigned char *buffer)
223{
224 for (int i = 0; i < nChars;) {
225 if (current == limit) {
226 if (!readLine()) {
227 return i;
228 }
229 }
230 intptr_t left = limit - current;
231 if (left + i > nChars) {
232 left = nChars - i;
233 }
234 memcpy(dest: buffer + i, src: current, n: left);
235 current += left;
236 i += static_cast<int>(left);
237 }
238 return nChars;
239}
240
241int DCTStream::lookChar()
242{
243 if (unlikely(current == nullptr)) {
244 return EOF;
245 }
246 return *current;
247}
248
249GooString *DCTStream::getPSFilter(int psLevel, const char *indent)
250{
251 GooString *s;
252
253 if (psLevel < 2) {
254 return nullptr;
255 }
256 if (!(s = str->getPSFilter(psLevel, indent))) {
257 return nullptr;
258 }
259 s->append(str: indent)->append(str: "<< >> /DCTDecode filter\n");
260 return s;
261}
262
263bool DCTStream::isBinary(bool last) const
264{
265 return str->isBinary(last: true);
266}
267

source code of poppler/poppler/DCTStream.cc