xref: /core/vcl/source/filter/graphicfilter2.cxx (revision 4ec93b7c)
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 <string.h>
21 #include <tools/stream.hxx>
22 #include <tools/fract.hxx>
23 #include <tools/urlobj.hxx>
24 #include <TypeSerializer.hxx>
25 #include <vcl/outdev.hxx>
26 #include <vcl/graphicfilter.hxx>
27 #include <unotools/ucbstreamhelper.hxx>
28 #include "graphicfilter_internal.hxx"
29 
30 #define DATA_SIZE           640
31 
32 GraphicDescriptor::GraphicDescriptor( const INetURLObject& rPath ) :
33     pFileStm( ::utl::UcbStreamHelper::CreateStream( rPath.GetMainURL( INetURLObject::DecodeMechanism::NONE ), StreamMode::READ ).release() ),
34     aPathExt( rPath.GetFileExtension().toAsciiLowerCase() ),
35     bOwnStream( true )
36 {
37     ImpConstruct();
38 }
39 
40 GraphicDescriptor::GraphicDescriptor( SvStream& rInStream, const OUString* pPath) :
41     pFileStm    ( &rInStream ),
42     bOwnStream  ( false )
43 {
44     ImpConstruct();
45 
46     if ( pPath )
47     {
48         INetURLObject aURL( *pPath );
49         aPathExt = aURL.GetFileExtension().toAsciiLowerCase();
50     }
51 }
52 
53 GraphicDescriptor::~GraphicDescriptor()
54 {
55     if ( bOwnStream )
56         delete pFileStm;
57 }
58 
59 bool GraphicDescriptor::Detect( bool bExtendedInfo )
60 {
61     bool bRet = false;
62     if ( pFileStm && !pFileStm->GetError() )
63     {
64         SvStream&      rStm = *pFileStm;
65         SvStreamEndian nOldFormat = rStm.GetEndian();
66 
67         if      ( ImpDetectGIF( rStm, bExtendedInfo ) ) bRet = true;
68         else if ( ImpDetectJPG( rStm, bExtendedInfo ) ) bRet = true;
69         else if ( ImpDetectBMP( rStm, bExtendedInfo ) ) bRet = true;
70         else if ( ImpDetectPNG( rStm, bExtendedInfo ) ) bRet = true;
71         else if ( ImpDetectTIF( rStm, bExtendedInfo ) ) bRet = true;
72         else if ( ImpDetectPCX( rStm ) ) bRet = true;
73         else if ( ImpDetectDXF( rStm, bExtendedInfo ) ) bRet = true;
74         else if ( ImpDetectMET( rStm, bExtendedInfo ) ) bRet = true;
75         else if ( ImpDetectSVM( rStm, bExtendedInfo ) ) bRet = true;
76         else if ( ImpDetectWMF( rStm, bExtendedInfo ) ) bRet = true;
77         else if ( ImpDetectEMF( rStm, bExtendedInfo ) ) bRet = true;
78         else if ( ImpDetectSVG( rStm, bExtendedInfo ) ) bRet = true;
79         else if ( ImpDetectPCT( rStm, bExtendedInfo ) ) bRet = true;
80         else if ( ImpDetectXBM( rStm, bExtendedInfo ) ) bRet = true;
81         else if ( ImpDetectXPM( rStm, bExtendedInfo ) ) bRet = true;
82         else if ( ImpDetectPBM( rStm, bExtendedInfo ) ) bRet = true;
83         else if ( ImpDetectPGM( rStm, bExtendedInfo ) ) bRet = true;
84         else if ( ImpDetectPPM( rStm, bExtendedInfo ) ) bRet = true;
85         else if ( ImpDetectRAS( rStm, bExtendedInfo ) ) bRet = true;
86         else if ( ImpDetectTGA( rStm, bExtendedInfo ) ) bRet = true;
87         else if ( ImpDetectPSD( rStm, bExtendedInfo ) ) bRet = true;
88         else if ( ImpDetectEPS( rStm, bExtendedInfo ) ) bRet = true;
89         else if ( ImpDetectPCD( rStm, bExtendedInfo ) ) bRet = true;
90 
91         rStm.SetEndian( nOldFormat );
92     }
93     return bRet;
94 }
95 
96 void GraphicDescriptor::ImpConstruct()
97 {
98     nFormat = GraphicFileFormat::NOT;
99     nBitsPerPixel = 0;
100     nPlanes = 0;
101     mnNumberOfImageComponents = 0;
102     bIsTransparent = false;
103     bIsAlpha = false;
104 }
105 
106 bool GraphicDescriptor::ImpDetectBMP( SvStream& rStm, bool bExtendedInfo )
107 {
108     sal_uInt16  nTemp16 = 0;
109     bool    bRet = false;
110     sal_Int32 nStmPos = rStm.Tell();
111 
112     rStm.SetEndian( SvStreamEndian::LITTLE );
113     rStm.ReadUInt16( nTemp16 );
114 
115     // OS/2-BitmapArray
116     if ( nTemp16 == 0x4142 )
117     {
118         rStm.SeekRel( 0x0c );
119         rStm.ReadUInt16( nTemp16 );
120     }
121 
122     // Bitmap
123     if ( nTemp16 == 0x4d42 )
124     {
125         nFormat = GraphicFileFormat::BMP;
126         bRet = true;
127 
128         if ( bExtendedInfo )
129         {
130             sal_uInt32  nTemp32;
131             sal_uInt32  nCompression;
132 
133             // up to first info
134             rStm.SeekRel( 0x10 );
135 
136             // Pixel width
137             rStm.ReadUInt32( nTemp32 );
138             aPixSize.setWidth( nTemp32 );
139 
140             // Pixel height
141             rStm.ReadUInt32( nTemp32 );
142             aPixSize.setHeight( nTemp32 );
143 
144             // Planes
145             rStm.ReadUInt16( nTemp16 );
146             nPlanes = nTemp16;
147 
148             // BitCount
149             rStm.ReadUInt16( nTemp16 );
150             nBitsPerPixel = nTemp16;
151 
152             // Compression
153             rStm.ReadUInt32( nTemp32 );
154             nCompression = nTemp32;
155 
156             // logical width
157             rStm.SeekRel( 4 );
158             rStm.ReadUInt32( nTemp32 );
159             if ( nTemp32 )
160                 aLogSize.setWidth( ( aPixSize.Width() * 100000 ) / nTemp32 );
161 
162             // logical height
163             rStm.ReadUInt32( nTemp32 );
164             if ( nTemp32 )
165                 aLogSize.setHeight( ( aPixSize.Height() * 100000 ) / nTemp32 );
166 
167             // further validation, check for rational values
168             if ( ( nBitsPerPixel > 24 ) || ( nCompression > 3 ) )
169             {
170                 nFormat = GraphicFileFormat::NOT;
171                 bRet = false;
172             }
173         }
174     }
175     rStm.Seek( nStmPos );
176     return bRet;
177 }
178 
179 bool GraphicDescriptor::ImpDetectGIF( SvStream& rStm, bool bExtendedInfo )
180 {
181     sal_uInt32  n32 = 0;
182     sal_uInt16  n16 = 0;
183     bool    bRet = false;
184     sal_uInt8   cByte = 0;
185 
186     sal_Int32 nStmPos = rStm.Tell();
187     rStm.SetEndian( SvStreamEndian::LITTLE );
188     rStm.ReadUInt32( n32 );
189 
190     if ( n32 == 0x38464947 )
191     {
192         rStm.ReadUInt16( n16 );
193         if ( ( n16 == 0x6137 ) || ( n16 == 0x6139 ) )
194         {
195             nFormat = GraphicFileFormat::GIF;
196             bRet = true;
197 
198             if ( bExtendedInfo )
199             {
200                 sal_uInt16 nTemp16 = 0;
201 
202                 // Pixel width
203                 rStm.ReadUInt16( nTemp16 );
204                 aPixSize.setWidth( nTemp16 );
205 
206                 // Pixel height
207                 rStm.ReadUInt16( nTemp16 );
208                 aPixSize.setHeight( nTemp16 );
209 
210                 // Bits/Pixel
211                 rStm.ReadUChar( cByte );
212                 nBitsPerPixel = ( ( cByte & 112 ) >> 4 ) + 1;
213             }
214         }
215     }
216     rStm.Seek( nStmPos );
217     return bRet;
218 }
219 
220 // returns the next jpeg marker, a return value of 0 represents an error
221 static sal_uInt8 ImpDetectJPG_GetNextMarker( SvStream& rStm )
222 {
223     sal_uInt8 nByte;
224     do
225     {
226         do
227         {
228             rStm.ReadUChar( nByte );
229             if (!rStm.good())   // as 0 is not allowed as marker,
230                 return 0;       // we can use it as errorcode
231         }
232         while ( nByte != 0xff );
233         do
234         {
235             rStm.ReadUChar( nByte );
236             if (!rStm.good())
237                 return 0;
238         }
239         while( nByte == 0xff );
240     }
241     while( nByte == 0 );        // 0xff00 represents 0xff and not a marker,
242                                 // the marker detection has to be restarted.
243     return nByte;
244 }
245 
246 bool GraphicDescriptor::ImpDetectJPG( SvStream& rStm,  bool bExtendedInfo )
247 {
248     sal_uInt32  nTemp32 = 0;
249     bool    bRet = false;
250 
251     sal_Int32 nStmPos = rStm.Tell();
252 
253     rStm.SetEndian( SvStreamEndian::BIG );
254     rStm.ReadUInt32( nTemp32 );
255 
256     // compare upper 24 bits
257     if( 0xffd8ff00 == ( nTemp32 & 0xffffff00 ) )
258     {
259         nFormat = GraphicFileFormat::JPG;
260         bRet = true;
261 
262         if ( bExtendedInfo )
263         {
264             rStm.SeekRel( -2 );
265 
266             ErrCode nError( rStm.GetError() );
267 
268             bool bScanFailure = false;
269             bool bScanFinished = false;
270             MapMode aMap;
271 
272             while (!bScanFailure && !bScanFinished && rStm.good())
273             {
274                 sal_uInt8 nMarker = ImpDetectJPG_GetNextMarker( rStm );
275                 switch( nMarker )
276                 {
277                     // fixed size marker, not having a two byte length parameter
278                     case 0xd0 :     // RST0
279                     case 0xd1 :
280                     case 0xd2 :
281                     case 0xd3 :
282                     case 0xd4 :
283                     case 0xd5 :
284                     case 0xd6 :
285                     case 0xd7 :     // RST7
286                     case 0x01 :     // TEM
287                     break;
288 
289                     case 0xd8 :     // SOI (has already been checked, there should not be a second one)
290                     case 0x00 :     // marker is invalid, we should stop now
291                         bScanFailure = true;
292                     break;
293 
294                     case 0xd9 :     // EOI
295                         bScanFinished = true;
296                     break;
297 
298                     // per default we assume marker segments containing a length parameter
299                     default :
300                     {
301                         sal_uInt16 nLength = 0;
302                         rStm.ReadUInt16( nLength );
303 
304                         if ( nLength < 2 )
305                             bScanFailure = true;
306                         else
307                         {
308                             sal_uInt32 nNextMarkerPos = rStm.Tell() + nLength - 2;
309                             switch( nMarker )
310                             {
311                                 case 0xe0 : // APP0 Marker
312                                 {
313                                     if ( nLength == 16 )
314                                     {
315                                         sal_Int32 nIdentifier = 0;
316                                         rStm.ReadInt32( nIdentifier );
317                                         if ( nIdentifier == 0x4a464946 )    // JFIF Identifier
318                                         {
319                                             sal_uInt8   nStringTerminator = 0;
320                                             sal_uInt8   nMajorRevision = 0;
321                                             sal_uInt8   nMinorRevision = 0;
322                                             sal_uInt8   nUnits = 0;
323                                             sal_uInt16  nHorizontalResolution = 0;
324                                             sal_uInt16  nVerticalResolution = 0;
325                                             sal_uInt8   nHorzThumbnailPixelCount = 0;
326                                             sal_uInt8   nVertThumbnailPixelCount = 0;
327 
328                                             rStm.ReadUChar( nStringTerminator )
329                                                 .ReadUChar( nMajorRevision )
330                                                 .ReadUChar( nMinorRevision )
331                                                 .ReadUChar( nUnits )
332                                                 .ReadUInt16( nHorizontalResolution )
333                                                 .ReadUInt16( nVerticalResolution )
334                                                 .ReadUChar( nHorzThumbnailPixelCount )
335                                                 .ReadUChar( nVertThumbnailPixelCount );
336 
337                                             // setting the logical size
338                                             if ( nUnits && nHorizontalResolution && nVerticalResolution )
339                                             {
340                                                 aMap.SetMapUnit( nUnits == 1 ? MapUnit::MapInch : MapUnit::MapCM );
341                                                 aMap.SetScaleX( Fraction( 1, nHorizontalResolution ) );
342                                                 aMap.SetScaleY( Fraction( 1, nVerticalResolution ) );
343                                                 aLogSize = OutputDevice::LogicToLogic( aPixSize, aMap, MapMode( MapUnit::Map100thMM ) );
344                                             }
345                                         }
346                                     }
347                                 }
348                                 break;
349 
350                                 // Start of Frame Markers
351                                 case 0xc0 : // SOF0
352                                 case 0xc1 : // SOF1
353                                 case 0xc2 : // SOF2
354                                 case 0xc3 : // SOF3
355                                 case 0xc5 : // SOF5
356                                 case 0xc6 : // SOF6
357                                 case 0xc7 : // SOF7
358                                 case 0xc9 : // SOF9
359                                 case 0xca : // SOF10
360                                 case 0xcb : // SOF11
361                                 case 0xcd : // SOF13
362                                 case 0xce : // SOF14
363                                 case 0xcf : // SOF15
364                                 {
365                                     sal_uInt8   nSamplePrecision = 0;
366                                     sal_uInt16  nNumberOfLines = 0;
367                                     sal_uInt16  nSamplesPerLine = 0;
368                                     sal_uInt8   nNumberOfImageComponents = 0;
369                                     sal_uInt8   nComponentsIdentifier = 0;
370                                     sal_uInt8   nSamplingFactor = 0;
371                                     sal_uInt8   nQuantizationTableDestinationSelector = 0;
372                                     rStm.ReadUChar( nSamplePrecision )
373                                         .ReadUInt16( nNumberOfLines )
374                                         .ReadUInt16( nSamplesPerLine )
375                                         .ReadUChar( nNumberOfImageComponents )
376                                         .ReadUChar( nComponentsIdentifier )
377                                         .ReadUChar( nSamplingFactor )
378                                         .ReadUChar( nQuantizationTableDestinationSelector );
379                                     mnNumberOfImageComponents = nNumberOfImageComponents;
380 
381                                     // nSamplingFactor (lower nibble: vertical,
382                                     // upper nibble: horizontal) is unused
383 
384                                     aPixSize.setHeight( nNumberOfLines );
385                                     aPixSize.setWidth( nSamplesPerLine );
386                                     nBitsPerPixel = ( nNumberOfImageComponents == 3 ? 24 : nNumberOfImageComponents == 1 ? 8 : 0 );
387                                     nPlanes = 1;
388 
389                                     if (aMap.GetMapUnit() != MapUnit::MapPixel)
390                                         // We already know the DPI, but the
391                                         // pixel size arrived later, so do the
392                                         // conversion again.
393                                         aLogSize = OutputDevice::LogicToLogic(
394                                             aPixSize, aMap, MapMode(MapUnit::Map100thMM));
395 
396                                     bScanFinished = true;
397                                 }
398                                 break;
399                             }
400                             rStm.Seek( nNextMarkerPos );
401                         }
402                     }
403                     break;
404                 }
405             }
406             rStm.SetError( nError );
407         }
408     }
409     rStm.Seek( nStmPos );
410     return bRet;
411 }
412 
413 bool GraphicDescriptor::ImpDetectPCD( SvStream& rStm, bool )
414 {
415     bool    bRet = false;
416 
417     sal_Int32 nStmPos = rStm.Tell();
418     rStm.SetEndian( SvStreamEndian::LITTLE );
419 
420     sal_uInt32  nTemp32 = 0;
421     sal_uInt16  nTemp16 = 0;
422     sal_uInt8   cByte = 0;
423 
424     rStm.SeekRel( 2048 );
425     rStm.ReadUInt32( nTemp32 );
426     rStm.ReadUInt16( nTemp16 );
427     rStm.ReadUChar( cByte );
428 
429     if ( ( nTemp32 == 0x5f444350 ) &&
430          ( nTemp16 == 0x5049 ) &&
431          ( cByte == 0x49 ) )
432     {
433         nFormat = GraphicFileFormat::PCD;
434         bRet = true;
435     }
436     rStm.Seek( nStmPos );
437     return bRet;
438 }
439 
440 bool GraphicDescriptor::ImpDetectPCX( SvStream& rStm )
441 {
442     // ! Because 0x0a can be interpreted as LF too ...
443     // we can't be sure that this special sign represent a PCX file only.
444     // Every Ascii file is possible here :-(
445     // We must detect the whole header.
446 
447     bool    bRet = false;
448     sal_uInt8   cByte = 0;
449 
450     sal_Int32 nStmPos = rStm.Tell();
451     rStm.SetEndian( SvStreamEndian::LITTLE );
452     rStm.ReadUChar( cByte );
453 
454     if ( cByte == 0x0a )
455     {
456         nFormat = GraphicFileFormat::PCX;
457 
458         sal_uInt16  nTemp16;
459         sal_uInt16  nXmin;
460         sal_uInt16  nXmax;
461         sal_uInt16  nYmin;
462         sal_uInt16  nYmax;
463         sal_uInt16  nDPIx;
464         sal_uInt16  nDPIy;
465 
466         rStm.SeekRel( 1 );
467 
468         // compression
469         rStm.ReadUChar( cByte );
470 
471         bRet = (cByte==0 || cByte ==1);
472         if (bRet)
473         {
474             // Bits/Pixel
475             rStm.ReadUChar( cByte );
476             nBitsPerPixel = cByte;
477 
478             // image dimensions
479             rStm.ReadUInt16( nTemp16 );
480             nXmin = nTemp16;
481             rStm.ReadUInt16( nTemp16 );
482             nYmin = nTemp16;
483             rStm.ReadUInt16( nTemp16 );
484             nXmax = nTemp16;
485             rStm.ReadUInt16( nTemp16 );
486             nYmax = nTemp16;
487 
488             aPixSize.setWidth( nXmax - nXmin + 1 );
489             aPixSize.setHeight( nYmax - nYmin + 1 );
490 
491             // resolution
492             rStm.ReadUInt16( nTemp16 );
493             nDPIx = nTemp16;
494             rStm.ReadUInt16( nTemp16 );
495             nDPIy = nTemp16;
496 
497             // set logical size
498             MapMode aMap( MapUnit::MapInch, Point(),
499                           Fraction( 1, nDPIx ), Fraction( 1, nDPIy ) );
500             aLogSize = OutputDevice::LogicToLogic( aPixSize, aMap,
501                                                    MapMode( MapUnit::Map100thMM ) );
502 
503             // number of color planes
504             cByte = 5; // Illegal value in case of EOF.
505             rStm.SeekRel( 49 );
506             rStm.ReadUChar( cByte );
507             nPlanes = cByte;
508 
509             bRet = (nPlanes<=4);
510         }
511     }
512 
513     rStm.Seek( nStmPos );
514     return bRet;
515 }
516 
517 bool GraphicDescriptor::ImpDetectPNG( SvStream& rStm, bool bExtendedInfo )
518 {
519     sal_uInt32  nTemp32 = 0;
520     bool    bRet = false;
521 
522     sal_Int32 nStmPos = rStm.Tell();
523     rStm.SetEndian( SvStreamEndian::BIG );
524     rStm.ReadUInt32( nTemp32 );
525 
526     if ( nTemp32 == 0x89504e47 )
527     {
528         rStm.ReadUInt32( nTemp32 );
529         if ( nTemp32 == 0x0d0a1a0a )
530         {
531             nFormat = GraphicFileFormat::PNG;
532             bRet = true;
533 
534             if ( bExtendedInfo )
535             {
536                 do {
537                     sal_uInt8 cByte = 0;
538 
539                     // IHDR-Chunk
540                     rStm.SeekRel( 8 );
541 
542                     // width
543                     rStm.ReadUInt32( nTemp32 );
544                     if (!rStm.good())
545                         break;
546                     aPixSize.setWidth( nTemp32 );
547 
548                     // height
549                     rStm.ReadUInt32( nTemp32 );
550                     if (!rStm.good())
551                         break;
552                     aPixSize.setHeight( nTemp32 );
553 
554                     // Bits/Pixel
555                     rStm.ReadUChar( cByte );
556                     if (!rStm.good())
557                         break;
558                     nBitsPerPixel = cByte;
559 
560                     // Colour type - check whether it supports alpha values
561                     sal_uInt8 cColType = 0;
562                     rStm.ReadUChar( cColType );
563                     if (!rStm.good())
564                          break;
565                     bIsAlpha = bIsTransparent = ( cColType == 4 || cColType == 6 );
566 
567                     // Planes always 1;
568                     // compression always
569                     nPlanes = 1;
570 
571                     sal_uInt32  nLen32 = 0;
572                     nTemp32 = 0;
573 
574                     rStm.SeekRel( 7 );
575 
576                     // read up to the start of the image
577                     rStm.ReadUInt32( nLen32 );
578                     rStm.ReadUInt32( nTemp32 );
579                     while( ( nTemp32 != 0x49444154 ) && rStm.good() )
580                     {
581                         if ( nTemp32 == 0x70485973 ) // physical pixel dimensions
582                         {
583                             sal_uLong   nXRes;
584                             sal_uLong   nYRes;
585 
586                             // horizontal resolution
587                             nTemp32 = 0;
588                             rStm.ReadUInt32( nTemp32 );
589                             nXRes = nTemp32;
590 
591                             // vertical resolution
592                             nTemp32 = 0;
593                             rStm.ReadUInt32( nTemp32 );
594                             nYRes = nTemp32;
595 
596                             // unit
597                             cByte = 0;
598                             rStm.ReadUChar( cByte );
599 
600                             if ( cByte )
601                             {
602                                 if ( nXRes )
603                                     aLogSize.setWidth( (aPixSize.Width() * 100000) / nXRes );
604 
605                                 if ( nYRes )
606                                     aLogSize.setHeight( (aPixSize.Height() * 100000) / nYRes );
607                             }
608 
609                             nLen32 -= 9;
610                         }
611                         else if ( nTemp32 == 0x74524e53 ) // transparency
612                         {
613                             bIsTransparent = true;
614                             bIsAlpha = ( cColType != 0 && cColType != 2 );
615                         }
616 
617                         // skip forward to next chunk
618                         rStm.SeekRel( 4 + nLen32 );
619                         rStm.ReadUInt32( nLen32 );
620                         rStm.ReadUInt32( nTemp32 );
621                     }
622                 } while (false);
623             }
624         }
625     }
626     rStm.Seek( nStmPos );
627     return bRet;
628 }
629 
630 bool GraphicDescriptor::ImpDetectTIF( SvStream& rStm, bool bExtendedInfo )
631 {
632     bool    bRet = false;
633     sal_uInt8   cByte1 = 0;
634     sal_uInt8   cByte2 = 1;
635 
636     sal_Int32 nStmPos = rStm.Tell();
637     rStm.ReadUChar( cByte1 );
638     rStm.ReadUChar( cByte2 );
639     if ( cByte1 == cByte2 )
640     {
641         bool bDetectOk = false;
642 
643         if ( cByte1 == 0x49 )
644         {
645             rStm.SetEndian( SvStreamEndian::LITTLE );
646             bDetectOk = true;
647         }
648         else if ( cByte1 == 0x4d )
649         {
650             rStm.SetEndian( SvStreamEndian::BIG );
651             bDetectOk = true;
652         }
653 
654         if ( bDetectOk )
655         {
656             sal_uInt16  nTemp16 = 0;
657 
658             rStm.ReadUInt16( nTemp16 );
659             if ( nTemp16 == 0x2a )
660             {
661                 nFormat = GraphicFileFormat::TIF;
662                 bRet = true;
663 
664                 if ( bExtendedInfo )
665                 {
666                     sal_uLong   nCount;
667                     sal_uLong   nMax = DATA_SIZE - 48;
668                     sal_uInt32  nTemp32 = 0;
669 
670                     // Offset of the first IFD
671                     rStm.ReadUInt32( nTemp32 );
672                     nCount = nTemp32 + 2;
673                     rStm.SeekRel( nCount - 0x08 );
674 
675                     if ( nCount < nMax )
676                     {
677                         bool bOk = false;
678 
679                         // read tags till we find Tag256 ( Width )
680                         // do not read more bytes than DATA_SIZE
681                         rStm.ReadUInt16( nTemp16 );
682                         while ( nTemp16 != 256 )
683                         {
684                             bOk = nCount < nMax;
685                             if ( !bOk )
686                             {
687                                 break;
688                             }
689                             rStm.SeekRel( 10 );
690                             rStm.ReadUInt16( nTemp16 );
691                             nCount += 12;
692                         }
693 
694                         if ( bOk )
695                         {
696                             // width
697                             rStm.ReadUInt16( nTemp16 );
698                             rStm.SeekRel( 4 );
699                             if ( nTemp16 == 3 )
700                             {
701                                 rStm.ReadUInt16( nTemp16 );
702                                 aPixSize.setWidth( nTemp16 );
703                                 rStm.SeekRel( 2 );
704                             }
705                             else
706                             {
707                                 rStm.ReadUInt32( nTemp32 );
708                                 aPixSize.setWidth( nTemp32 );
709                             }
710 
711                             // height
712                             rStm.SeekRel( 2 );
713                             rStm.ReadUInt16( nTemp16 );
714                             rStm.SeekRel( 4 );
715                             if ( nTemp16 == 3 )
716                             {
717                                 rStm.ReadUInt16( nTemp16 );
718                                 aPixSize.setHeight( nTemp16 );
719                                 rStm.SeekRel( 2 );
720                             }
721                             else
722                             {
723                                 rStm.ReadUInt32( nTemp32 );
724                                 aPixSize.setHeight( nTemp32 );
725                             }
726 
727                             // Bits/Pixel
728                             rStm.ReadUInt16( nTemp16 );
729                             if ( nTemp16 == 258 )
730                             {
731                                 rStm.SeekRel( 6 );
732                                 rStm.ReadUInt16( nTemp16 );
733                                 nBitsPerPixel = nTemp16;
734                                 rStm.SeekRel( 2 );
735                             }
736                             else
737                                 rStm.SeekRel( -2 );
738 
739                             // compression
740                             rStm.ReadUInt16( nTemp16 );
741                             if ( nTemp16 == 259 )
742                             {
743                                 rStm.SeekRel( 6 );
744                                 rStm.ReadUInt16( nTemp16 ); // compression
745                                 rStm.SeekRel( 2 );
746                             }
747                             else
748                                 rStm.SeekRel( -2 );
749                         }
750                     }
751                 }
752             }
753         }
754     }
755     rStm.Seek( nStmPos );
756     return bRet;
757 }
758 
759 bool GraphicDescriptor::ImpDetectXBM( SvStream&, bool )
760 {
761     bool bRet = aPathExt.startsWith( "xbm" );
762     if (bRet)
763         nFormat = GraphicFileFormat::XBM;
764 
765     return bRet;
766 }
767 
768 bool GraphicDescriptor::ImpDetectXPM( SvStream&, bool )
769 {
770     bool bRet = aPathExt.startsWith( "xpm" );
771     if (bRet)
772         nFormat = GraphicFileFormat::XPM;
773 
774     return bRet;
775 }
776 
777 bool GraphicDescriptor::ImpDetectPBM( SvStream& rStm, bool )
778 {
779     bool bRet = false;
780 
781     // check file extension first, as this trumps the 2 ID bytes
782     if ( aPathExt.startsWith( "pbm" ) )
783         bRet = true;
784     else
785     {
786         sal_Int32 nStmPos = rStm.Tell();
787         sal_uInt8   nFirst = 0, nSecond = 0;
788         rStm.ReadUChar( nFirst ).ReadUChar( nSecond );
789         if ( nFirst == 'P' && ( ( nSecond == '1' ) || ( nSecond == '4' ) ) )
790             bRet = true;
791         rStm.Seek( nStmPos );
792     }
793 
794     if ( bRet )
795         nFormat = GraphicFileFormat::PBM;
796 
797     return bRet;
798 }
799 
800 bool GraphicDescriptor::ImpDetectPGM( SvStream& rStm, bool )
801 {
802     bool bRet = false;
803 
804     if ( aPathExt.startsWith( "pgm" ) )
805         bRet = true;
806     else
807     {
808         sal_uInt8 nFirst = 0, nSecond = 0;
809         sal_Int32 nStmPos = rStm.Tell();
810         rStm.ReadUChar( nFirst ).ReadUChar( nSecond );
811         if ( nFirst == 'P' && ( ( nSecond == '2' ) || ( nSecond == '5' ) ) )
812             bRet = true;
813         rStm.Seek( nStmPos );
814     }
815 
816     if ( bRet )
817         nFormat = GraphicFileFormat::PGM;
818 
819     return bRet;
820 }
821 
822 bool GraphicDescriptor::ImpDetectPPM( SvStream& rStm, bool )
823 {
824     bool bRet = false;
825 
826     if ( aPathExt.startsWith( "ppm" ) )
827         bRet = true;
828     else
829     {
830         sal_uInt8   nFirst = 0, nSecond = 0;
831         sal_Int32 nStmPos = rStm.Tell();
832         rStm.ReadUChar( nFirst ).ReadUChar( nSecond );
833         if ( nFirst == 'P' && ( ( nSecond == '3' ) || ( nSecond == '6' ) ) )
834             bRet = true;
835         rStm.Seek( nStmPos );
836     }
837 
838     if ( bRet )
839         nFormat = GraphicFileFormat::PPM;
840 
841     return bRet;
842 }
843 
844 bool GraphicDescriptor::ImpDetectRAS( SvStream& rStm, bool )
845 {
846     sal_uInt32 nMagicNumber = 0;
847     bool bRet = false;
848     sal_Int32 nStmPos = rStm.Tell();
849     rStm.SetEndian( SvStreamEndian::BIG );
850     rStm.ReadUInt32( nMagicNumber );
851     if ( nMagicNumber == 0x59a66a95 )
852     {
853         nFormat = GraphicFileFormat::RAS;
854         bRet = true;
855     }
856     rStm.Seek( nStmPos );
857     return bRet;
858 }
859 
860 bool GraphicDescriptor::ImpDetectTGA( SvStream&, bool )
861 {
862     bool bRet = aPathExt.startsWith( "tga" );
863     if (bRet)
864         nFormat = GraphicFileFormat::TGA;
865 
866     return bRet;
867 }
868 
869 bool GraphicDescriptor::ImpDetectPSD( SvStream& rStm, bool bExtendedInfo )
870 {
871     bool bRet = false;
872 
873     sal_uInt32  nMagicNumber = 0;
874     sal_Int32 nStmPos = rStm.Tell();
875     rStm.SetEndian( SvStreamEndian::BIG );
876     rStm.ReadUInt32( nMagicNumber );
877     if ( nMagicNumber == 0x38425053 )
878     {
879         sal_uInt16 nVersion = 0;
880         rStm.ReadUInt16( nVersion );
881         if ( nVersion == 1 )
882         {
883             bRet = true;
884             if ( bExtendedInfo )
885             {
886                 sal_uInt16 nChannels = 0;
887                 sal_uInt32 nRows = 0;
888                 sal_uInt32 nColumns = 0;
889                 sal_uInt16 nDepth = 0;
890                 sal_uInt16 nMode = 0;
891                 rStm.SeekRel( 6 );  // Pad
892                 rStm.ReadUInt16( nChannels ).ReadUInt32( nRows ).ReadUInt32( nColumns ).ReadUInt16( nDepth ).ReadUInt16( nMode );
893                 if ( ( nDepth == 1 ) || ( nDepth == 8 ) || ( nDepth == 16 ) )
894                 {
895                     nBitsPerPixel = ( nDepth == 16 ) ? 8 : nDepth;
896                     switch ( nChannels )
897                     {
898                         case 4 :
899                         case 3 :
900                             nBitsPerPixel = 24;
901                             [[fallthrough]];
902                         case 2 :
903                         case 1 :
904                             aPixSize.setWidth( nColumns );
905                             aPixSize.setHeight( nRows );
906                         break;
907                         default:
908                             bRet = false;
909                     }
910                 }
911                 else
912                     bRet = false;
913             }
914         }
915     }
916 
917     if ( bRet )
918         nFormat = GraphicFileFormat::PSD;
919     rStm.Seek( nStmPos );
920     return bRet;
921 }
922 
923 bool GraphicDescriptor::ImpDetectEPS( SvStream& rStm, bool )
924 {
925     // check the EPS preview and the file extension
926     sal_uInt32  nFirstLong = 0;
927     sal_uInt8   nFirstBytes[20] = {};
928     bool        bRet = false;
929 
930     sal_Int32 nStmPos = rStm.Tell();
931     rStm.SetEndian( SvStreamEndian::BIG );
932     rStm.ReadUInt32( nFirstLong );
933     rStm.SeekRel( -4 );
934     rStm.ReadBytes( &nFirstBytes, 20 );
935 
936     if ( ( nFirstLong == 0xC5D0D3C6 ) || aPathExt.startsWith( "eps" ) ||
937         ( ImplSearchEntry( nFirstBytes, reinterpret_cast<sal_uInt8 const *>("%!PS-Adobe"), 10, 10 )
938             && ImplSearchEntry( &nFirstBytes[15], reinterpret_cast<sal_uInt8 const *>("EPS"), 3, 3 ) ) )
939     {
940         nFormat = GraphicFileFormat::EPS;
941         bRet = true;
942     }
943     rStm.Seek( nStmPos );
944     return bRet;
945 }
946 
947 bool GraphicDescriptor::ImpDetectDXF( SvStream&, bool )
948 {
949     bool bRet = aPathExt.startsWith( "dxf" );
950     if (bRet)
951         nFormat = GraphicFileFormat::DXF;
952 
953     return bRet;
954 }
955 
956 bool GraphicDescriptor::ImpDetectMET( SvStream&, bool )
957 {
958     bool bRet = aPathExt.startsWith( "met" );
959     if (bRet)
960         nFormat = GraphicFileFormat::MET;
961 
962     return bRet;
963 }
964 
965 bool GraphicDescriptor::ImpDetectPCT( SvStream& rStm, bool )
966 {
967     bool bRet = aPathExt.startsWith( "pct" );
968     if (bRet)
969         nFormat = GraphicFileFormat::PCT;
970     else
971     {
972         sal_uInt64 const nStreamPos = rStm.Tell();
973         sal_uInt64 const nStreamLen = rStm.remainingSize();
974         if (isPCT(rStm, nStreamPos, nStreamLen))
975         {
976             bRet = true;
977             nFormat = GraphicFileFormat::PCT;
978         }
979         rStm.Seek(nStreamPos);
980     }
981 
982     return bRet;
983 }
984 
985 bool GraphicDescriptor::ImpDetectSVM( SvStream& rStm, bool bExtendedInfo )
986 {
987     sal_uInt32  n32 = 0;
988     bool    bRet = false;
989     sal_uInt8   cByte = 0;
990 
991     sal_Int32 nStmPos = rStm.Tell();
992     rStm.SetEndian( SvStreamEndian::LITTLE );
993     rStm.ReadUInt32( n32 );
994     if ( n32 == 0x44475653 )
995     {
996         cByte = 0;
997         rStm.ReadUChar( cByte );
998         if ( cByte == 0x49 )
999         {
1000             nFormat = GraphicFileFormat::SVM;
1001             bRet = true;
1002 
1003             if ( bExtendedInfo )
1004             {
1005                 sal_uInt32  nTemp32;
1006                 sal_uInt16  nTemp16;
1007 
1008                 rStm.SeekRel( 0x04 );
1009 
1010                 // width
1011                 nTemp32 = 0;
1012                 rStm.ReadUInt32( nTemp32 );
1013                 aLogSize.setWidth( nTemp32 );
1014 
1015                 // height
1016                 nTemp32 = 0;
1017                 rStm.ReadUInt32( nTemp32 );
1018                 aLogSize.setHeight( nTemp32 );
1019 
1020                 // read MapUnit and determine PrefSize
1021                 nTemp16 = 0;
1022                 rStm.ReadUInt16( nTemp16 );
1023                 aLogSize = OutputDevice::LogicToLogic( aLogSize,
1024                                                        MapMode( static_cast<MapUnit>(nTemp16) ),
1025                                                        MapMode( MapUnit::Map100thMM ) );
1026             }
1027         }
1028     }
1029     else
1030     {
1031         rStm.SeekRel( -4 );
1032         n32 = 0;
1033         rStm.ReadUInt32( n32 );
1034 
1035         if( n32 == 0x4D4C4356 )
1036         {
1037             sal_uInt16 nTmp16 = 0;
1038 
1039             rStm.ReadUInt16( nTmp16 );
1040 
1041             if( nTmp16 == 0x4654 )
1042             {
1043                 nFormat = GraphicFileFormat::SVM;
1044                 bRet = true;
1045 
1046                 if( bExtendedInfo )
1047                 {
1048                     MapMode aMapMode;
1049 
1050                     rStm.SeekRel( 0x06 );
1051                     ReadMapMode( rStm, aMapMode );
1052                     TypeSerializer aSerializer(rStm);
1053                     aSerializer.readSize(aLogSize);
1054                     aLogSize = OutputDevice::LogicToLogic( aLogSize, aMapMode, MapMode( MapUnit::Map100thMM ) );
1055                 }
1056             }
1057         }
1058     }
1059     rStm.Seek( nStmPos );
1060     return bRet;
1061 }
1062 
1063 bool GraphicDescriptor::ImpDetectWMF( SvStream&, bool )
1064 {
1065     bool bRet = aPathExt.startsWith( "wmf" );
1066     if (bRet)
1067         nFormat = GraphicFileFormat::WMF;
1068 
1069     return bRet;
1070 }
1071 
1072 bool GraphicDescriptor::ImpDetectEMF( SvStream& rStm, bool bExtendedInfo )
1073 {
1074     sal_uInt32 nRecordType = 0;
1075     bool bRet = false;
1076 
1077     sal_Int32 nStmPos = rStm.Tell();
1078     rStm.SetEndian( SvStreamEndian::LITTLE );
1079     rStm.ReadUInt32( nRecordType );
1080 
1081     if ( nRecordType == 0x00000001 )
1082     {
1083         sal_uInt32 nHeaderSize = 0;
1084         sal_Int32 nBoundLeft = 0, nBoundTop = 0, nBoundRight = 0, nBoundBottom = 0;
1085         sal_Int32 nFrameLeft = 0, nFrameTop = 0, nFrameRight = 0, nFrameBottom = 0;
1086         sal_uInt32 nSignature = 0;
1087 
1088         rStm.ReadUInt32( nHeaderSize );
1089         rStm.ReadInt32( nBoundLeft );
1090         rStm.ReadInt32( nBoundTop );
1091         rStm.ReadInt32( nBoundRight );
1092         rStm.ReadInt32( nBoundBottom );
1093         rStm.ReadInt32( nFrameLeft );
1094         rStm.ReadInt32( nFrameTop );
1095         rStm.ReadInt32( nFrameRight );
1096         rStm.ReadInt32( nFrameBottom );
1097         rStm.ReadUInt32( nSignature );
1098 
1099         if ( nSignature == 0x464d4520 )
1100         {
1101             nFormat = GraphicFileFormat::EMF;
1102             bRet = true;
1103 
1104             if ( bExtendedInfo )
1105             {
1106                 // size in pixels
1107                 aPixSize.setWidth( nBoundRight - nBoundLeft + 1 );
1108                 aPixSize.setHeight( nBoundBottom - nBoundTop + 1 );
1109 
1110                 // size in 0.01mm units
1111                 aLogSize.setWidth( nFrameRight - nFrameLeft + 1 );
1112                 aLogSize.setHeight( nFrameBottom - nFrameTop + 1 );
1113             }
1114         }
1115     }
1116 
1117     rStm.Seek( nStmPos );
1118     return bRet;
1119 }
1120 
1121 bool GraphicDescriptor::ImpDetectSVG( SvStream& /*rStm*/, bool /*bExtendedInfo*/ )
1122 {
1123     bool bRet = aPathExt.startsWith( "svg" );
1124     if (bRet)
1125         nFormat = GraphicFileFormat::SVG;
1126 
1127     return bRet;
1128 }
1129 
1130 OUString GraphicDescriptor::GetImportFormatShortName( GraphicFileFormat nFormat )
1131 {
1132     const char *pKeyName = nullptr;
1133 
1134     switch( nFormat )
1135     {
1136         case GraphicFileFormat::BMP :   pKeyName = "bmp";   break;
1137         case GraphicFileFormat::GIF :   pKeyName = "gif";   break;
1138         case GraphicFileFormat::JPG :   pKeyName = "jpg";   break;
1139         case GraphicFileFormat::PCD :   pKeyName = "pcd";   break;
1140         case GraphicFileFormat::PCX :   pKeyName = "pcx";   break;
1141         case GraphicFileFormat::PNG :   pKeyName = "png";   break;
1142         case GraphicFileFormat::XBM :   pKeyName = "xbm";   break;
1143         case GraphicFileFormat::XPM :   pKeyName = "xpm";   break;
1144         case GraphicFileFormat::PBM :   pKeyName = "pbm";   break;
1145         case GraphicFileFormat::PGM :   pKeyName = "pgm";   break;
1146         case GraphicFileFormat::PPM :   pKeyName = "ppm";   break;
1147         case GraphicFileFormat::RAS :   pKeyName = "ras";   break;
1148         case GraphicFileFormat::TGA :   pKeyName = "tga";   break;
1149         case GraphicFileFormat::PSD :   pKeyName = "psd";   break;
1150         case GraphicFileFormat::EPS :   pKeyName = "eps";   break;
1151         case GraphicFileFormat::TIF :   pKeyName = "tif";   break;
1152         case GraphicFileFormat::DXF :   pKeyName = "dxf";   break;
1153         case GraphicFileFormat::MET :   pKeyName = "met";   break;
1154         case GraphicFileFormat::PCT :   pKeyName = "pct";   break;
1155         case GraphicFileFormat::SVM :   pKeyName = "svm";   break;
1156         case GraphicFileFormat::WMF :   pKeyName = "wmf";   break;
1157         case GraphicFileFormat::EMF :   pKeyName = "emf";   break;
1158         case GraphicFileFormat::SVG :   pKeyName = "svg";   break;
1159         default: assert(false);
1160     }
1161 
1162     return OUString::createFromAscii(pKeyName);
1163 }
1164 
1165 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1166