LibreOffice Module vcl (master) 1
itiff.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20#include <sal/config.h>
21#include <sal/log.hxx>
22
24#include <vcl/graph.hxx>
25#include <vcl/BitmapTools.hxx>
28#include <tools/fract.hxx>
29#include <tools/stream.hxx>
31
32#include <tiffio.h>
33
34#include <filter/TiffReader.hxx>
35
36namespace
37{
38 struct Context
39 {
40 SvStream& rStream;
41 tsize_t nStart;
42 tsize_t nSize;
43 bool bAllowOneShortRead;
44 Context(SvStream& rInStream)
45 : rStream(rInStream)
46 , nStart(rInStream.Tell())
47 , nSize(rInStream.remainingSize())
48 , bAllowOneShortRead(false)
49 {
50 }
51 };
52}
53
54static tsize_t tiff_read(thandle_t handle, tdata_t buf, tsize_t size)
55{
56 Context* pContext = static_cast<Context*>(handle);
57 tsize_t nRead = pContext->rStream.ReadBytes(buf, size);
58 // tdf#149417 allow one short read, which is similar to what
59 // we do for jpeg since tdf#138950
60 if (nRead < size && pContext->bAllowOneShortRead)
61 {
62 memset(static_cast<char*>(buf) + nRead, 0, size - nRead);
63 pContext->bAllowOneShortRead = false;
64 return size;
65 }
66 return nRead;
67}
68
69static tsize_t tiff_write(thandle_t, tdata_t, tsize_t)
70{
71 return -1;
72}
73
74static toff_t tiff_seek(thandle_t handle, toff_t offset, int whence)
75{
76 Context* pContext = static_cast<Context*>(handle);
77
78 switch (whence)
79 {
80 case SEEK_SET:
81 offset = pContext->nStart + offset;
82 break;
83 case SEEK_CUR:
84 offset = pContext->rStream.Tell() + offset;
85 break;
86 case SEEK_END:
87 offset = pContext->rStream.TellEnd() + offset;
88 break;
89 default:
90 assert(false && "unknown seek type");
91 break;
92 }
93
94 pContext->rStream.Seek(offset);
95
96 return offset - pContext->nStart;
97}
98
99static int tiff_close(thandle_t)
100{
101 return 0;
102}
103
104static toff_t tiff_size(thandle_t handle)
105{
106 Context* pContext = static_cast<Context*>(handle);
107 return pContext->nSize;
108}
109
111{
112 auto origErrorHandler = TIFFSetErrorHandler(nullptr);
113 auto origWarningHandler = TIFFSetWarningHandler(nullptr);
114 comphelper::ScopeGuard restoreDefaultHandlers([&]() {
115 TIFFSetErrorHandler(origErrorHandler);
116 TIFFSetWarningHandler(origWarningHandler);
117 });
118
119 Context aContext(rTIFF);
120 TIFF* tif = TIFFClientOpen("libtiff-svstream", "r", &aContext,
123 tiff_size, nullptr, nullptr);
124
125 if (!tif)
126 return false;
127
128 const auto nOrigPos = rTIFF.Tell();
129
130 Animation aAnimation;
131
132 const bool bFuzzing = utl::ConfigManager::IsFuzzing();
133 uint64_t nTotalPixelsRequired = 0;
134
135 do
136 {
137 uint32_t w, h;
138
139 if (TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &w) != 1)
140 {
141 SAL_WARN("filter.tiff", "missing width");
142 break;
143 }
144
145 if (TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &h) != 1)
146 {
147 SAL_WARN("filter.tiff", "missing height");
148 break;
149 }
150
151 if (w > SAL_MAX_INT32 / 32 || h > SAL_MAX_INT32 / 32)
152 {
153 SAL_WARN("filter.tiff", "image too large");
154 break;
155 }
156
157 uint32_t nPixelsRequired;
158 // use the same max size that libtiff defaults to for its own utilities
159 constexpr size_t nMaxPixelsAllowed = (256 * 1024 * 1024) / 4;
160 // two buffers currently required, so limit further
161 bool bOk = !o3tl::checked_multiply(w, h, nPixelsRequired) && nPixelsRequired <= nMaxPixelsAllowed / 2;
162 SAL_WARN_IF(!bOk, "filter.tiff", "skipping oversized tiff image " << w << " x " << h);
163
164 if (!TIFFIsTiled(tif))
165 {
166 size_t nStripSize = TIFFStripSize(tif);
167 if (nStripSize > SAL_MAX_INT32)
168 {
169 SAL_WARN("filter.tiff", "skipping oversized tiff strip size " << nStripSize);
170 bOk = false;
171 }
172 }
173
174 if (bOk && bFuzzing)
175 {
176 const uint64_t MAX_PIXEL_SIZE = 120000000;
177 const uint64_t MAX_TILE_SIZE = 100000000;
178 nTotalPixelsRequired += nPixelsRequired;
179 if (TIFFTileSize64(tif) > MAX_TILE_SIZE || nTotalPixelsRequired > MAX_PIXEL_SIZE)
180 {
181 SAL_WARN("filter.tiff", "skipping large tiffs");
182 break;
183 }
184
185 if (TIFFIsTiled(tif))
186 {
187 uint32_t tw, th;
188 TIFFGetField(tif, TIFFTAG_TILEWIDTH, &tw);
189 TIFFGetField(tif, TIFFTAG_TILELENGTH, &th);
190
191 if (tw > w || th > h)
192 {
193 bOk = th < 1000 * tw && tw < 1000 * th;
194 SAL_WARN_IF(!bOk, "filter.tiff", "skipping slow bizarre ratio tile of " << tw << " x " << th << " for image of " << w << " x " << h);
195 }
196
198 if (TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &PhotometricInterpretation) == 1)
199 {
200 if (PhotometricInterpretation == PHOTOMETRIC_LOGL)
201 {
202 uint32_t nLogLBufferRequired;
203 bOk &= !o3tl::checked_multiply(tw, th, nLogLBufferRequired) && nLogLBufferRequired < MAX_PIXEL_SIZE;
204 SAL_WARN_IF(!bOk, "filter.tiff", "skipping oversized tiff tile " << tw << " x " << th);
205 }
206 }
207
208 uint16_t Compression;
209 if (TIFFGetField(tif, TIFFTAG_COMPRESSION, &Compression) == 1)
210 {
211 if (Compression == COMPRESSION_CCITTFAX4)
212 {
213 uint32_t DspRuns;
214 bOk &= !o3tl::checked_multiply(tw, static_cast<uint32_t>(4), DspRuns) && DspRuns < MAX_PIXEL_SIZE;
215 SAL_WARN_IF(!bOk, "filter.tiff", "skipping oversized tiff tile width: " << tw);
216 }
217 }
218 }
219 }
220
221 if (!bOk)
222 break;
223
224 std::vector<uint32_t> raster(nPixelsRequired);
225 aContext.bAllowOneShortRead = true;
226 if (TIFFReadRGBAImageOriented(tif, w, h, raster.data(), ORIENTATION_TOPLEFT, 1))
227 {
229 BitmapScopedWriteAccess access(bitmap);
230 if (!access)
231 {
232 SAL_WARN("filter.tiff", "cannot create image " << w << " x " << h);
233 break;
234 }
235
236 AlphaMask bitmapAlpha(Size(w, h));
237 AlphaScopedWriteAccess accessAlpha(bitmapAlpha);
238 if (!accessAlpha)
239 {
240 SAL_WARN("filter.tiff", "cannot create alpha " << w << " x " << h);
241 break;
242 }
243
244 /*
245 ORIENTATION_TOPLEFT = 1
246 ORIENTATION_TOPRIGHT = 2
247 ORIENTATION_BOTRIGHT = 3
248 ORIENTATION_BOTLEFT = 4
249 ORIENTATION_LEFTTOP = 5
250 ORIENTATION_RIGHTTOP = 6
251 ORIENTATION_RIGHTBOT = 7
252 ORIENTATION_LEFTBOT = 8
253 */
254 uint16_t nOrientation;
255 if (TIFFGetField(tif, TIFFTAG_ORIENTATION, &nOrientation) != 1)
256 nOrientation = 0;
257
258 for (uint32_t y = 0; y < h; ++y)
259 {
260 const uint32_t* src = raster.data() + w * y;
261 for (uint32_t x = 0; x < w; ++x)
262 {
263 sal_uInt8 r = TIFFGetR(*src);
264 sal_uInt8 g = TIFFGetG(*src);
265 sal_uInt8 b = TIFFGetB(*src);
266 sal_uInt8 a = TIFFGetA(*src);
267
268 uint32_t dest;
269 switch (nOrientation)
270 {
271 case ORIENTATION_LEFTBOT:
272 dest = w - 1 - x;
273 break;
274 default:
275 dest = x;
276 break;
277 }
278
279 access->SetPixel(y, dest, Color(r, g, b));
280 accessAlpha->SetPixelIndex(y, dest, a);
281 ++src;
282 }
283 }
284
285 raster.clear();
286
287 access.reset();
288 accessAlpha.reset();
289
290 BitmapEx aBitmapEx(bitmap, bitmapAlpha);
291
292 if (!bFuzzing)
293 {
294 switch (nOrientation)
295 {
296 case ORIENTATION_LEFTBOT:
297 aBitmapEx.Rotate(2700_deg10, COL_BLACK);
298 break;
299 default:
300 break;
301 }
302 }
303
304 MapMode aMapMode;
305 uint16_t ResolutionUnit = RESUNIT_NONE;
306 if (TIFFGetField(tif, TIFFTAG_RESOLUTIONUNIT, &ResolutionUnit) == 1 && ResolutionUnit != RESUNIT_NONE)
307 {
308 float xres = 0, yres = 0;
309
310 if (TIFFGetField(tif, TIFFTAG_XRESOLUTION, &xres) == 1 &&
311 TIFFGetField(tif, TIFFTAG_YRESOLUTION, &yres) == 1 &&
312 xres != 0 && yres != 0)
313 {
314 if (ResolutionUnit == RESUNIT_INCH)
315 aMapMode = MapMode(MapUnit::MapInch, Point(0,0), Fraction(1/xres), Fraction(1/yres));
316 else if (ResolutionUnit == RESUNIT_CENTIMETER)
317 aMapMode = MapMode(MapUnit::MapCM, Point(0,0), Fraction(1/xres), Fraction(1/yres));
318 }
319 }
320 aBitmapEx.SetPrefMapMode(aMapMode);
321 aBitmapEx.SetPrefSize(Size(w, h));
322
323 AnimationFrame aAnimationFrame(aBitmapEx, Point(0, 0), aBitmapEx.GetSizePixel(),
325 aAnimation.Insert(aAnimationFrame);
326 }
327 else
328 break;
329 } while (TIFFReadDirectory(tif));
330
331 TIFFClose(tif);
332
333 const auto nImages = aAnimation.Count();
334 if (nImages)
335 {
336 if (nImages == 1)
337 rGraphic = aAnimation.GetBitmapEx();
338 else
339 rGraphic = aAnimation;
340
341 // seek to end of TIFF if succeeded
343
344 return true;
345 }
346
347 rTIFF.Seek(nOrigPos);
348 return false;
349}
350
351/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
#define ANIMATION_TIMEOUT_ON_CLICK
Definition: Animation.hxx:28
const BitmapEx & GetBitmapEx() const
Definition: Animation.hxx:60
size_t Count() const
Definition: Animation.hxx:71
bool Insert(const AnimationFrame &rAnimationFrame)
Definition: Animation.cxx:411
void SetPrefMapMode(const MapMode &rPrefMapMode)
Definition: bitmapex.hxx:80
void SetPrefSize(const Size &rPrefSize)
Definition: bitmapex.hxx:77
bool Rotate(Degree10 nAngle10, const Color &rFillColor)
Rotate bitmap by the specified angle.
Definition: BitmapEx.cxx:325
const Size & GetSizePixel() const
Definition: bitmapex.hxx:73
sal_uInt64 Tell() const
sal_uInt64 Seek(sal_uInt64 nPos)
static bool IsFuzzing()
This template handles BitmapAccess the RAII way.
constexpr ::Color COL_BLACK(0x00, 0x00, 0x00)
float y
float x
#define Compression
Definition: etiff.cxx:33
#define ResolutionUnit
Definition: etiff.cxx:42
#define PhotometricInterpretation
Definition: etiff.cxx:34
static tsize_t tiff_write(thandle_t, tdata_t, tsize_t)
Definition: itiff.cxx:69
static int tiff_close(thandle_t)
Definition: itiff.cxx:99
static toff_t tiff_size(thandle_t handle)
Definition: itiff.cxx:104
static toff_t tiff_seek(thandle_t handle, toff_t offset, int whence)
Definition: itiff.cxx:74
bool ImportTiffGraphicImport(SvStream &rTIFF, Graphic &rGraphic)
Definition: itiff.cxx:110
static tsize_t tiff_read(thandle_t handle, tdata_t buf, tsize_t size)
Definition: itiff.cxx:54
uno_Any a
#define SAL_WARN_IF(condition, area, stream)
#define SAL_WARN(area, stream)
size
std::enable_if< std::is_signed< T >::value, bool >::type checked_multiply(T a, T b, T &result)
sal_Int32 h
sal_Int32 w
#define STREAM_SEEK_TO_END
unsigned char sal_uInt8
#define SAL_MAX_INT32