LibreOffice Module vcl (master) 1
GraphicFormatDetector.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
22#include <algorithm>
23
27#include <tools/solar.h>
28#include <tools/zcodec.hxx>
29#include <tools/fract.hxx>
30#include <filter/WebpReader.hxx>
32#include <vcl/outdev.hxx>
33#include <utility>
34
35constexpr sal_uInt32 SVG_CHECK_SIZE = 2048;
36constexpr sal_uInt32 WMF_EMF_CHECK_SIZE = 44;
37constexpr sal_uInt32 DATA_SIZE = 640;
38
39namespace
40{
41class SeekGuard
42{
43public:
44 SeekGuard(SvStream& rStream, sal_uInt64 nStartPosition)
45 : mrStream(rStream)
46 , mnStartPosition(nStartPosition)
47 {
48 }
49 ~SeekGuard() { mrStream.Seek(mnStartPosition); }
50
51private:
52 SvStream& mrStream;
53 sal_uInt64 mnStartPosition;
54};
55}
56
57namespace vcl
58{
59bool peekGraphicFormat(SvStream& rStream, OUString& rFormatExtension, bool bTest)
60{
61 vcl::GraphicFormatDetector aDetector(rStream, rFormatExtension);
62 if (!aDetector.detect())
63 return false;
64
65 // The following variable is used when bTest == true. It remains false
66 // if the format (rFormatExtension) has not yet been set.
67 bool bSomethingTested = false;
68
69 // Now the different formats are checked. The order *does* matter. e.g. a MET file
70 // could also go through the BMP test, however, a BMP file can hardly go through the MET test.
71 // So MET should be tested prior to BMP. However, theoretically a BMP file could conceivably
72 // go through the MET test. These problems are of course not only in MET and BMP.
73 // Therefore, in the case of a format check (bTest == true) we only test *exactly* this
74 // format. Everything else could have fatal consequences, for example if the user says it is
75 // a BMP file (and it is a BMP) file, and the file would go through the MET test ...
76
77 if (!bTest || rFormatExtension.startsWith("MET"))
78 {
79 bSomethingTested = true;
80 if (aDetector.checkMET())
81 {
82 rFormatExtension = getImportFormatShortName(aDetector.getMetadata().mnFormat);
83 return true;
84 }
85 }
86
87 if (!bTest || rFormatExtension.startsWith("BMP"))
88 {
89 bSomethingTested = true;
90 if (aDetector.checkBMP())
91 {
92 rFormatExtension = getImportFormatShortName(aDetector.getMetadata().mnFormat);
93 return true;
94 }
95 }
96
97 if (!bTest || rFormatExtension.startsWith("WMF") || rFormatExtension.startsWith("WMZ")
98 || rFormatExtension.startsWith("EMF") || rFormatExtension.startsWith("EMZ"))
99 {
100 bSomethingTested = true;
101 if (aDetector.checkWMF() || aDetector.checkEMF())
102 {
103 rFormatExtension = getImportFormatShortName(aDetector.getMetadata().mnFormat);
104 return true;
105 }
106 }
107
108 if (!bTest || rFormatExtension.startsWith("PCX"))
109 {
110 bSomethingTested = true;
111 if (aDetector.checkPCX())
112 {
113 rFormatExtension = getImportFormatShortName(aDetector.getMetadata().mnFormat);
114 return true;
115 }
116 }
117
118 if (!bTest || rFormatExtension.startsWith("TIF"))
119 {
120 bSomethingTested = true;
121 if (aDetector.checkTIF())
122 {
123 rFormatExtension = getImportFormatShortName(aDetector.getMetadata().mnFormat);
124 return true;
125 }
126 }
127
128 if (!bTest || rFormatExtension.startsWith("GIF"))
129 {
130 bSomethingTested = true;
131 if (aDetector.checkGIF())
132 {
133 rFormatExtension = getImportFormatShortName(aDetector.getMetadata().mnFormat);
134 return true;
135 }
136 }
137
138 if (!bTest || rFormatExtension.startsWith("APNG"))
139 {
140 bSomethingTested = true;
141 if (aDetector.checkAPNG())
142 {
143 rFormatExtension = getImportFormatShortName(aDetector.getMetadata().mnFormat);
144 return true;
145 }
146 }
147
148 if (!bTest || rFormatExtension.startsWith("PNG"))
149 {
150 bSomethingTested = true;
151 if (aDetector.checkPNG())
152 {
153 rFormatExtension = getImportFormatShortName(aDetector.getMetadata().mnFormat);
154 return true;
155 }
156 }
157
158 if (!bTest || rFormatExtension.startsWith("JPG"))
159 {
160 bSomethingTested = true;
161 if (aDetector.checkJPG())
162 {
163 rFormatExtension = getImportFormatShortName(aDetector.getMetadata().mnFormat);
164 return true;
165 }
166 }
167
168 if (!bTest || rFormatExtension.startsWith("SVM"))
169 {
170 bSomethingTested = true;
171 if (aDetector.checkSVM())
172 {
173 rFormatExtension = getImportFormatShortName(aDetector.getMetadata().mnFormat);
174 return true;
175 }
176 }
177
178 if (!bTest || rFormatExtension.startsWith("PCD"))
179 {
180 bSomethingTested = true;
181 if (aDetector.checkPCD())
182 {
183 rFormatExtension = getImportFormatShortName(aDetector.getMetadata().mnFormat);
184 return true;
185 }
186 }
187
188 if (!bTest || rFormatExtension.startsWith("PSD"))
189 {
190 bSomethingTested = true;
191 if (aDetector.checkPSD())
192 {
193 rFormatExtension = getImportFormatShortName(aDetector.getMetadata().mnFormat);
194 return true;
195 }
196 }
197
198 if (!bTest || rFormatExtension.startsWith("EPS"))
199 {
200 bSomethingTested = true;
201 if (aDetector.checkEPS())
202 {
203 rFormatExtension = getImportFormatShortName(aDetector.getMetadata().mnFormat);
204 return true;
205 }
206 }
207
208 if (!bTest || rFormatExtension.startsWith("DXF"))
209 {
210 if (aDetector.checkDXF())
211 {
212 rFormatExtension = getImportFormatShortName(aDetector.getMetadata().mnFormat);
213 return true;
214 }
215 }
216
217 if (!bTest || rFormatExtension.startsWith("PCT"))
218 {
219 bSomethingTested = true;
220 if (aDetector.checkPCT())
221 {
222 rFormatExtension = getImportFormatShortName(aDetector.getMetadata().mnFormat);
223 return true;
224 }
225 }
226
227 if (!bTest || rFormatExtension.startsWith("PBM") || rFormatExtension.startsWith("PGM")
228 || rFormatExtension.startsWith("PPM"))
229 {
230 bSomethingTested = true;
231 if (aDetector.checkPBM() || aDetector.checkPGM() || aDetector.checkPPM())
232 {
233 rFormatExtension = getImportFormatShortName(aDetector.getMetadata().mnFormat);
234 return true;
235 }
236 }
237
238 if (!bTest || rFormatExtension.startsWith("RAS"))
239 {
240 bSomethingTested = true;
241 if (aDetector.checkRAS())
242 {
243 rFormatExtension = getImportFormatShortName(aDetector.getMetadata().mnFormat);
244 return true;
245 }
246 }
247
248 if (!bTest)
249 {
250 bSomethingTested = true;
251 if (aDetector.checkXPM())
252 {
253 rFormatExtension = getImportFormatShortName(aDetector.getMetadata().mnFormat);
254 return true;
255 }
256 }
257 else if (rFormatExtension.startsWith("XPM"))
258 {
259 return true;
260 }
261
262 if (!bTest)
263 {
264 if (aDetector.checkXBM())
265 {
266 rFormatExtension = getImportFormatShortName(aDetector.getMetadata().mnFormat);
267 return true;
268 }
269 }
270 else if (rFormatExtension.startsWith("XBM"))
271 {
272 return true;
273 }
274
275 if (!bTest)
276 {
277 if (aDetector.checkSVG())
278 {
279 rFormatExtension = getImportFormatShortName(aDetector.getMetadata().mnFormat);
280 return true;
281 }
282 }
283 else if (rFormatExtension.startsWith("SVG"))
284 {
285 return true;
286 }
287
288 if (!bTest || rFormatExtension.startsWith("TGA"))
289 {
290 bSomethingTested = true;
291 if (aDetector.checkTGA())
292 {
293 rFormatExtension = getImportFormatShortName(aDetector.getMetadata().mnFormat);
294 return true;
295 }
296 }
297
298 if (!bTest || rFormatExtension.startsWith("MOV"))
299 {
300 if (aDetector.checkMOV())
301 {
302 rFormatExtension = getImportFormatShortName(aDetector.getMetadata().mnFormat);
303 return true;
304 }
305 }
306
307 if (!bTest || rFormatExtension.startsWith("PDF"))
308 {
309 if (aDetector.checkPDF())
310 {
311 rFormatExtension = getImportFormatShortName(aDetector.getMetadata().mnFormat);
312 return true;
313 }
314 }
315
316 if (!bTest || rFormatExtension.startsWith("WEBP"))
317 {
318 bSomethingTested = true;
319 if (aDetector.checkWEBP())
320 {
321 rFormatExtension = getImportFormatShortName(aDetector.getMetadata().mnFormat);
322 return true;
323 }
324 }
325
326 return bTest && !bSomethingTested;
327}
328
329namespace
330{
331bool isPCT(SvStream& rStream, sal_uLong nStreamPos, sal_uLong nStreamLen)
332{
333 sal_uInt8 sBuf[3];
334 // store number format
335 SvStreamEndian oldNumberFormat = rStream.GetEndian();
336 sal_uInt32 nOffset; // in MS documents the pict format is used without the first 512 bytes
337 for (nOffset = 0; (nOffset <= 512) && ((nStreamPos + nOffset + 14) <= nStreamLen);
338 nOffset += 512)
339 {
340 short y1, x1, y2, x2;
341 bool bdBoxOk = true;
342
343 rStream.Seek(nStreamPos + nOffset);
344 // size of the pict in version 1 pict ( 2bytes) : ignored
345 rStream.SeekRel(2);
346 // bounding box (bytes 2 -> 9)
347 rStream.SetEndian(SvStreamEndian::BIG);
348 rStream.ReadInt16(y1).ReadInt16(x1).ReadInt16(y2).ReadInt16(x2);
349 rStream.SetEndian(oldNumberFormat); // reset format
350
351 // read version op
352 rStream.ReadBytes(sBuf, 3);
353
354 if (!rStream.good())
355 break;
356
357 if (x1 > x2 || y1 > y2 || // bad bdbox
358 (x1 == x2 && y1 == y2) || // 1 pixel picture
359 x2 - x1 > 2048 || y2 - y1 > 2048) // picture abnormally big
360 bdBoxOk = false;
361
362 // see http://developer.apple.com/legacy/mac/library/documentation/mac/pdf/Imaging_With_QuickDraw/Appendix_A.pdf
363 // normal version 2 - page A23 and A24
364 if (sBuf[0] == 0x00 && sBuf[1] == 0x11 && sBuf[2] == 0x02)
365 return true;
366 // normal version 1 - page A25
367 else if (sBuf[0] == 0x11 && sBuf[1] == 0x01 && bdBoxOk)
368 return true;
369 }
370 return false;
371}
372
373} // end anonymous namespace
374
375GraphicFormatDetector::GraphicFormatDetector(SvStream& rStream, OUString aFormatExtension,
376 bool bExtendedInfo)
377 : mrStream(rStream)
378 , maExtension(std::move(aFormatExtension))
379 , mnFirstLong(0)
380 , mnSecondLong(0)
381 , mnStreamPosition(0)
382 , mnStreamLength(0)
383 , mbExtendedInfo(bExtendedInfo)
384 , mbWasCompressed(false)
385 , maMetadata()
386{
387}
388
390{
391 maFirstBytes.clear();
392 maFirstBytes.resize(256, 0);
393
394 mnFirstLong = 0;
395 mnSecondLong = 0;
396
399 SeekGuard aGuard(mrStream, mnStreamPosition);
400
401 if (!mnStreamLength)
402 {
403 SvLockBytes* pLockBytes = mrStream.GetLockBytes();
404 if (pLockBytes)
405 pLockBytes->SetSynchronMode();
407 }
408
409 if (mnStreamLength == 0)
410 {
411 return false; // this prevents at least a STL assertion
412 }
413 else if (mnStreamLength >= maFirstBytes.size())
414 {
415 // load first 256 bytes into a buffer
416 sal_uInt64 nRead = mrStream.ReadBytes(maFirstBytes.data(), maFirstBytes.size());
417 if (nRead < maFirstBytes.size())
418 mnStreamLength = nRead;
419 }
420 else
421 {
423 }
424
425 if (mrStream.GetError())
426 return false;
427
428 for (int i = 0; i < 4; ++i)
429 {
430 mnFirstLong = (mnFirstLong << 8) | sal_uInt32(maFirstBytes[i]);
431 mnSecondLong = (mnSecondLong << 8) | sal_uInt32(maFirstBytes[i + 4]);
432 }
433 return true;
434}
435
437{
438 if (maFirstBytes[2] != 0xd3)
439 return false;
440 SeekGuard aGuard(mrStream, mnStreamPosition);
441 mrStream.SetEndian(SvStreamEndian::BIG);
443 sal_uInt16 nFieldSize;
445
446 mrStream.ReadUInt16(nFieldSize).ReadUChar(nMagic);
447 for (int i = 0; i < 3; i++)
448 {
449 if (nFieldSize < 6)
450 return false;
451 if (mnStreamLength < mrStream.Tell() + nFieldSize)
452 return false;
453 mrStream.SeekRel(nFieldSize - 3);
454 mrStream.ReadUInt16(nFieldSize).ReadUChar(nMagic);
455 if (nMagic != 0xd3)
456 return false;
457 }
458 mrStream.SetEndian(SvStreamEndian::LITTLE);
459
460 if (mrStream.GetError())
461 return false;
462
464 return true;
465}
466
468{
469 SeekGuard aGuard(mrStream, mnStreamPosition);
470 bool bRet = false;
471 sal_uInt8 nOffset;
472
473 // We're possibly also able to read an OS/2 bitmap array
474 // ('BA'), therefore we must adjust the offset to discover the
475 // first bitmap in the array
476 if (maFirstBytes[0] == 0x42 && maFirstBytes[1] == 0x41)
477 nOffset = 14;
478 else
479 nOffset = 0;
480
481 // Now we initially test on 'BM'
482 if (maFirstBytes[0 + nOffset] == 0x42 && maFirstBytes[1 + nOffset] == 0x4d)
483 {
484 // OS/2 can set the Reserved flags to a value other than 0
485 // (which they really should not do...);
486 // In this case we test the size of the BmpInfoHeaders
487 if ((maFirstBytes[6 + nOffset] == 0x00 && maFirstBytes[7 + nOffset] == 0x00
488 && maFirstBytes[8 + nOffset] == 0x00 && maFirstBytes[9 + nOffset] == 0x00)
489 || maFirstBytes[14 + nOffset] == 0x28 || maFirstBytes[14 + nOffset] == 0x0c)
490 {
492 bRet = true;
493 if (mbExtendedInfo)
494 {
495 sal_uInt32 nTemp32;
496 sal_uInt16 nTemp16;
497 sal_uInt32 nCompression;
498
499 mrStream.SetEndian(SvStreamEndian::LITTLE);
500 mrStream.Seek(mnStreamPosition + nOffset + 2);
501
502 // up to first info
503 mrStream.SeekRel(0x10);
504
505 // Pixel width
506 mrStream.ReadUInt32(nTemp32);
508
509 // Pixel height
510 mrStream.ReadUInt32(nTemp32);
512
513 // Planes
514 mrStream.ReadUInt16(nTemp16);
515 maMetadata.mnPlanes = nTemp16;
516
517 // BitCount
518 mrStream.ReadUInt16(nTemp16);
519 maMetadata.mnBitsPerPixel = nTemp16;
520
521 // Compression
522 mrStream.ReadUInt32(nTemp32);
523 nCompression = nTemp32;
524
525 // logical width
526 mrStream.SeekRel(4);
527 mrStream.ReadUInt32(nTemp32);
528 sal_uInt32 nXPelsPerMeter = 0;
529 if (nTemp32)
530 {
532 / nTemp32);
533 nXPelsPerMeter = nTemp32;
534 }
535
536 // logical height
537 mrStream.ReadUInt32(nTemp32);
538 sal_uInt32 nYPelsPerMeter = 0;
539 if (nTemp32)
540 {
542 / nTemp32);
543 nYPelsPerMeter = nTemp32;
544 }
545
546 // further validation, check for rational values
547 if ((maMetadata.mnBitsPerPixel > 24) || (nCompression > 3))
548 {
550 bRet = false;
551 }
552
553 if (bRet && nXPelsPerMeter && nYPelsPerMeter)
554 {
556 = MapMode(MapUnit::MapMM, Point(), Fraction(1000, nXPelsPerMeter),
557 Fraction(1000, nYPelsPerMeter));
558
561 }
562 }
563 }
564 }
565 return bRet;
566}
567
569{
570 sal_uInt64 nCheckSize = std::min<sal_uInt64>(mnStreamLength, 256);
571 sal_uInt8 sExtendedOrDecompressedFirstBytes[WMF_EMF_CHECK_SIZE];
572 sal_uInt64 nDecompressedSize = nCheckSize;
573 // check if it is gzipped -> wmz
574 checkAndUncompressBuffer(sExtendedOrDecompressedFirstBytes, WMF_EMF_CHECK_SIZE,
575 nDecompressedSize);
576 if (mnFirstLong == 0xd7cdc69a || mnFirstLong == 0x01000900)
577 {
578 if (mbWasCompressed)
580 else
582 return true;
583 }
584 return false;
585}
586
588{
589 sal_uInt64 nCheckSize = std::min<sal_uInt64>(mnStreamLength, 256);
590 sal_uInt8 sExtendedOrDecompressedFirstBytes[WMF_EMF_CHECK_SIZE];
591 sal_uInt64 nDecompressedSize = nCheckSize;
592 // check if it is gzipped -> emz
593 sal_uInt8* pCheckArray = checkAndUncompressBuffer(sExtendedOrDecompressedFirstBytes,
594 WMF_EMF_CHECK_SIZE, nDecompressedSize);
595 if (mnFirstLong == 0x01000000 && pCheckArray[40] == 0x20 && pCheckArray[41] == 0x45
596 && pCheckArray[42] == 0x4d && pCheckArray[43] == 0x46)
597 {
598 if (mbWasCompressed)
600 else
602 if (mbExtendedInfo)
603 {
604 sal_Int32 nBoundLeft = 0, nBoundTop = 0, nBoundRight = 0, nBoundBottom = 0;
605 sal_Int32 nFrameLeft = 0, nFrameTop = 0, nFrameRight = 0, nFrameBottom = 0;
606 nBoundLeft = pCheckArray[8] | (pCheckArray[9] << 8) | (pCheckArray[10] << 16)
607 | (pCheckArray[11] << 24);
608 nBoundTop = pCheckArray[12] | (pCheckArray[13] << 8) | (pCheckArray[14] << 16)
609 | (pCheckArray[15] << 24);
610 nBoundRight = pCheckArray[16] | (pCheckArray[17] << 8) | (pCheckArray[18] << 16)
611 | (pCheckArray[19] << 24);
612 nBoundBottom = pCheckArray[20] | (pCheckArray[21] << 8) | (pCheckArray[22] << 16)
613 | (pCheckArray[23] << 24);
614 nFrameLeft = pCheckArray[24] | (pCheckArray[25] << 8) | (pCheckArray[26] << 16)
615 | (pCheckArray[27] << 24);
616 nFrameTop = pCheckArray[28] | (pCheckArray[29] << 8) | (pCheckArray[30] << 16)
617 | (pCheckArray[31] << 24);
618 nFrameRight = pCheckArray[32] | (pCheckArray[33] << 8) | (pCheckArray[34] << 16)
619 | (pCheckArray[35] << 24);
620 nFrameBottom = pCheckArray[36] | (pCheckArray[37] << 8) | (pCheckArray[38] << 16)
621 | (pCheckArray[39] << 24);
622 // size in pixels
623 maMetadata.maPixSize.setWidth(nBoundRight - nBoundLeft + 1);
624 maMetadata.maPixSize.setHeight(nBoundBottom - nBoundTop + 1);
625 // size in 0.01mm units
626 maMetadata.maLogSize.setWidth(nFrameRight - nFrameLeft + 1);
627 maMetadata.maLogSize.setHeight(nFrameBottom - nFrameTop + 1);
628 }
629 return true;
630 }
631 return false;
632}
633
635{
636 // ! Because 0x0a can be interpreted as LF too ...
637 // we can't be sure that this special sign represent a PCX file only.
638 // Every Ascii file is possible here :-(
639 // We must detect the whole header.
640 bool bRet = false;
641 sal_uInt8 cByte = 0;
642 SeekGuard aGuard(mrStream, mrStream.Tell());
643 mrStream.SetEndian(SvStreamEndian::LITTLE);
644 mrStream.ReadUChar(cByte);
645 if (cByte == 0x0a)
646 {
648 mrStream.SeekRel(1);
649 // compression
650 mrStream.ReadUChar(cByte);
651 bRet = (cByte == 0 || cByte == 1);
652 if (bRet)
653 {
654 sal_uInt16 nTemp16;
655 sal_uInt16 nXmin;
656 sal_uInt16 nXmax;
657 sal_uInt16 nYmin;
658 sal_uInt16 nYmax;
659 sal_uInt16 nDPIx;
660 sal_uInt16 nDPIy;
661
662 // Bits/Pixel
663 mrStream.ReadUChar(cByte);
665
666 // image dimensions
667 mrStream.ReadUInt16(nTemp16);
668 nXmin = nTemp16;
669 mrStream.ReadUInt16(nTemp16);
670 nYmin = nTemp16;
671 mrStream.ReadUInt16(nTemp16);
672 nXmax = nTemp16;
673 mrStream.ReadUInt16(nTemp16);
674 nYmax = nTemp16;
675
676 maMetadata.maPixSize.setWidth(nXmax - nXmin + 1);
677 maMetadata.maPixSize.setHeight(nYmax - nYmin + 1);
678
679 // resolution
680 mrStream.ReadUInt16(nTemp16);
681 nDPIx = nTemp16;
682 mrStream.ReadUInt16(nTemp16);
683 nDPIy = nTemp16;
684
685 // set logical size
686 MapMode aMap(MapUnit::MapInch, Point(), Fraction(1, nDPIx), Fraction(1, nDPIy));
688 MapMode(MapUnit::Map100thMM));
689
690 // number of color planes
691 cByte = 5; // Illegal value in case of EOF.
692 mrStream.SeekRel(49);
693 mrStream.ReadUChar(cByte);
694 maMetadata.mnPlanes = cByte;
695
696 bRet = (maMetadata.mnPlanes <= 4);
697 }
698 }
699 return bRet;
700}
701
703{
704 SeekGuard aGuard(mrStream, mrStream.Tell());
706 bool bRet = false;
707 sal_uInt8 cByte1 = 0;
708 sal_uInt8 cByte2 = 1;
709
710 mrStream.ReadUChar(cByte1);
711 mrStream.ReadUChar(cByte2);
712 if (cByte1 == cByte2)
713 {
714 bool bDetectOk = false;
715
716 if (cByte1 == 0x49)
717 {
718 mrStream.SetEndian(SvStreamEndian::LITTLE);
719 bDetectOk = true;
720 }
721 else if (cByte1 == 0x4d)
722 {
723 mrStream.SetEndian(SvStreamEndian::BIG);
724 bDetectOk = true;
725 }
726
727 if (bDetectOk)
728 {
729 sal_uInt16 nTemp16 = 0;
730
731 mrStream.ReadUInt16(nTemp16);
732 if (nTemp16 == 0x2a)
733 {
735 bRet = true;
736
737 if (mbExtendedInfo)
738 {
740 sal_uLong nMax = DATA_SIZE - 48;
741 sal_uInt32 nTemp32 = 0;
742
743 // Offset of the first IFD
744 mrStream.ReadUInt32(nTemp32);
745 nCount = nTemp32 + 2;
746 mrStream.SeekRel(nCount - 0x08);
747
748 if (nCount < nMax)
749 {
750 bool bOk = false;
751
752 // read tags till we find Tag256 ( Width )
753 // do not read more bytes than DATA_SIZE
754 mrStream.ReadUInt16(nTemp16);
755 while (nTemp16 != 256)
756 {
757 bOk = nCount < nMax;
758 if (!bOk)
759 {
760 break;
761 }
762 mrStream.SeekRel(10);
763 mrStream.ReadUInt16(nTemp16);
764 nCount += 12;
765 }
766
767 if (bOk)
768 {
769 // width
770 mrStream.ReadUInt16(nTemp16);
771 mrStream.SeekRel(4);
772 if (nTemp16 == 3)
773 {
774 mrStream.ReadUInt16(nTemp16);
776 mrStream.SeekRel(2);
777 }
778 else
779 {
780 mrStream.ReadUInt32(nTemp32);
782 }
783
784 // height
785 mrStream.SeekRel(2);
786 mrStream.ReadUInt16(nTemp16);
787 mrStream.SeekRel(4);
788 if (nTemp16 == 3)
789 {
790 mrStream.ReadUInt16(nTemp16);
792 mrStream.SeekRel(2);
793 }
794 else
795 {
796 mrStream.ReadUInt32(nTemp32);
798 }
799
800 // Bits/Pixel
801 mrStream.ReadUInt16(nTemp16);
802 if (nTemp16 == 258)
803 {
804 mrStream.SeekRel(6);
805 mrStream.ReadUInt16(nTemp16);
806 maMetadata.mnBitsPerPixel = nTemp16;
807 mrStream.SeekRel(2);
808 }
809 else
810 mrStream.SeekRel(-2);
811
812 // compression
813 mrStream.ReadUInt16(nTemp16);
814 if (nTemp16 == 259)
815 {
816 mrStream.SeekRel(6);
817 mrStream.ReadUInt16(nTemp16); // compression
818 mrStream.SeekRel(2);
819 }
820 else
821 mrStream.SeekRel(-2);
822 }
823 }
824 }
825 }
826 }
827 }
828 return bRet;
829}
830
832{
833 if (mnFirstLong == 0x47494638 && (maFirstBytes[4] == 0x37 || maFirstBytes[4] == 0x39)
834 && maFirstBytes[5] == 0x61)
835 {
837 if (mbExtendedInfo)
838 {
839 sal_uInt16 nWidth = maFirstBytes[6] | (maFirstBytes[7] << 8);
840 sal_uInt16 nHeight = maFirstBytes[8] | (maFirstBytes[9] << 8);
841 maMetadata.maPixSize = Size(nWidth, nHeight);
842 maMetadata.mnBitsPerPixel = ((maFirstBytes[10] & 112) >> 4) + 1;
843 }
844 return true;
845 }
846 return false;
847}
848
850{
851 SeekGuard aGuard(mrStream, mnStreamPosition);
852 uint64_t nSignature = (static_cast<uint64_t>(mnFirstLong) << 32) | mnSecondLong;
853 if (nSignature == PNG_SIGNATURE)
854 {
856 if (mbExtendedInfo)
857 {
858 sal_uInt32 nTemp32;
860 do
861 {
862 sal_uInt8 cByte = 0;
863
864 // IHDR-Chunk
865 mrStream.SeekRel(8);
866
867 // width
868 mrStream.ReadUInt32(nTemp32);
869 if (!mrStream.good())
870 break;
872
873 // height
874 mrStream.ReadUInt32(nTemp32);
875 if (!mrStream.good())
876 break;
878
879 // Bits/Pixel
880 mrStream.ReadUChar(cByte);
881 if (!mrStream.good())
882 break;
884
885 // Colour type - check whether it supports alpha values
886 sal_uInt8 cColType = 0;
887 mrStream.ReadUChar(cColType);
888 if (!mrStream.good())
889 break;
891 = (cColType == 4 || cColType == 6);
892
893 // Planes always 1;
894 // compression always
896
897 sal_uInt32 nLen32 = 0;
898 nTemp32 = 0;
899
900 mrStream.SeekRel(7);
901
902 // read up to the start of the image
903 mrStream.ReadUInt32(nLen32);
904 mrStream.ReadUInt32(nTemp32);
905 while (mrStream.good() && nTemp32 != PNG_IDAT_SIGNATURE)
906 {
907 if (nTemp32 == PNG_PHYS_SIGNATURE) // physical pixel dimensions
908 {
909 sal_uLong nXRes;
910 sal_uLong nYRes;
911
912 // horizontal resolution
913 nTemp32 = 0;
914 mrStream.ReadUInt32(nTemp32);
915 nXRes = nTemp32;
916
917 // vertical resolution
918 nTemp32 = 0;
919 mrStream.ReadUInt32(nTemp32);
920 nYRes = nTemp32;
921
922 // unit
923 cByte = 0;
924 mrStream.ReadUChar(cByte);
925
926 if (cByte)
927 {
928 if (nXRes)
930 (maMetadata.maPixSize.Width() * 100000) / nXRes);
931
932 if (nYRes)
934 (maMetadata.maPixSize.Height() * 100000) / nYRes);
935 }
936
937 nLen32 -= 9;
938 }
939 else if (nTemp32 == PNG_TRNS_SIGNATURE) // transparency
940 {
942 maMetadata.mbIsAlpha = (cColType != 0 && cColType != 2);
943 }
944
945 // skip forward to next chunk
946 mrStream.SeekRel(4 + nLen32);
947 mrStream.ReadUInt32(nLen32);
948 mrStream.ReadUInt32(nTemp32);
949 }
950 } while (false);
951 }
952 return true;
953 }
954 return false;
955}
956
958{
961 {
963 return true;
964 }
965 return false;
966}
967
969{
970 if ((mnFirstLong == 0xffd8ffe0 && maFirstBytes[6] == 0x4a && maFirstBytes[7] == 0x46
971 && maFirstBytes[8] == 0x49 && maFirstBytes[9] == 0x46)
972 || (mnFirstLong == 0xffd8fffe) || (0xffd8ff00 == (mnFirstLong & 0xffffff00)))
973 {
975 return true;
976 }
977 return false;
978}
979
981{
982 sal_uInt32 n32 = 0;
983 bool bRet = false;
984 SeekGuard aGuard(mrStream, mrStream.Tell());
985 mrStream.SetEndian(SvStreamEndian::LITTLE);
986 mrStream.ReadUInt32(n32);
987 if (n32 == 0x44475653)
988 {
989 sal_uInt8 cByte = 0;
990 mrStream.ReadUChar(cByte);
991 if (cByte == 0x49)
992 {
994 bRet = true;
995
996 if (mbExtendedInfo)
997 {
998 sal_uInt32 nTemp32;
999 sal_uInt16 nTemp16;
1000
1001 mrStream.SeekRel(0x04);
1002
1003 // width
1004 nTemp32 = 0;
1005 mrStream.ReadUInt32(nTemp32);
1006 maMetadata.maLogSize.setWidth(nTemp32);
1007
1008 // height
1009 nTemp32 = 0;
1010 mrStream.ReadUInt32(nTemp32);
1012
1013 // read MapUnit and determine PrefSize
1014 nTemp16 = 0;
1015 mrStream.ReadUInt16(nTemp16);
1017 maMetadata.maLogSize, MapMode(static_cast<MapUnit>(nTemp16)),
1018 MapMode(MapUnit::Map100thMM));
1019 }
1020 }
1021 }
1022 else
1023 {
1024 mrStream.SeekRel(-4);
1025 n32 = 0;
1026 mrStream.ReadUInt32(n32);
1027
1028 if (n32 == 0x4D4C4356)
1029 {
1030 sal_uInt16 nTmp16 = 0;
1031
1032 mrStream.ReadUInt16(nTmp16);
1033
1034 if (nTmp16 == 0x4654)
1035 {
1037 bRet = true;
1038
1039 if (mbExtendedInfo)
1040 {
1041 MapMode aMapMode;
1042 mrStream.SeekRel(0x06);
1043 TypeSerializer aSerializer(mrStream);
1044 aSerializer.readMapMode(aMapMode);
1045 aSerializer.readSize(maMetadata.maLogSize);
1047 maMetadata.maLogSize, aMapMode, MapMode(MapUnit::Map100thMM));
1048 }
1049 }
1050 }
1051 }
1052 return bRet;
1053}
1054
1056{
1057 if (mnStreamLength < 2055)
1058 return false;
1059 char sBuffer[8];
1060 SeekGuard aGuard(mrStream, mnStreamPosition);
1062 sBuffer[mrStream.ReadBytes(sBuffer, 7)] = 0;
1063
1064 if (strncmp(sBuffer, "PCD_IPI", 7) == 0)
1065 {
1067 return true;
1068 }
1069 return false;
1070}
1071
1073{
1074 SeekGuard aGuard(mrStream, mnStreamPosition);
1075 bool bRet = false;
1076 if ((mnFirstLong == 0x38425053) && ((mnSecondLong >> 16) == 1))
1077 {
1079 bRet = true;
1080 if (mbExtendedInfo)
1081 {
1082 sal_uInt16 nChannels = 0;
1083 sal_uInt32 nRows = 0;
1084 sal_uInt32 nColumns = 0;
1085 sal_uInt16 nDepth = 0;
1086 sal_uInt16 nMode = 0;
1088 mrStream.SeekRel(6); // Pad
1089 mrStream.ReadUInt16(nChannels)
1090 .ReadUInt32(nRows)
1091 .ReadUInt32(nColumns)
1092 .ReadUInt16(nDepth)
1093 .ReadUInt16(nMode);
1094 if ((nDepth == 1) || (nDepth == 8) || (nDepth == 16))
1095 {
1096 maMetadata.mnBitsPerPixel = (nDepth == 16) ? 8 : nDepth;
1097 switch (nChannels)
1098 {
1099 case 4:
1100 case 3:
1102 [[fallthrough]];
1103 case 2:
1104 case 1:
1105 maMetadata.maPixSize.setWidth(nColumns);
1107 break;
1108 default:
1109 bRet = false;
1110 }
1111 }
1112 else
1113 bRet = false;
1114 }
1115 }
1116 return bRet;
1117}
1118
1120{
1121 const char* pFirstBytesAsCharArray = reinterpret_cast<char*>(maFirstBytes.data());
1122
1123 if (mnFirstLong == 0xC5D0D3C6)
1124 {
1126 return true;
1127 }
1128 else if (checkArrayForMatchingStrings(pFirstBytesAsCharArray, 30, { "%!PS-Adobe", " EPS" }))
1129 {
1131 return true;
1132 }
1133
1134 return false;
1135}
1136
1138{
1139 if (strncmp(reinterpret_cast<char*>(maFirstBytes.data()), "AutoCAD Binary DXF", 18) == 0)
1140 {
1142 return true;
1143 }
1144
1145 // ASCII DXF File Format
1146 int i = 0;
1147 while (i < 256 && maFirstBytes[i] <= 32)
1148 {
1149 ++i;
1150 }
1151
1152 if (i < 256 && maFirstBytes[i] == '0')
1153 {
1154 ++i;
1155
1156 // only now do we have sufficient data to make a judgement
1157 // based on a '0' + 'SECTION' == DXF argument
1158
1159 while (i < 256 && maFirstBytes[i] <= 32)
1160 {
1161 ++i;
1162 }
1163
1164 if (i + 7 < 256
1165 && (strncmp(reinterpret_cast<char*>(maFirstBytes.data() + i), "SECTION", 7) == 0))
1166 {
1168 return true;
1169 }
1170 }
1171 return false;
1172}
1173
1175{
1176 SeekGuard aGuard(mrStream, mnStreamPosition);
1178 {
1180 return true;
1181 }
1182 return false;
1183}
1184
1186{
1187 SeekGuard aGuard(mrStream, mrStream.Tell());
1188 sal_uInt8 nFirst = 0, nSecond = 0, nThird = 0;
1189 mrStream.ReadUChar(nFirst).ReadUChar(nSecond).ReadUChar(nThird);
1190 if (nFirst == 'P' && ((nSecond == '1') || (nSecond == '4')) && isspace(nThird))
1191 {
1193 return true;
1194 }
1195 return false;
1196}
1197
1199{
1200 sal_uInt8 nFirst = 0, nSecond = 0, nThird = 0;
1201 SeekGuard aGuard(mrStream, mrStream.Tell());
1202 mrStream.ReadUChar(nFirst).ReadUChar(nSecond).ReadUChar(nThird);
1203 if (nFirst == 'P' && ((nSecond == '2') || (nSecond == '5')) && isspace(nThird))
1204 {
1206 return true;
1207 }
1208 return false;
1209}
1210
1212{
1213 sal_uInt8 nFirst = 0, nSecond = 0, nThird = 0;
1214 SeekGuard aGuard(mrStream, mrStream.Tell());
1215 mrStream.ReadUChar(nFirst).ReadUChar(nSecond).ReadUChar(nThird);
1216 if (nFirst == 'P' && ((nSecond == '3') || (nSecond == '6')) && isspace(nThird))
1217 {
1219 return true;
1220 }
1221 return false;
1222}
1223
1225{
1226 if (mnFirstLong == 0x59a66a95)
1227 {
1229 return true;
1230 }
1231 return false;
1232}
1233
1235{
1236 const char* pFirstBytesAsCharArray = reinterpret_cast<char*>(maFirstBytes.data());
1237 if (matchArrayWithString(pFirstBytesAsCharArray, 256, "/* XPM */"))
1238 {
1240 return true;
1241 }
1242 return false;
1243}
1244
1246{
1247 sal_uInt64 nSize = std::min<sal_uInt64>(mnStreamLength, 2048);
1248 std::unique_ptr<sal_uInt8[]> pBuffer(new sal_uInt8[nSize]);
1249
1250 SeekGuard aGuard(mrStream, mnStreamPosition);
1252 nSize = mrStream.ReadBytes(pBuffer.get(), nSize);
1253
1254 const char* pBufferAsCharArray = reinterpret_cast<char*>(pBuffer.get());
1255
1256 if (checkArrayForMatchingStrings(pBufferAsCharArray, nSize, { "#define", "_width" }))
1257 {
1259 return true;
1260 }
1261 return false;
1262}
1263
1265{
1266 SeekGuard aGuard(mrStream, mnStreamPosition);
1267 sal_uInt64 nCheckSize = std::min<sal_uInt64>(mnStreamLength, 256);
1268 sal_uInt8 sExtendedOrDecompressedFirstBytes[SVG_CHECK_SIZE];
1269 sal_uInt64 nDecompressedSize = nCheckSize;
1270 // check if it is gzipped -> svgz
1271 sal_uInt8* pCheckArray = checkAndUncompressBuffer(sExtendedOrDecompressedFirstBytes,
1272 SVG_CHECK_SIZE, nDecompressedSize);
1273 nCheckSize = std::min<sal_uInt64>(nDecompressedSize, 256);
1274 bool bIsSvg(false);
1275 bool bIsGZip = mbWasCompressed;
1276 const char* pCheckArrayAsCharArray = reinterpret_cast<char*>(pCheckArray);
1277 // check for XML
1278 // #119176# SVG files which have no xml header at all have shown up this is optional
1279 // check for "xml" then "version" then "DOCTYPE" and "svg" tags
1280 if (checkArrayForMatchingStrings(pCheckArrayAsCharArray, nCheckSize,
1281 { "<?xml", "version", "DOCTYPE", "svg" }))
1282 {
1283 bIsSvg = true;
1284 }
1285
1286 // check for svg element in 1st 256 bytes
1287 // search for '<svg'
1288 if (!bIsSvg && checkArrayForMatchingStrings(pCheckArrayAsCharArray, nCheckSize, { "<svg" }))
1289 {
1290 bIsSvg = true;
1291 }
1292
1293 // extended search for svg element
1294 if (!bIsSvg)
1295 {
1296 // it's a xml, look for '<svg' in full file. Should not happen too
1297 // often since the tests above will handle most cases, but can happen
1298 // with Svg files containing big comment headers or Svg as the host
1299 // language
1300
1301 pCheckArrayAsCharArray = reinterpret_cast<char*>(sExtendedOrDecompressedFirstBytes);
1302
1303 if (bIsGZip)
1304 {
1305 nCheckSize = std::min<sal_uInt64>(nDecompressedSize, SVG_CHECK_SIZE);
1306 }
1307 else
1308 {
1309 nCheckSize = std::min<sal_uInt64>(mnStreamLength, SVG_CHECK_SIZE);
1311 nCheckSize = mrStream.ReadBytes(sExtendedOrDecompressedFirstBytes, nCheckSize);
1312 }
1313
1314 // search for '<svg'
1315 if (checkArrayForMatchingStrings(pCheckArrayAsCharArray, nCheckSize, { "<svg" }))
1316 {
1317 bIsSvg = true;
1318 }
1319 }
1320
1321 if (bIsSvg)
1322 {
1323 if (mbWasCompressed)
1325 else
1327 return true;
1328 }
1329 return false;
1330}
1331
1333{
1334 SeekGuard aGuard(mrStream, mnStreamPosition);
1335 // Check TGA ver.2 footer bytes
1336 if (mnStreamLength > 18)
1337 {
1338 char sFooterBytes[18];
1339
1341 mrStream.SeekRel(-18);
1342 if (mrStream.ReadBytes(sFooterBytes, 18) == 18
1343 && memcmp(sFooterBytes, "TRUEVISION-XFILE.", SAL_N_ELEMENTS(sFooterBytes)) == 0)
1344 {
1346 return true;
1347 }
1348 }
1349
1350 // Fallback to file extension check
1351 if (maExtension.startsWith("TGA"))
1352 {
1354 return true;
1355 }
1356 return false;
1357}
1358
1360{
1361 if ((maFirstBytes[4] == 'f' && maFirstBytes[5] == 't' && maFirstBytes[6] == 'y'
1362 && maFirstBytes[7] == 'p' && maFirstBytes[8] == 'q' && maFirstBytes[9] == 't')
1363 || (maFirstBytes[4] == 'm' && maFirstBytes[5] == 'o' && maFirstBytes[6] == 'o'
1364 && maFirstBytes[7] == 'v' && maFirstBytes[11] == 'l' && maFirstBytes[12] == 'm'))
1365 {
1367 return true;
1368 }
1369 return false;
1370}
1371
1373{
1374 if (maFirstBytes[0] == '%' && maFirstBytes[1] == 'P' && maFirstBytes[2] == 'D'
1375 && maFirstBytes[3] == 'F' && maFirstBytes[4] == '-')
1376 {
1378 return true;
1379 }
1380 return false;
1381}
1382
1384{
1385 if (maFirstBytes[0] == 'R' && maFirstBytes[1] == 'I' && maFirstBytes[2] == 'F'
1386 && maFirstBytes[3] == 'F' && maFirstBytes[8] == 'W' && maFirstBytes[9] == 'E'
1387 && maFirstBytes[10] == 'B' && maFirstBytes[11] == 'P')
1388 {
1390 if (mbExtendedInfo)
1391 {
1396 }
1397 return true;
1398 }
1399 return false;
1400}
1401
1403
1405 sal_uInt32 nSize, sal_uInt64& nRetSize)
1406{
1407 SeekGuard aGuard(mrStream, mnStreamPosition);
1409 {
1410 ZCodec aCodec;
1412 aCodec.BeginCompression(ZCODEC_DEFAULT_COMPRESSION, /*gzLib*/ true);
1413 auto nDecompressedOut = aCodec.Read(mrStream, aUncompressedBuffer, nSize);
1414 // ZCodec::Decompress returns -1 on failure
1415 nRetSize = nDecompressedOut < 0 ? 0 : nDecompressedOut;
1416 aCodec.EndCompression();
1417 // Recalculate first/second long
1418 for (int i = 0; i < 4; ++i)
1419 {
1420 mnFirstLong = (mnFirstLong << 8) | sal_uInt32(aUncompressedBuffer[i]);
1421 mnSecondLong = (mnSecondLong << 8) | sal_uInt32(aUncompressedBuffer[i + 4]);
1422 }
1423 mbWasCompressed = true;
1424 return aUncompressedBuffer;
1425 }
1426 mbWasCompressed = false;
1427 return maFirstBytes.data();
1428}
1429
1430} // vcl namespace
1431
1432/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
constexpr sal_uInt32 WMF_EMF_CHECK_SIZE
constexpr sal_uInt32 SVG_CHECK_SIZE
constexpr sal_uInt32 DATA_SIZE
constexpr sal_uInt32 PNG_TRNS_SIGNATURE
constexpr sal_uInt64 PNG_SIGNATURE
constexpr sal_uInt32 PNG_IDAT_SIGNATURE
constexpr int PNG_SIGNATURE_SIZE
constexpr sal_uInt32 PNG_PHYS_SIGNATURE
SAL_WARN_UNUSED_RESULT Point LogicToLogic(const Point &rPtSource, const MapMode *pMapModeSource, const MapMode *pMapModeDest) const
Definition: map.cxx:1580
constexpr tools::Long getHeight() const
constexpr tools::Long Height() const
constexpr tools::Long getWidth() const
void setWidth(tools::Long nWidth)
void setHeight(tools::Long nHeight)
constexpr tools::Long Width() const
void SetSynchronMode(bool bTheSync=true)
sal_uInt64 Tell() const
void SetEndian(SvStreamEndian SvStreamEndian)
bool good() const
SvStream & ReadInt16(sal_Int16 &rInt16)
SvStream & ReadUInt32(sal_uInt32 &rUInt32)
SvStreamEndian GetEndian() const
SvLockBytes * GetLockBytes() const
sal_uInt64 Seek(sal_uInt64 nPos)
std::size_t ReadBytes(void *pData, std::size_t nSize)
sal_uInt64 SeekRel(sal_Int64 nPos)
ErrCode GetError() const
SvStream & ReadUInt16(sal_uInt16 &rUInt16)
sal_uInt64 remainingSize()
SvStream & ReadUChar(unsigned char &rChar)
void readMapMode(MapMode &rMapMode)
tools::Long Read(SvStream &rIStm, sal_uInt8 *pData, sal_uInt32 nSize)
static bool IsZCompressed(SvStream &rIStm)
tools::Long EndCompression()
void BeginCompression(int nCompressLevel=ZCODEC_DEFAULT_COMPRESSION, bool gzLib=false)
void readSize(Size &rSize)
const GraphicMetadata & getMetadata()
sal_uInt8 * checkAndUncompressBuffer(sal_uInt8 *aUncompressedBuffer, sal_uInt32 nSize, sal_uInt64 &nDecompressedSize)
Checks whether mrStream needs to be uncompressed and returns a pointer to the to aUncompressedBuffer ...
std::vector< sal_uInt8 > maFirstBytes
GraphicFormatDetector(SvStream &rStream, OUString aFormatExtension, bool bExtendedInfo=false)
static bool isAPng(SvStream &rStream)
int nCount
#define SAL_N_ELEMENTS(arr)
MapUnit
int i
bool peekGraphicFormat(SvStream &rStream, OUString &rFormatExtension, bool bTest)
static OUString getImportFormatShortName(GraphicFileFormat nFormat)
bool checkArrayForMatchingStrings(const char *pSource, sal_Int32 nSourceSize, std::vector< OString > const &rStrings)
const char * matchArrayWithString(const char *pSource, sal_Int32 nSourceSize, OString const &rString)
HashMap_OWString_Interface aMap
const sal_uInt16 nMagic
bool ReadWebpInfo(SvStream &stream, Size &pixelSize, sal_uInt16 &bitsPerPixel, bool &hasAlpha)
Definition: reader.cxx:306
sal_uIntPtr sal_uLong
#define STREAM_SEEK_TO_END
SvStreamEndian
std::optional< Size > maPreferredLogSize
GraphicFileFormat mnFormat
sal_uInt16 mnBitsPerPixel
std::optional< MapMode > maPreferredMapMode
unsigned char sal_uInt8
#define ZCODEC_DEFAULT_COMPRESSION