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