filters

structures.cpp
1/* This file is part of the LibMSWrite Library
2 Copyright (C) 2001-2003 Clarence Dang <clarencedang@users.sourceforge.net>
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License Version 2 as published by the Free Software Foundation.
7
8 This library is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 Library General Public License Version 2 for more details.
12
13 You should have received a copy of the GNU Library General Public License
14 Version 2 along with this library; see the file COPYING.LIB. If not,
15 write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
16 * Boston, MA 02110-1301, USA.
17
18 LibMSWrite Project Website:
19 http://sourceforge.net/projects/libmswrite/
20*/
21
22/*
23 * structures.cpp - Process Internal Microsoft(r) Write file structures
24 * This file has ugly reading/writing code that couldn't be generated.
25 * It performs some higher-level sanity checks.
26 *
27 */
28
29#include "structures.h"
30#include "structures_private.h"
31
32namespace MSWrite
33{
34 PageLayout::PageLayout () : m_numModified (0)
35 {
36 }
37
38 PageLayout::~PageLayout ()
39 {
40 }
41
42 PageLayout &PageLayout::operator= (const PageLayout &rhs)
43 {
44 if (this == &rhs)
45 return *this;
46
47 PageLayoutGenerated::operator= (rhs);
48 NeedsHeader::operator= (rhs);
49
50 m_numModified = rhs.m_numModified;
51
52 return *this;
53 }
54
55 bool PageLayout::readFromDevice (void)
56 {
57 CHECK_DEVICE;
58
59 #ifdef DEBUG_PAGELAYOUT
60 m_device->debug ("\n<<<< PageLayout::readFromDevice >>>>\n");
61 #endif
62
63 int numPageLayoutPages = m_header->getNumPageSectionProperty ();
64
65 #ifdef DEBUG_PAGELAYOUT
66 m_device->debug ("num pageLayoutPages: ", numPageLayoutPages);
67 #endif
68
69 // no PageLayout
70 if (numPageLayoutPages == 0)
71 return true;
72 else if (numPageLayoutPages > 1)
73 ErrorAndQuit (Error::InvalidFormat, "invalid #pageLayoutPages\n");
74
75 // seek to the PageLayout in the file
76 if (!m_device->seekInternal (m_header->getPageSectionProperty () * 128, SEEK_SET))
77 ErrorAndQuit (Error::FileError, "could not seek to pageLayout\n");
78
79 if (!PageLayoutGenerated::readFromDevice ())
80 return false;
81
82 #ifdef DEBUG_PAGELAYOUT
83 Dump (magic102);
84 Dump (magic512);
85
86 Dump (pageHeight);
87 Dump (pageWidth);
88 Dump (pageNumberStart);
89 Dump (topMargin);
90 Dump (textHeight);
91 Dump (leftMargin);
92 Dump (textWidth);
93
94 Dump (magic256);
95
96 Dump (headerFromTop);
97 Dump (footerFromTop);
98
99 Dump (magic720);
100 Dump (zero);
101 Dump (magic1080);
102 Dump (unknown);
103 Dump (zero2);
104 #endif
105
106 #define UpdateModifiedCount(variable) if (m_##variable != variable##Default) m_numModified++
107 UpdateModifiedCount (magic102);
108 UpdateModifiedCount (magic512);
109 UpdateModifiedCount (pageHeight);
110 UpdateModifiedCount (pageWidth);
111 UpdateModifiedCount (pageNumberStart);
112 UpdateModifiedCount (topMargin);
113 UpdateModifiedCount (textHeight);
114 UpdateModifiedCount (leftMargin);
115 UpdateModifiedCount (textWidth);
116 UpdateModifiedCount (magic256);
117 UpdateModifiedCount (headerFromTop);
118 UpdateModifiedCount (footerFromTop);
119 UpdateModifiedCount (magic720);
120 UpdateModifiedCount (zero);
121 UpdateModifiedCount (magic1080);
122 //UpdateModifiedCount (unknown); // no reliable default for unknown
123 UpdateModifiedCount (zero2);
124 #undef UpdateModifiedCount
125
126 return true;
127 }
128
129 bool PageLayout::writeToDevice (void)
130 {
131 CHECK_DEVICE;
132
133 #ifdef DEBUG_PAGELAYOUT
134 m_device->debug ("\n>>>> PageLayout::writeToDevice <<<<\n");
135 #endif
136
137 m_header->setPageSectionProperty (m_device->tellInternal () / 128);
138
139 // if we're just using defaults, we don't need a pageLayout
140 if (!getIsModified ())
141 return true;
142
143 if (!PageLayoutGenerated::writeToDevice ())
144 return false;
145
146 return true;
147 }
148
149
150 Font::Font (const Byte *name, const Byte family) : m_name (NULL)
151 {
152 if (name) setName (name);
153 FontGenerated::m_family = family;
154 }
155
156 Font::~Font ()
157 {
158 delete [] m_name;
159 }
160
161 Font &Font::operator= (const Font &rhs)
162 {
163 if (this == &rhs)
164 return *this;
165
166 FontGenerated::operator= (rhs);
167
168 setName (rhs.getName ());
169
170 return *this;
171 }
172
173 bool Font::readFromDevice (void)
174 {
175 CHECK_DEVICE;
176
177 if (!FontGenerated::readFromDevice ())
178 return false;
179
180 #ifdef DEBUG_FONT
181 Dump (numDataBytes);
182 Dump (family);
183 #endif
184
185 // is this font on the next page?
186 if (m_numDataBytes == 0xFFFF)
187 return false; // Device::m_error will be clear so this is ok
188
189 // is this the last font _ever_?
190 if (m_numDataBytes == 0)
191 return false; // Device::m_error will be clear so this is ok
192
193 // Font structure size can't exceed a page anyway
194 if (m_numDataBytes > 128 - sizeof (m_numDataBytes))
195 ErrorAndQuit (MSWrite::Error::InvalidFormat, "Font nameLen is too big\n");
196
197 // calculate length of name
198 int nameLen = m_numDataBytes - sizeof (m_family) - 1 /* NUL */;
199
200 // we should really call this->setName()...
201 m_name = new Byte [nameLen + 1];
202 if (!m_name)
203 ErrorAndQuit (Error::OutOfMemory, "could not allocate memory for fontName\n");
204
205 // read fontName
206 if (!m_device->readInternal (m_name, nameLen + 1))
207 ErrorAndQuit (Error::FileError, "could not read fontName\n");
208
209 // check name is valid
210 if (m_name [nameLen] != '\0')
211 ErrorAndQuit (Error::InvalidFormat, "fontName not NUL-terminated\n");
212
213 #ifdef DEBUG_FONT
214 m_device->debug ("\tfontName=", (const char *) m_name);
215 #endif
216 return true;
217 }
218
219 bool Font::writeToDevice (void)
220 {
221 CHECK_DEVICE;
222
223 // should I be on the next page? (TODO: this check is stupid if it's the last font)
224 Word offset = m_device->tellInternal () % 128; // from start of current page
225 if (offset + sizeof (m_numDataBytes) + m_numDataBytes + sizeof (m_numDataBytes)/*for next Font's 0xFFFF*/ > 128)
226 {
227 // signal that font is on next page
228 Byte scratch [2] = {0xFF, 0xFF};
229 if (!m_device->writeInternal (scratch, 2))
230 ErrorAndQuit (Error::FileError, "could not write Font 0xFFFF\n");
231 return false; // Device::m_error will be clear so this is ok
232 }
233
234 // --- we don't have to write 0 to indicate last font (since we write the right number of elements) ---
235
236 // write numDataBytes, family
237 if (!FontGenerated::writeToDevice ())
238 return false;
239
240 if (!m_device->writeInternal (m_name, m_numDataBytes - sizeof (m_family)))
241 ErrorAndQuit (Error::FileError, "could not write fontName\n");
242
243 return true;
244 }
245
246
247 FormatCharProperty::FormatCharProperty () : m_fontTable (NULL)
248 {
249 // m_afterEndCharByte =
250 // m_font =
251 }
252
253 FormatCharProperty::~FormatCharProperty ()
254 {
255 }
256
257 FormatCharProperty &FormatCharProperty::operator= (const FormatCharProperty &rhs)
258 {
259 if (this == &rhs)
260 return *this;
261
262 FormatCharPropertyGenerated::operator= (rhs);
263
264 m_afterEndCharByte = rhs.m_afterEndCharByte;
265 m_fontTable = rhs.m_fontTable;
266 m_font = rhs.m_font;
267
268 return *this;
269 }
270
271 bool FormatCharProperty::operator== (FormatCharProperty &rhs)
272 {
273 DWord numDataBytes;
274
275 if ((numDataBytes = getNumDataBytes ()) != rhs.getNumDataBytes ())
276 return false;
277
278 writeToArray ();
279 rhs.writeToArray ();
280
281 return memcmp (m_data + sizeof (m_numDataBytes), rhs.m_data + sizeof (m_numDataBytes), numDataBytes) == 0;
282 }
283
284 bool FormatCharProperty::readFromDevice (void)
285 {
286 CHECK_DEVICE;
287
288 #ifdef DEBUG_CHAR
289 m_device->debug ("\nFormatCharProperty::readFromDevice\n");
290 #endif
291
292 if (!FormatCharPropertyGenerated::readFromDevice ())
293 return false;
294
295 if (!m_fontTable)
296 ErrorAndQuit (Error::InternalError, "m_fontTable not setup for FormatCharProperty::readFromDevice\n");
297
298 // note: the converse is done in FormatInfoPage::add(), not FormatCharProperty::writeToDevice
299 if (!updateFont ()) return false;
300
301 return true;
302 }
303
304 bool FormatCharProperty::writeToDevice (void)
305 {
306 CHECK_DEVICE;
307
308 #ifdef DEBUG_CHAR
309 m_device->debug ("\nFormatCharProperty::writeToDevice, #bytes=", getNumDataBytes ());
310 #endif
311
312 return FormatCharPropertyGenerated::writeToDevice ();
313 }
314
315 bool FormatCharProperty::updateFont (void)
316 {
317 Font *fontPtr = m_fontTable->getFont (getFontCode ());
318 if (!fontPtr)
319 ErrorAndQuit (Error::InvalidFormat, "fontCode out of range\n");
320
321 m_font = *fontPtr;
322 return true;
323 }
324
325 bool FormatCharProperty::updateFontCode (void)
326 {
327 DWord code = m_fontTable->addFont (&m_font);
328 if (code == 0xFFFFFFFF)
329 return false;
330 else
331 {
332 setFontCode (code);
333 return true;
334 }
335 }
336
337
338 FormatParaPropertyTabulator::FormatParaPropertyTabulator ()
339 {
340 }
341
342 FormatParaPropertyTabulator::~FormatParaPropertyTabulator ()
343 {
344 }
345
346 FormatParaPropertyTabulator &FormatParaPropertyTabulator::operator= (const FormatParaPropertyTabulator &rhs)
347 {
348 if (this == &rhs)
349 return *this;
350
351 FormatParaPropertyTabulatorGenerated::operator= (rhs);
352
353 return *this;
354 }
355
356 bool FormatParaPropertyTabulator::readFromDevice (void)
357 {
358 CHECK_DEVICE;
359
360 #ifdef DEBUG_PARA_TAB
361 m_device->debug ("\nFormatParaPropertyTabulator::readFromDevice\n");
362 #endif
363
364 if (!FormatParaPropertyTabulatorGenerated::readFromDevice ())
365 return false;
366
367 #ifdef DEBUG_PARA_TAB
368 Dump (indent);
369 Dump (type);
370 Dump (zero);
371 #endif
372
373 return true;
374 }
375
376 bool FormatParaPropertyTabulator::writeToDevice (void)
377 {
378 CHECK_DEVICE;
379
380 #ifdef DEBUG_PARA_TAB
381 m_device->debug ("\nFormatParaPropertyTabulator::writeToDevice\n");
382 #endif
383
384 return FormatParaPropertyTabulatorGenerated::writeToDevice ();
385 }
386
387
388 FormatParaProperty::FormatParaProperty () : m_leftMargin (0xFFFF),
389 m_rightMargin (0xFFFF),
390 m_numTabulators (0),
391 m_addedTooManyTabs (false)
392 {
393 // m_afterEndCharByte =
394 }
395
396 FormatParaProperty::~FormatParaProperty ()
397 {
398 }
399
400 FormatParaProperty &FormatParaProperty::operator= (const FormatParaProperty &rhs)
401 {
402 if (this == &rhs)
403 return *this;
404
405 FormatParaPropertyGenerated::operator= (rhs);
406
407 m_afterEndCharByte = rhs.m_afterEndCharByte;
408 m_leftMargin = rhs.m_leftMargin, m_rightMargin = rhs.m_rightMargin;
409 m_numTabulators = rhs.m_numTabulators;
410 m_addedTooManyTabs = rhs.m_addedTooManyTabs;
411
412 return *this;
413 }
414
415 bool FormatParaProperty::operator== (FormatParaProperty &rhs)
416 {
417 DWord numDataBytes;
418 if ((numDataBytes = getNumDataBytes ()) != rhs.getNumDataBytes ())
419 return false;
420
421 writeToArray ();
422 rhs.writeToArray ();
423
424 return memcmp (m_data + sizeof (m_numDataBytes), rhs.m_data + sizeof (m_numDataBytes), numDataBytes) == 0;
425 }
426
427 bool FormatParaProperty::readFromDevice (void)
428 {
429 CHECK_DEVICE;
430
431 #ifdef DEBUG_PARA
432 m_device->debug ("\nFormatParaProperty::readFromDevice\n");
433 #endif
434
435 #ifdef CHECK_INTERNAL
436 if (m_leftMargin == 0xFFFF && m_rightMargin == 0xFFFF)
437 ErrorAndQuit (Error::InternalError, "FormatParaProperty::readFromDevice call not setup\n");
438 #endif
439
440 if (!FormatParaPropertyGenerated::readFromDevice ())
441 return false;
442
443 // Correct indentation in Header and Footer:
444 //
445 // For unknown reasons, Write adds the leftMargin to the leftIndent
446 // and rightMargin to rightIndent in the Header and Footer only but
447 // does not touch the leftIndentFirstLine
448 //
449 // TODO: what if it's an image in the header/footer?
450 if (getIsNotNormalParagraph ())
451 {
452 // adjust leftIndent
453 if (m_leftMargin < m_leftIndent)
454 m_leftIndent -= m_leftMargin;
455 else
456 m_leftIndent = 0;
457
458 // adjust rightIndent
459 if (m_rightMargin < m_rightIndent)
460 m_rightIndent -= m_rightMargin;
461 else
462 m_rightIndent = 0;
463 }
464
465 if (m_numDataBytes > 22)
466 m_numTabulators = (m_numDataBytes - 22) / FormatParaPropertyTabulator::s_size;
467 else
468 m_numTabulators = 0;
469
470 // for impexp
471 if (getNumDataBytes () != m_numDataBytes && !m_numTabulators)
472 m_device->error (Error::Warn, "m_numDataBytes != getNumDataBytes ()\n");
473 UseThisMuch::signalHaveSetData (false, m_numDataBytes * 8);
474
475 return true;
476 }
477
478 bool FormatParaProperty::writeToDevice (void)
479 {
480 CHECK_DEVICE;
481
482 #ifdef CHECK_INTERNAL
483 if (m_leftMargin == 0xFFFF && m_rightMargin == 0xFFFF)
484 ErrorAndQuit (Error::InternalError, "FormatParaProperty::writeToDevice call not setup\n");
485 #endif
486
487 #ifdef DEBUG_PARA
488 m_device->debug ("\nFormatParaProperty::writeToDevice, #bytes=", getNumDataBytes ());
489 #endif
490
491 if (m_addedTooManyTabs)
492 ErrorAndQuit (Error::InternalError, "cannot have more than 14 tabulators; shouldn't even have more than 12\n");
493
494 if (m_numTabulators > 12)
495 m_device->error (Error::Warn, "should not have more than 12 tabulators since you can only access 12 tabs via the GUI\n");
496
497 // --- indents adjusted in updateIndents() called by FormatInfoPage::add() ---
498
499 if (!FormatParaPropertyGenerated::writeToDevice ())
500 return false;
501
502 return true;
503 }
504
505
506 Image::Image () : m_externalImage (NULL),
507 m_externalImageSize (0),
508 m_externalImageUpto (0),
509 m_originalWidth (0), m_originalHeight (0),
510 m_displayedWidth (0), m_displayedHeight (0)
511 {
512 }
513
514 Image::~Image ()
515 {
516 delete [] m_externalImage;
517 }
518
519 Image &Image::operator= (const Image &rhs)
520 {
521 if (this == &rhs)
522 return *this;
523
524 ImageGenerated::operator= (rhs);
525
526 m_externalImageSize = rhs.m_externalImageSize;
527 m_externalImageUpto = rhs.m_externalImageUpto;
528
529 delete [] m_externalImage;
530 m_externalImage = new Byte [m_externalImageSize];
531 if (!m_externalImage)
532 return *this; // TODO: error check
533
534 if (rhs.m_externalImage)
535 memcpy (m_externalImage, rhs.m_externalImage, m_externalImageUpto);
536
537 m_originalWidth = rhs.m_originalWidth;
538 m_originalHeight = rhs.m_originalHeight;
539
540 m_displayedWidth = rhs.m_displayedWidth;
541 m_displayedHeight = rhs.m_displayedHeight;
542
543 return *this;
544 }
545
546 // returns how many bytes are needed for each scanline (with byte padding)
547 int Image::getBytesPerScanLine (const int width, const int bitsPerPixel, const int padBytes)
548 {
549 int bitWidth;
550 int byteWidth;
551
552 // figure out how many bytes it would take to represent "width" pixels, each taking "bitsPerPixel" bits
553 bitWidth = width * bitsPerPixel;
554 byteWidth = bitWidth / 8;
555 if (bitWidth % 8)
556 byteWidth++;
557
558 // pad the number of bytes taken to the next multiple of "padBytes"
559 // (if not divisible by "padBytes")
560 byteWidth = (byteWidth + padBytes - 1) / padBytes * padBytes;
561
562 return byteWidth;
563 }
564
565 bool Image::readFromDevice (void)
566 {
567 CHECK_DEVICE;
568
569 #ifdef DEBUG_IMAGE
570 m_device->debug ("\n<<<< Image::readFromDevice >>>>\n");
571 #endif
572
573 if (!ImageGenerated::readFromDevice ())
574 return false;
575
576 #ifdef DEBUG_IMAGE
577 Dump (mappingMode);
578 Dump (MFP_width);
579 Dump (MFP_height);
580 Dump (MFP_unknown);
581
582 Dump (indent);
583 Dump (width);
584 Dump (height);
585 Dump (zero);
586
587 Dump (numHeaderBytes);
588 Dump (numDataBytes);
589 Dump (horizontalScalingRel1000);
590 Dump (verticalScalingRel1000);
591 #endif
592
593 if (getIsWMF ())
594 {
595 //
596 // get image dimensions
597 //
598
599 if (m_bmh->getWidth () || m_bmh->getHeight ())
600 m_device->error (Error::Warn, "m_bmh structure should be 0 for WMFs\n");
601
602 m_originalWidth = Milli2Twip (double (m_MFP_width) / 100.0) * 4.0/3.0;
603 m_originalHeight = Milli2Twip (double (m_MFP_height) / 100.0) * 4.0/3.0;
604
605 m_displayedWidth = double (m_width);
606 m_displayedHeight = double (m_height);
607
608 if (m_horizontalScalingRel1000 != 1000)
609 m_device->error (Error::Warn, "horizontal scaling should not be set for WMFs\n");
610 if (m_verticalScalingRel1000 != 1000)
611 m_device->error (Error::Warn, "vertical scaling should not be set for WMFs\n");
612
613
614 //
615 // read image
616 //
617
618 m_externalImage = new Byte [m_externalImageSize = getNumDataBytes ()];
619 if (!m_externalImage)
620 ErrorAndQuit (Error::OutOfMemory, "could not allocate memory for external WMF image\n");
621
622 if (!m_device->readInternal (m_externalImage, m_externalImageSize))
623 ErrorAndQuit (Error::FileError, "could not read internal WMF\n");
624
625 // Header check
626 WMFHeader wmfHeader;
627 m_device->setCache (m_externalImage);
628 wmfHeader.setDevice (m_device);
629 if (!wmfHeader.readFromDevice ())
630 return false;
631
632 // TODO: fix this incorrect check (see wmf.cpp)
633 //if (wmfHeader.getFileSize () * sizeof (Word) != m_numDataBytes)
634 // m_device->error (Error::Warn, "wmfHeader.fileSize != numDataBytes\n");
635 m_device->setCache (NULL);
636 }
637 else // if (getIsBMP ())
638 {
639 //
640 // get image dimensions
641 //
642
643 m_originalWidth = Point2Twip (DWord (m_bmh->getWidth ()));
644 m_originalHeight = Point2Twip (DWord (m_bmh->getHeight ()));
645
646 m_displayedWidth = m_originalWidth / 1.38889 * m_horizontalScalingRel1000 / 1000;
647 m_displayedHeight = m_originalHeight / 1.38889 * m_verticalScalingRel1000 / 1000;
648
649#define MSWrite_fabs(val) (((val)>=0)?(val):(-(val)))
650
651 if (MSWrite_fabs (m_MFP_width / double (m_bmh->getWidth ()) - 2.64) > .3)
652 m_device->error (Error::Warn, "m_MFP_width != m_bmh->getWidth() * 2.64\n");
653 if (MSWrite_fabs (m_MFP_height / double (m_bmh->getHeight ()) - 2.64) > .3)
654 m_device->error (Error::Warn, "m_MFP_height != m_bmh->getHeight() * 2.64\n");
655
656#undef MSWrite_fabs
657
658 if (m_width)
659 m_device->error (Error::Warn, "m_width should not be set for BMPs\n");
660
661 if (m_height)
662 m_device->error (Error::Warn, "m_height should not be set for BMPs\n");
663
664
665 //
666 // read image
667 //
668
669 Byte *internalData = new Byte [getNumDataBytes ()];
670 if (!internalData)
671 ErrorAndQuit (Error::OutOfMemory, "could not allocate memory for internal BMP image\n");
672 if (!m_device->readInternal (internalData, getNumDataBytes ()))
673 ErrorAndQuit (Error::FileError, "could not read internal BMP\n");
674
675 // infoHeader
676 BMP_BitmapInfoHeader infoHeader;
677 infoHeader.setWidth (m_bmh->getWidth ());
678 infoHeader.setHeight (m_bmh->getHeight ());
679 if (m_bmh->getNumPlanes () != 1)
680 ErrorAndQuit (Error::InvalidFormat, "bmh.m_numPlanes != 1\n");
681 infoHeader.setNumPlanes (m_bmh->getNumPlanes ());
682 infoHeader.setBitsPerPixel (m_bmh->getBitsPerPixel ());
683 infoHeader.setCompression (0); // BI_RGB (uncompressed)
684 infoHeader.setSizeImage (0); // lazy
685 infoHeader.setXPixelsPerMeter (0), infoHeader.setYPixelsPerMeter (0);
686 infoHeader.setColoursUsed (1 << infoHeader.getBitsPerPixel ());
687 infoHeader.setColoursImportant (infoHeader.getColoursUsed ());
688
689 if (infoHeader.getColoursUsed () != 2)
690 ErrorAndQuit (Error::InternalError, "colour bitmap??? Please email clarencedang@users.sourceforge.net this file\n");
691
692 Word colourTableSize = infoHeader.getColoursUsed () * BMP_BitmapColourIndex::s_size;
693
694 // fileHeader
695 BMP_BitmapFileHeader fileHeader;
696 DWord fileSize = BMP_BitmapFileHeader::s_size + BMP_BitmapInfoHeader::s_size
697 + colourTableSize
698 + (m_bmh->getHeight ()
699 * getBytesPerScanLine (m_bmh->getWidth (), m_bmh->getBitsPerPixel (), 4));
700
701 fileHeader.setTotalBytes (fileSize);
702 fileHeader.setActualImageOffset (BMP_BitmapFileHeader::s_size + BMP_BitmapInfoHeader::s_size
703 + colourTableSize);
704
705 // colourTable
706 BMP_BitmapColourIndex *colourIndex = new BMP_BitmapColourIndex [infoHeader.getColoursUsed ()];
707 if (!colourIndex)
708 ErrorAndQuit (Error::OutOfMemory, "could not allocate memory for colourIndex[]\n");
709
710
711 // black and white...
712 colourIndex [0].setRed (0), colourIndex [0].setGreen (0), colourIndex [0].setBlue (0);
713 colourIndex [1].setRed (0xFF), colourIndex [1].setGreen (0xFF), colourIndex [1].setBlue (0xFF);
714
715 m_externalImage = new Byte [m_externalImageSize = fileSize];
716 if (!m_externalImage)
717 ErrorAndQuit (Error::OutOfMemory, "could not allocate memory for external BMP image\n");
718
719 MemoryDevice device;
720 device.setCache (m_externalImage);
721 fileHeader.setDevice (&device);
722 fileHeader.writeToDevice ();
723 infoHeader.setDevice (&device);
724 infoHeader.writeToDevice ();
725 for (int i = 0; i < 2; i++)
726 {
727 colourIndex [i].setDevice (&device);
728 colourIndex [i].writeToDevice ();
729 }
730
731 // write out each scanline
732 // (BMP padded to 4 bytes vs WRI input bitmap which is actually padded to 2)
733 Word scanLineWRILength = getBytesPerScanLine (infoHeader.getWidth (), infoHeader.getBitsPerPixel (), 2);
734 if (scanLineWRILength != m_bmh->getWidthBytes ())
735 ErrorAndQuit (Error::InvalidFormat, "scanLineWRILength != m_bmh->getWidthBytes()\n");
736 Word scanLineBMPLength = getBytesPerScanLine (infoHeader.getWidth (), infoHeader.getBitsPerPixel (), 4);
737
738 #ifdef DEBUG_IMAGE
739 m_device->debug ("in: scanLineWRILength: ", scanLineWRILength);
740 m_device->debug ("out: scanLineBMPLength: ", scanLineBMPLength);
741 #endif
742
743 Byte *padding = new Byte [scanLineBMPLength - scanLineWRILength];
744 if (!padding)
745 ErrorAndQuit (Error::OutOfMemory, "could not allocate memory for scanline\n");
746 memset (padding, 0, scanLineBMPLength - scanLineWRILength);
747
748 // the DIB is upside-down...
749 for (int i = (int) infoHeader.getHeight () - 1; i >= 0; i--)
750 {
751 // write bitmap scanline
752 device.writeInternal (internalData + i * scanLineWRILength, scanLineWRILength * sizeof (Byte));
753
754 // write padding for scanline
755 device.writeInternal (padding, (scanLineBMPLength - scanLineWRILength) * sizeof (Byte));
756 }
757
758 delete [] padding;
759
760 device.setCache (NULL);
761
762 delete [] colourIndex;
763 delete [] internalData;
764 }
765
766 return true;
767 }
768
769 bool Image::writeToDevice (void)
770 {
771 CHECK_DEVICE;
772
773 #ifdef DEBUG_IMAGE
774 m_device->debug ("\n>>>> Image::writeToDevice <<<<\n");
775 #endif
776
777 #ifdef DEBUG_IMAGE
778 Dump (mappingMode);
779 //Dump (MFP_width); // will change below
780 //Dump (MFP_height);
781 Dump (MFP_unknown);
782
783 Dump (indent);
784 //Dump (width);
785 //Dump (height);
786 Dump (zero);
787
788 Dump (numHeaderBytes);
789 //Dump (numDataBytes);
790 //Dump (horizontalScalingRel1000);
791 //Dump (verticalScalingRel1000);
792 #endif
793
794 //
795 // write data
796 //
797 //
798 if (getIsWMF ())
799 {
800 // Header check
801 WMFHeader wmfHeader;
802 m_device->setCache (m_externalImage);
803 wmfHeader.setDevice (m_device);
804 if (!wmfHeader.readFromDevice ()) return false;
805
806 // TODO: fix this incorrect check (see wmf.cpp)
807 //if (wmfHeader.getFileSize () * sizeof (Word) != m_externalImageSize)
808 // m_device->error (Error::Warn, "wmfHeader.fileSize != externalImageSize\n");
809 m_device->setCache (NULL);
810
811
812 //
813 // set image dimensions
814 //
815
816 // entire BitmapHeader is unused with WMFs
817 m_bmh->setWidth (0);
818 m_bmh->setHeight (0);
819 m_bmh->setWidthBytes (0);
820 m_bmh->setNumPlanes (0);
821 m_bmh->setBitsPerPixel (0);
822
823 m_MFP_width = Word (Twip2Milli (m_originalWidth * 0.75) * 100.0);
824 m_MFP_height = Word (Twip2Milli (m_originalHeight * 0.75) * 100.0);
825
826 m_width = Word (m_displayedWidth);
827 m_height = Word (m_displayedHeight);
828
829 // not used by WMFs
830 m_horizontalScalingRel1000 = m_verticalScalingRel1000 = 1000;
831
832
833 // write header
834 setNumDataBytes (m_externalImageSize);
835 if (!ImageGenerated::writeToDevice ())
836 return false;
837
838 // external=internal with WMF (i.e. we really do write a WMF)
839 if (!m_device->writeInternal (m_externalImage, m_externalImageSize)) return false;
840 }
841 else // if (getIsBMP ())
842 {
843 m_device->setCache (m_externalImage);
844
845 BMP_BitmapFileHeader fileHeader;
846 fileHeader.setDevice (m_device);
847 if (!fileHeader.readFromDevice ()) return false;
848
849
850 /*Word colourTableSize = (1 << m_bmh->getNumPlanes ()) * BMP_BitmapColourIndex::s_size;
851
852 // fileHeader
853 DWord fileSize = BMP_BitmapFileHeader::s_size + BMP_BitmapInfoHeader::s_size
854 + colourTableSize
855 + (m_bmh->getHeight ()
856 * getBytesPerScanLine (m_bmh->getWidth (), m_bmh->getBitsPerPixel (), 4));
857
858 fileHeader.setTotalBytes (fileSize);
859 fileHeader.setActualImageOffset (BMP_BitmapFileHeader::s_size + BMP_BitmapInfoHeader::s_size
860 + colourTableSize);*/
861
862 // infoHeader
863 BMP_BitmapInfoHeader infoHeader;
864 infoHeader.setDevice (m_device);
865 if (!infoHeader.readFromDevice ()) return false;
866
867 // write out each scanline
868 // to .WRI (padded to 2) vs input BMP (padded to 4
869 Word scanLineWRILength = getBytesPerScanLine (infoHeader.getWidth (), infoHeader.getBitsPerPixel (), 2);
870 Word scanLineBMPLength = getBytesPerScanLine (infoHeader.getWidth (), infoHeader.getBitsPerPixel (), 4);
871
872 if (infoHeader.getWidth () != Word (Twip2Point (m_originalWidth)))
873 ErrorAndQuit (Error::Warn, "infoHeader width != m_originalWidth\n");
874 if (infoHeader.getHeight () != Word (Twip2Point (m_originalHeight)))
875 ErrorAndQuit (Error::Warn, "infoHeader.height != m_originalHeight\n");
876
877 m_bmh->setWidth (infoHeader.getWidth ());
878 m_bmh->setHeight (infoHeader.getHeight ());
879 m_bmh->setWidthBytes (scanLineWRILength);
880 if (infoHeader.getNumPlanes () != 1)
881 ErrorAndQuit (Error::InvalidFormat, "infoHeader.getNumPlanes() != 1\n");
882 m_bmh->setNumPlanes (infoHeader.getNumPlanes ());
883 m_bmh->setBitsPerPixel (infoHeader.getBitsPerPixel ());
884 if (infoHeader.getCompression () != 0) // BI_RGB (uncompressed)
885 ErrorAndQuit (Error::Unsupported, "compressed bitmaps unsupported\n");
886 //infoHeader.setSizeImage (0); // lazy
887 //infoHeader.setXPixelsPerMeter (0), infoHeader.setYPixelsPerMeter (0);
888 infoHeader.setColoursUsed (1 << infoHeader.getBitsPerPixel ()); // make life easier
889 //infoHeader.setColoursImportant (infoHeader.getColoursUsed ());
890
891 if (infoHeader.getColoursUsed () != 2)
892 ErrorAndQuit (Error::Unsupported, "can't save colour BMPs, use WMFs for that purpose\n");
893
894 // colourTable
895 BMP_BitmapColourIndex *colourIndex = new BMP_BitmapColourIndex [infoHeader.getColoursUsed ()];
896 if (!colourIndex)
897 ErrorAndQuit (Error::OutOfMemory, "could not allocate memory for colourIndex[]\n");
898 colourIndex [0].setDevice (m_device);
899 if (!colourIndex [0].readFromDevice ()) return false;
900 if (colourIndex [0].getRed () != 0 || colourIndex [0].getGreen () != 0 || colourIndex [0].getBlue () != 0)
901 m_device->error (Error::Warn, "black not black\n");
902 colourIndex [1].setDevice (m_device);
903 if (!colourIndex [1].readFromDevice ()) return false;
904 if (colourIndex [1].getRed () != 0xFF || colourIndex [1].getGreen () != 0xFF || colourIndex [1].getBlue () != 0xFF)
905 m_device->error (Error::Warn, "white not white\n");
906
907 // finish reading from m_externalImage
908 m_device->setCache (NULL);
909
910
911 //
912 // set image dimensions
913 //
914
915 m_MFP_width = Word (Twip2Point (m_originalWidth) * 2.64);
916 m_MFP_height = Word (Twip2Point (m_originalHeight) * 2.64);
917
918 // BMPs don't use
919 m_width = 0, m_height = 0;
920
921 m_horizontalScalingRel1000 = Word (m_displayedWidth * 1.38889 * 1000.0 / m_originalWidth);
922 m_verticalScalingRel1000 = Word (m_displayedHeight * 1.38889 * 1000.0 / m_originalHeight);
923
924
925 // write header
926 setNumDataBytes (infoHeader.getHeight () * scanLineBMPLength);
927 if (!ImageGenerated::writeToDevice ())
928 return false;
929
930 // the DIB is upside-down...
931 Byte *bmpData = m_externalImage + fileHeader.getActualImageOffset () + (infoHeader.getHeight () - 1) * scanLineBMPLength;
932 for (int i = (int) infoHeader.getHeight () - 1; i >= 0; i--)
933 {
934 // write bitmap scanline (padded to 2)
935 //if (!m_device->writeInternal (m_externalImage + fileHeader.getActualImageOffset () + i * scanLineBMPLength, scanLineWRILength))
936 if (!m_device->writeInternal (bmpData, scanLineWRILength))
937 return false;
938
939 bmpData -= scanLineBMPLength;
940 }
941
942 delete [] colourIndex;
943 }
944
945 return true;
946 }
947
948
949 OLE::OLE () : m_externalObject (NULL),
950 m_externalObjectSize (0), m_externalObjectUpto (0)
951 {
952 }
953
954 OLE::~OLE ()
955 {
956 delete [] m_externalObject;
957 }
958
959 OLE &OLE::operator= (const OLE &rhs)
960 {
961 if (this == &rhs)
962 return *this;
963
964 OLEGenerated::operator= (rhs);
965
966 m_externalObjectSize = rhs.m_externalObjectSize;
967 m_externalObjectUpto = rhs.m_externalObjectUpto;
968
969 delete [] m_externalObject;
970 m_externalObject = new Byte [m_externalObjectSize];
971 if (!m_externalObject)
972 return *this; // TODO: error check
973
974 if (rhs.m_externalObject)
975 memcpy (m_externalObject, rhs.m_externalObject, m_externalObjectUpto);
976
977 return *this;
978 }
979
980 bool OLE::readFromDevice (void)
981 {
982 CHECK_DEVICE;
983
984 #ifdef DEBUG_OBJECT
985 m_device->debug ("\n<<<< OLE::readFromDevice >>>>\n");
986 #endif
987
988 if (!OLEGenerated::readFromDevice ())
989 return false;
990
991 #ifdef DEBUG_OBJECT
992 Dump (zero);
993
994 switch (m_objectType)
995 {
996 case OLEType::Static:
997 m_device->debug ("\tobjectType: 1 - static\n");
998 break;
999 case OLEType::Embedded:
1000 m_device->debug ("\tobjectType: 2 - embedded\n");
1001 break;
1002 case OLEType::Link:
1003 m_device->debug ("\tobjectType: 3 - link\n");
1004 break;
1005 }
1006
1007 Dump (indent);
1008 Dump (width);
1009 Dump (height);
1010 Dump (zero2);
1011 Dump (numDataBytes);
1012 Dump (zero3);
1013 Dump (objectName);
1014 Dump (zero4);
1015 Dump (numHeaderBytes);
1016 Dump (zero5);
1017 Dump (widthScaledRel1000);
1018 Dump (heightScaledRel1000);
1019 #endif
1020
1021 // OPT: TODO: this is dumb, we read it only to give it back to the parser who tells the generator to write it
1022 m_externalObject = new Byte [m_externalObjectSize = getNumDataBytes ()];
1023 if (!m_externalObject)
1024 ErrorAndQuit (Error::OutOfMemory, "could not allocate memory for external OLE object\n");
1025
1026 if (!m_device->readInternal (m_externalObject, m_externalObjectSize))
1027 return false;
1028
1029 return true;
1030 }
1031
1032 bool OLE::writeToDevice (void)
1033 {
1034 CHECK_DEVICE;
1035
1036 #ifdef DEBUG_OBJECT
1037 m_device->debug ("\n>>>> OLE::writeToDevice <<<<\n");
1038 #endif
1039
1040 #ifdef DEBUG_OBJECT
1041 Dump (zero);
1042
1043 switch (m_objectType)
1044 {
1045 case OLEType::Static:
1046 m_device->debug ("\tobjectType: 1 - static\n");
1047 break;
1048 case OLEType::Embedded:
1049 m_device->debug ("\tobjectType: 2 - embedded\n");
1050 break;
1051 case OLEType::Link:
1052 m_device->debug ("\tobjectType: 3 - link\n");
1053 break;
1054 }
1055
1056 Dump (indent);
1057 Dump (width);
1058 Dump (height);
1059 Dump (zero2);
1060 Dump (numDataBytes);
1061 Dump (zero3);
1062 Dump (objectName);
1063 Dump (zero4);
1064 Dump (numHeaderBytes);
1065 Dump (zero5);
1066 Dump (widthScaledRel1000);
1067 Dump (heightScaledRel1000);
1068 #endif
1069
1070 // write header
1071 if (!OLEGenerated::writeToDevice ())
1072 return false;
1073
1074 // write data
1075 if (!m_device->writeInternal (m_externalObject, m_externalObjectSize))
1076 return false;
1077
1078 return true;
1079 }
1080
1081} // namespace MSWrite {
1082
1083// end of structures.cpp