1 /*
2   Copyright 2008 Brad Hards <bradh@frogmouth.net>
3   Copyright 2009 Inge Wallin <inge@lysator.liu.se>
4 
5   This library is free software; you can redistribute it and/or
6   modify it under the terms of the GNU Lesser General Public
7   License as published by the Free Software Foundation; either
8   version 2.1 of the License, or (at your option) any later version.
9 
10   This library is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13   Lesser General Public License for more details.
14 
15   You should have received a copy of the GNU Lesser General Public
16   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
17 */
18 
19 #include "EmfRecords.h"
20 
21 #include "EmfEnums.h"
22 #include "EmfObjects.h"
23 #include "Bitmap.h"
24 
25 #include <VectorImageDebug.h>
26 
27 namespace Libemf
28 {
29 
30 
31 /*****************************************************************************/
32 
33 
BitBltRecord(QDataStream & stream,quint32 recordSize)34 BitBltRecord::BitBltRecord( QDataStream &stream, quint32 recordSize )
35     : m_bitmap(0)
36 {
37     //debugVectorImage << "stream position at the start: " << stream.device()->pos();
38     //debugVectorImage << "record size: " << recordSize;
39 
40     stream >> m_bounds;
41 
42     stream >> m_xDest;          // x, y of upper left corner of the destination.
43     stream >> m_yDest;
44     stream >> m_cxDest;         // width, height of the rectangle in logical coords.
45     stream >> m_cyDest;
46     //debugVectorImage << "Destination" << m_xDest << m_yDest << m_cxDest << m_cyDest;
47 
48     stream >> m_BitBltRasterOperation;
49     //debugVectorImage << "bitblt raster operation:" << hex << m_BitBltRasterOperation << dec;
50 
51     stream >> m_xSrc;           // x, y of the source
52     stream >> m_ySrc;
53     //debugVectorImage << "Source" << m_xSrc << m_ySrc;
54 
55     //debugVectorImage << "position before the matrix: " << stream.device()->pos();
56     stream.setFloatingPointPrecision(QDataStream::SinglePrecision);
57     float M11, M12, M21, M22, Dx, Dy;
58     stream >> M11;              // Transformation matrix
59     stream >> M12;
60     stream >> M21;
61     stream >> M22;
62     stream >> Dx;
63     stream >> Dy;
64     m_XFormSrc = QTransform( M11, M12, M21, M22, Dx, Dy );
65     //debugVectorImage << "Matrix" << m_XFormSrc;
66     //debugVectorImage << "position after the matrix: " << stream.device()->pos();
67 
68     stream >> m_red >> m_green >> m_blue >> m_reserved;
69     //debugVectorImage << "Background color" << m_red << m_green << m_blue << m_reserved;
70     //debugVectorImage << "position after background color: " << stream.device()->pos();
71 
72     stream >> m_UsageSrc;
73     //debugVectorImage << "Color table interpretation" << m_UsageSrc;
74 
75     stream >> m_offBmiSrc;      // Offset to start of bitmap header from start of record
76     stream >> m_cbBmiSrc;       // Size of source bitmap header
77     stream >> m_offBitsSrc;     // Offset to source bitmap from start of record
78     stream >> m_cbBitsSrc;      // Size of source bitmap
79 #if 0
80     debugVectorImage << "header offset:" << m_offBmiSrc;
81     debugVectorImage << "header size:  " << m_cbBmiSrc;
82     debugVectorImage << "bitmap offset:" << m_offBitsSrc;
83     debugVectorImage << "bitmap size:  " << m_cbBitsSrc;
84 #endif
85 
86     //debugVectorImage << "stream position before the image: " << stream.device()->pos();
87     if (m_cbBmiSrc > 0) {
88         m_bitmap = new Bitmap( stream, recordSize, 8 + 23 * 4, // header + 23 ints
89                                m_offBmiSrc, m_cbBmiSrc,
90                                m_offBitsSrc, m_cbBitsSrc );
91     }
92 
93     //debugVectorImage << "stream position at the end: " << stream.device()->pos();
94 }
95 
~BitBltRecord()96 BitBltRecord::~BitBltRecord()
97 {
98     delete m_bitmap;
99 }
100 
hasImage() const101 bool BitBltRecord::hasImage() const
102 {
103     return m_bitmap && m_bitmap->hasImage();
104     //return ( ( m_cbBmiSrc != 0 ) && ( m_cbBitsSrc != 0 ) );
105 }
106 
image()107 QImage BitBltRecord::image()
108 {
109     return m_bitmap->image();
110 #if 0
111     if ( ! hasImage() ) {
112         return 0;
113     }
114 
115     if ( m_image != 0 ) {
116         return m_image;
117     }
118 
119     QImage::Format format = QImage::Format_Invalid;
120     if ( m_BmiSrc->bitCount() == BI_BITCOUNT_4 ) {
121         if ( m_BmiSrc->compression() == 0x00 ) {
122             format = QImage::Format_RGB555;
123         } else {
124             debugVectorImage << "Unexpected compression format for BI_BITCOUNT_4:"
125                      << m_BmiSrc->compression();
126             Q_ASSERT( 0 );
127         }
128     } else if ( m_BmiSrc->bitCount() == BI_BITCOUNT_5 ) {
129         format = QImage::Format_RGB888;
130     } else {
131         debugVectorImage << "Unexpected format:" << m_BmiSrc->bitCount();
132         Q_ASSERT( 0 );
133     }
134     m_image = new QImage( (const uchar*)m_imageData.constData(),
135                           m_BmiSrc->width(), m_BmiSrc->height(), format );
136 
137     return m_image;
138 #endif
139 }
140 
141 /*****************************************************************************/
StretchDiBitsRecord(QDataStream & stream,quint32 recordSize)142 StretchDiBitsRecord::StretchDiBitsRecord( QDataStream &stream, quint32 recordSize )
143     : m_bitmap(0)
144 {
145     //debugVectorImage << "stream position at the start: " << stream.device()->pos();
146     //debugVectorImage << "recordSize =" << recordSize;
147 
148     stream >> m_Bounds;
149     stream >> m_xDest;
150     stream >> m_yDest;
151     stream >> m_xSrc;
152     stream >> m_ySrc;
153     stream >> m_cxSrc;
154     stream >> m_cySrc;
155 
156     stream >> m_offBmiSrc;
157     stream >> m_cbBmiSrc;
158     stream >> m_offBitsSrc;
159     stream >> m_cbBitsSrc;
160 
161     stream >> m_UsageSrc;       // How to interpret color table values.
162     stream >> m_BitBltRasterOperation;
163     stream >> m_cxDest;
164     stream >> m_cyDest;
165 #if 0
166     debugVectorImage << "bounds:" << m_Bounds;
167     debugVectorImage << "destination:" << QPoint(m_xDest, m_yDest) << QSize(m_cxDest, m_cyDest);
168     debugVectorImage << "source:" << QPoint(m_xSrc, m_ySrc) << QSize(m_cxSrc, m_cySrc);
169     debugVectorImage << "header offset:" << m_offBmiSrc;
170     debugVectorImage << "header size:  " << m_cbBmiSrc;
171     debugVectorImage << "bitmap offset:" << m_offBitsSrc;
172     debugVectorImage << "bitmap size:  " << m_cbBitsSrc;
173 
174     debugVectorImage << "m_BitBltRasterOperation =" << hex << m_BitBltRasterOperation << dec;
175 #endif
176 
177     //debugVectorImage << "stream position before the image: " << stream.device()->pos();
178     if (m_cbBmiSrc > 0) {
179         m_bitmap = new Bitmap( stream, recordSize, 8 + 18 * 4, // header + 18 ints
180                                m_offBmiSrc, m_cbBmiSrc,
181                                m_offBitsSrc, m_cbBitsSrc );
182     }
183 
184     //debugVectorImage << "stream position at the end: " << stream.device()->pos();
185 #if 0
186     // Read away those bytes that preceed the header.  These are undefined
187     // according to the spec.  80 is the size of the record above.
188     qint32 dummy;
189     int    padding = 0;
190     while (m_offBmiSrc - padding > 80) {
191         stream >> dummy;
192         padding += 4;
193     }
194     m_BmiSrc = new BitmapHeader( stream, m_cbBmiSrc );
195 
196     // 40 is the size of the header record.
197     while (m_offBitsSrc - padding > 80 + 40) {
198         stream >> dummy;
199         padding += 4;
200     }
201     m_imageData.resize( m_cbBitsSrc );
202     stream.readRawData( m_imageData.data(), m_cbBitsSrc );
203 #endif
204 }
205 
~StretchDiBitsRecord()206 StretchDiBitsRecord::~StretchDiBitsRecord()
207 {
208     delete m_bitmap;
209 }
210 
bounds() const211 QRect StretchDiBitsRecord::bounds() const
212 {
213     return m_Bounds;
214 }
215 
hasImage() const216 bool StretchDiBitsRecord::hasImage() const
217 {
218     return m_bitmap && m_bitmap->hasImage();
219 }
220 
image()221 QImage StretchDiBitsRecord::image()
222 {
223     return m_bitmap->image();
224 #if 0
225     if ( m_image != 0 ) {
226         return m_image;
227     }
228 
229     QImage::Format format = QImage::Format_Invalid;
230 
231     // Start by determining which QImage format we are going to use.
232     if (m_BmiSrc->bitCount() == BI_BITCOUNT_1) {
233         format = QImage::Format_Mono;
234     } else if ( m_BmiSrc->bitCount() == BI_BITCOUNT_4 ) {
235         if ( m_BmiSrc->compression() == BI_RGB ) {
236             format = QImage::Format_RGB555;
237         } else {
238             debugVectorImage << "Unexpected compression format for BI_BITCOUNT_4:"
239                           << m_BmiSrc->compression();
240             Q_ASSERT( 0 );
241         }
242     } else if ( m_BmiSrc->bitCount() == BI_BITCOUNT_5 ) {
243         format = QImage::Format_RGB888;
244     } else {
245         debugVectorImage << "Unexpected format:" << m_BmiSrc->bitCount();
246         //Q_ASSERT(0);
247     }
248 
249     // According to MS-WMF 2.2.2.3, the sign of the height decides if
250     // this is a compressed bitmap or not.
251     if (m_BmiSrc->height() > 0) {
252         // This bitmap is a top-down bitmap without compression.
253         m_image = new QImage( (const uchar*)m_imageData.constData(),
254                               m_BmiSrc->width(), m_BmiSrc->height(), format );
255 
256         // The WMF images are in the BGR color order.
257         if (format == QImage::Format_RGB888)
258             *m_image = m_image->rgbSwapped();
259 
260         // We have to mirror this bitmap in the X axis since WMF images are stored bottom-up.
261         *m_image = m_image->mirrored(false, true);
262     } else {
263         // This bitmap is a bottom-up bitmap which uses compression.
264         switch (m_BmiSrc->compression()) {
265         case BI_RGB:
266             m_image = new QImage( (const uchar*)m_imageData.constData(),
267                                   m_BmiSrc->width(), -m_BmiSrc->height(), format );
268             // The WMF images are in the BGR color order.
269             *m_image = m_image->rgbSwapped();
270             break;
271 
272         // These compressions are not yet supported, so return an empty image.
273         case BI_RLE8:
274         case BI_RLE4:
275         case BI_BITFIELDS:
276         case BI_JPEG:
277         case BI_PNG:
278         case BI_CMYK:
279         case BI_CMYKRLE8:
280         case BI_CMYKRLE4:
281         default:
282             m_image = new QImage(m_BmiSrc->width(), m_BmiSrc->height(), format);
283             break;
284         }
285     }
286 
287     return m_image;
288 #endif
289 }
290 
291 /*****************************************************************************/
ExtCreateFontIndirectWRecord(QDataStream & stream,quint32 size)292 ExtCreateFontIndirectWRecord::ExtCreateFontIndirectWRecord( QDataStream &stream, quint32 size )
293 {
294     stream >> m_ihFonts;
295     size -= 12;
296 
297     // TODO: Check size, we might need to do a LogFontExDv parse
298     stream >> m_height;
299     stream >> m_width;
300     size -= 8;
301 
302     stream >> m_escapement;
303     size -= 4;
304 
305     stream >> m_orientation;
306     size -= 4;
307 
308     stream >> m_weight;
309     size -= 4;
310 
311     stream >> m_italic;
312     stream >> m_underline;
313     stream >> m_strikeout;
314     stream >> m_charSet;
315     size -= 4;
316 
317     stream >> m_outPrecision;
318     stream >> m_clipPrecision;
319     stream >> m_quality;
320     stream >> m_pitchAndFamily;
321     size -= 4;
322 
323     QChar myChar[64];
324     for ( int i = 0; i < 32; ++i ) {
325 	stream >> myChar[i];
326     }
327     size -= 64;
328 
329     for ( int i = 0; i < 32; ++i ) {
330 	if ( ! myChar[i].isNull() ) {
331 	    m_facename.append( myChar[i] );
332 	}
333     }
334 
335 #if 0
336     for ( int i = 0; i < 64; ++i ) {
337 	stream >> myChar[i];
338     }
339     size -= 128;
340 
341     for ( int i = 0; i < 64; ++i ) {
342 	if ( ! myChar[i].isNull() ) {
343 	    m_fullName.append( myChar[i] );
344 	}
345     }
346     debugVectorImage << "fullName:" << m_fullName;
347 
348     for ( int i = 0; i < 32; ++i ) {
349 	stream >> myChar[i];
350     }
351     size -= 64;
352     for ( int i = 0; i < 32; ++i ) {
353 	if ( ! myChar[i].isNull() ) {
354 	    m_style.append( myChar[i] );
355 	}
356     }
357     debugVectorImage << "style:" << m_style;
358 
359     for ( int i = 0; i < 32; ++i ) {
360 	stream >> myChar[i];
361     }
362     size -= 64;
363     for ( int i = 0; i < 32; ++i ) {
364 	if ( ! myChar[i].isNull() ) {
365 	    m_script.append( myChar[i] );
366 	}
367     }
368     debugVectorImage << "script:" << m_script;
369 #endif
370     soakBytes( stream, size ); // rest of the record.
371 }
372 
~ExtCreateFontIndirectWRecord()373 ExtCreateFontIndirectWRecord::~ExtCreateFontIndirectWRecord()
374 {
375 }
376 
soakBytes(QDataStream & stream,int numBytes)377 void ExtCreateFontIndirectWRecord::soakBytes( QDataStream &stream, int numBytes )
378 {
379     quint8 scratch;
380     for ( int i = 0; i < numBytes; ++i ) {
381         stream >> scratch;
382     }
383 }
384 
385 }
386