1 /*
2    drvsvm.cpp : This file is part of pstoedit
3 	 Backend for OpenOffice metafile (SVM - StarView metafile)
4 
5    Copyright (C) 2005 Thorsten Behrens (tbehrens at acm.org)
6 
7     This program is free software; you can redistribute it and/or modify
8     it under the terms of the GNU General Public License as published by
9     the Free Software Foundation; either version 2 of the License, or
10     (at your option) any later version.
11 
12     This program is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15     GNU General Public License for more details.
16 
17     You should have received a copy of the GNU General Public License
18     along with this program; if not, write to the Free Software
19     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 
21 */
22 #include "drvsvm.h"
23 #include "svm_actions.h"
24 
25 #include I_string_h
26 #include I_fstream
27 #include I_stdio
28 #include I_stdlib
29 
30 #include <math.h>
31 #include <utility>
32 #include <vector>
33 
34 /* TODO section:
35  *
36  * Check the following example files again:
37  * - largetextspace.ps
38  * - colrtest.ps
39  * - images
40  * - bounding box
41  */
42 
43 /* Most of the information necessary to read/write SVM files is
44  * available only by reading the code - for the parts used here, this
45  * is vcl/inc/metaact.hxx, vcl/inc/font.hxx, vcl/inc/vclenum.hxx and
46  * the corresponding source files in vcl/source/gdi. The actual
47  * reading/writing of SVMs resides in vcl/source/gdi/gdimtf.cxx (all
48  * files relative to the OpenOffice.org source tree, of course).
49  */
50 
51 namespace {
52    // const char description[] = "generated by SVM backend of pstoedit\0input\0\0";
53 
writePod(ostream & outf,const T value)54     template< typename T > void writePod(ostream& outf,
55                                          const T  value)
56     {
57         outf.write( const_cast<char*>(
58                         reinterpret_cast<const char*>(&value)),
59                     sizeof(value) );
60     }
61 
62 #ifdef PS_BIG_ENDIAN
63 // taken from OpenOffice.org, sal/inc/osl/endian.h (LGPLed)
64 # define MAKEWORD(bl, bh)    ((drvSVM::uInt16)((bl) & 0xFF) | (((drvSVM::uInt16)(bh) & 0xFF) << 8))
65 # define LOBYTE(w)           ((drvSVM::uInt8)((drvSVM::uInt16)(w) & 0xFF))
66 # define HIBYTE(w)           ((drvSVM::uInt8)(((drvSVM::uInt16)(w) >> 8) & 0xFF))
67 # define MAKEDWORD(wl, wh)   ((drvSVM::uInt32)((wl) & 0xFFFF) | (((drvSVM::uInt32)(wh) & 0xFFFF) << 16))
68 # define LOWORD(d)           ((drvSVM::uInt16)((drvSVM::uInt32)(d) & 0xFFFF))
69 # define HIWORD(d)           ((drvSVM::uInt16)(((drvSVM::uInt32)(d) >> 16) & 0xFFFF))
70 # define SWAPWORD(w)         MAKEWORD(HIBYTE(w),LOBYTE(w))
71 
72     // have to override >8 bit writes, since SVM is little-endian -
73     // note further that we _don't_ need to override the signed types,
74     // too - integral promotion does happen for normal method lookup
75     // (compared to choosing a template specialization)
76 
writePod(ostream & outf,drvSVM::uInt16 value)77     void writePod(ostream& 	outf,
78                   drvSVM::uInt16  value)
79     {
80         value = SWAPWORD(value);
81         outf.write( const_cast<char*>(
82                         reinterpret_cast<const char*>(&value)),
83                     sizeof(drvSVM::uInt16) );
84     }
85 
writePod(ostream & outf,drvSVM::uInt32 value)86     void writePod(ostream& 	outf,
87                   drvSVM::uInt32  value)
88     {
89         value = MAKEDWORD(SWAPWORD(HIWORD(value)),
90                           SWAPWORD(LOWORD(value)));
91         outf.write( const_cast<char*>(
92                         reinterpret_cast<const char*>(&value)),
93                     sizeof(drvSVM::uInt32) );
94     }
95 #endif
96 
fakeVersionCompat(ostream & outf,drvSVM::uInt16 versionId,drvSVM::uInt32 len)97     static void fakeVersionCompat(ostream& outf,
98 						   drvSVM::uInt16 	versionId,
99                            drvSVM::uInt32	len)
100     {
101         writePod(outf, versionId);
102         writePod(outf, len);
103     }
104 }
105 
derivedConstructor(drvSVM)106 drvSVM::derivedConstructor(drvSVM) :
107     constructBase,
108     headerPos(0),
109     actionCount(0),
110     isDriverOk(close_output_file_and_reopen_in_binary_mode())
111 {
112     // setup driver base class
113     const char *const defaultfontname = "System";
114     setCurrentFontName(defaultfontname, false /* is standard font */ );
115 
116 	x_offset = 0.0;
117 	y_offset = currentDeviceHeight; // need to mirror in y
118 
119 
120     // write SVM file header
121     // ---------------------
122 
123     outf << "VCLMTF";
124     fakeVersionCompat(outf, 1, 0x31);
125 
126 	// stream compress mode
127     writePod(outf, (uInt32)0);
128 
129     headerPos = outf.tellp();
130 
131     // pref mapmode (place holder, gets written again in destructor)
132     fakeVersionCompat(outf, 1, 0x1b);
133     writePod(outf, (uInt16)0); // map unit: 100th mm
134     writePod(outf, (Int32)0);  // origin x
135     writePod(outf, (Int32)0);  // origin y
136     writePod(outf, (Int32)1);  // scale x numerator
137     writePod(outf, (Int32)1);  // scale x denominator
138     writePod(outf, (Int32)1);  // scale y numerator
139     writePod(outf, (Int32)1);  // scale y denominator
140     writePod(outf, (uInt8)0);  // 'simple' mapmode flag
141 
142 	// pref size
143     writePod(outf, (Int32)0); // prefsize x
144     writePod(outf, (Int32)0); // prefsize y
145 
146 	// action count
147     writePod(outf, (uInt32)0);
148 
149     // set PostScript-compatible text alignment
150     writePod(outf,
151              (uInt16)META_TEXTALIGN_ACTION);
152     fakeVersionCompat(outf, 1, 0);
153     writePod(outf, (uInt16)1); // alignment: baseline
154     ++actionCount;
155 }
156 
~drvSVM()157 drvSVM::~drvSVM()
158 {
159 	const BBox& psBBox( getCurrentBBox() );
160 
161     // write out pref mapmode (can only do that now reliably, as
162     // during construction, input might not have been fully parsed)
163     outf.seekp(headerPos);
164 
165     if (Verbose())
166         errf << "calculated Bounding Box: "
167              << l_transX(psBBox.ll.x_)
168              << " "
169              << l_transY(psBBox.ur.y_)
170              << " "
171              << l_transX(psBBox.ur.x_)
172              << " "
173              << l_transY(psBBox.ll.y_) << endl;
174 
175     // pref mapmode
176     fakeVersionCompat(outf, 1, 0x1b);
177     writePod(outf, (uInt16)0); // map unit: 100th mm
178     writePod(outf,
179              (Int32)l_transX(psBBox.ll.x_)); // origin x
180     writePod(outf,
181              (Int32)l_transY(psBBox.ur.y_)); // origin y
182 
183     // convert between pt and 100th mm (factor 35.14598)
184     writePod(outf, (Int32)3514598); // scale x numerator
185     writePod(outf, (Int32)100000);  // scale x denominator
186     writePod(outf, (Int32)3514598); // scale y numerator
187     writePod(outf, (Int32)100000);  // scale y denominator
188     writePod(outf, (uInt8)0); // clear 'simple' mapmode flag
189 
190 	// pref size
191     writePod(outf, (Int32)abs(
192                  l_transX(psBBox.ll.x_) - l_transX(psBBox.ur.x_)) + 1 ); // prefsize x
193     writePod(outf, (Int32)abs(
194                  l_transY(psBBox.ll.y_) - l_transY(psBBox.ur.y_)) + 1 ); // prefsize y
195 
196 	// action count
197     writePod(outf, (uInt32)actionCount);
198 }
199 
setAttrs(LineColorAction eLineAction,FillColorAction eFillAction)200 void drvSVM::setAttrs( LineColorAction eLineAction,
201                        FillColorAction eFillAction )
202 {
203     // write MetaLineColorAction
204     writePod(outf,
205              (uInt16)META_LINECOLOR_ACTION);
206     fakeVersionCompat(outf, 1, 0);
207     writePod(outf,
208              (uInt8)(edgeB()*255 + .5));
209     writePod(outf,
210              (uInt8)(edgeG()*255 + .5));
211     writePod(outf,
212              (uInt8)(edgeR()*255 + .5));
213     writePod(outf,(uInt8)0); // dummy
214 
215     switch( eLineAction )
216     {
217         case lineColor:
218             // switch on line color
219             writePod(outf, (uInt8)1);
220             break;
221 
222         case noLineColor:
223             // switch off line color
224             writePod(outf, (uInt8)0);
225             break;
226 
227         default:
228             assert (0 && "Unknown line color action");
229             break;
230     }
231 
232     ++actionCount;
233 
234     // write MetaFillColorAction
235     writePod(outf,
236              (uInt16)META_FILLCOLOR_ACTION);
237     fakeVersionCompat(outf, 1, 0);
238     writePod(outf,
239              (uInt8)(fillB()*255 + .5));
240     writePod(outf,
241              (uInt8)(fillG()*255 + .5));
242     writePod(outf,
243              (uInt8)(fillR()*255 + .5));
244     writePod(outf,(uInt8)0); // dummy
245 
246     switch( eFillAction )
247     {
248         case fillColor:
249             // switch on fill color
250             writePod(outf, (uInt8)1);
251             break;
252 
253         case noFillColor:
254             // switch off fill color
255             writePod(outf, (uInt8)0);
256             break;
257 
258         default:
259             assert (0 && "Unknown fill color action");
260             break;
261     }
262 
263     ++actionCount;
264 }
265 
ClipPath(cliptype)266 void drvSVM::ClipPath(cliptype /* clipmode*/)
267 {
268 }
269 
Save()270 void drvSVM::Save()
271 {
272 }
273 
Restore()274 void drvSVM::Restore()
275 {
276 }
277 
close_page()278 void drvSVM::close_page()
279 {
280 	// NOOP in drvsvm
281 }
282 
283 
open_page()284 void drvSVM::open_page()
285 {
286 	// NOOP in drvsvm
287 }
288 
write_path(VectorOfVectorOfPoints const & polyPolygon,VectorOfVectorOfFlags const & polyPolygonFlags)289 void  drvSVM::write_path( VectorOfVectorOfPoints const& polyPolygon,
290                           VectorOfVectorOfFlags const&  polyPolygonFlags )
291 {
292     // write MetaPolyPolygonAction
293     writePod(outf,
294              (uInt16)META_POLYPOLYGON_ACTION);
295     fakeVersionCompat(outf, 2, 0);
296     const std::size_t numPolies( polyPolygon.size() );
297 
298     // write polyPolygon.size() empty polygons
299     writePod(outf, (uInt16)numPolies);
300 	{for( std::size_t i=0; i<numPolies; ++i )
301 		writePod(outf, (uInt16)0);}
302 
303     // write polyPolygon.size() polygons, possibly with curves
304     writePod(outf, (uInt16)numPolies);
305 	{for( std::size_t i=0; i<numPolies; ++i )
306     {
307         // write out index (the polygons written here are 'replacing'
308         // the dummies written above, at the given index)
309         writePod(outf, (uInt16)i);
310 
311         fakeVersionCompat(outf, 1, 0);
312         writePod(outf, (uInt16)polyPolygon[i].size());
313         outf.write( reinterpret_cast<char*>(
314                         const_cast<IntPoint*>( &polyPolygon[i][0] )),
315                         sizeof(IntPoint)*polyPolygon[i].size() );
316         writePod(outf, (uInt8)1); // flag: have flag array
317         outf.write( reinterpret_cast<char*>(
318                         const_cast<uInt8*>( &polyPolygonFlags[i][0] )),
319                         sizeof(uInt8)*polyPolygonFlags[i].size() );
320 	}}
321 
322     ++actionCount;
323 }
324 
write_polyline(VectorOfVectorOfPoints const & polyPolygon,VectorOfVectorOfFlags const & polyPolygonFlags)325 void  drvSVM::write_polyline( VectorOfVectorOfPoints const& polyPolygon,
326                               VectorOfVectorOfFlags const&  polyPolygonFlags )
327 {
328     const std::size_t numPolies( polyPolygon.size() );
329     for( std::size_t currPoly=0; currPoly<numPolies; ++currPoly ) {
330         // write MetaPolyLineAction
331         writePod(outf,
332                  (uInt16)META_POLYLINE_ACTION);
333         fakeVersionCompat(outf, 3, 0);
334 
335         // write empty polygon
336         writePod(outf, (uInt16)0);
337 
338         // write LineInfo
339         fakeVersionCompat(outf, 1, 0); // TODO(F2): support V2 dash/dot parameterization
340         switch (currentLineType()) {
341             case dotted:
342             case dashed:
343             case dashdot:
344             case dashdotdot:
345                 writePod(outf, (uInt16)2);
346                 break;
347 
348             case solid:
349                 writePod(outf, (uInt16)1);
350                 break;
351 
352             default:
353                 assert (0 && "Unknown line pattern type");
354                 break;
355         }
356         writePod(outf, (Int32)(currentLineWidth() + .5));
357 
358         // write out actual polygon data
359         writePod(outf, (uInt8)1); // flag, enabling following polygon
360 
361         fakeVersionCompat(outf, 1, 0);
362         writePod(outf, (uInt16)polyPolygon[currPoly].size());
363         outf.write( reinterpret_cast<char*>(
364                         const_cast<IntPoint*>( &polyPolygon[currPoly][0] )),
365                         sizeof(IntPoint)*polyPolygon[currPoly].size() );
366         writePod(outf, (uInt8)1); // flag: have flag array
367         outf.write( reinterpret_cast<char*>(
368                         const_cast<uInt8*>( &polyPolygonFlags[currPoly][0] )),
369                         sizeof(uInt8)*polyPolygonFlags[currPoly].size() );
370 
371         ++actionCount;
372     }
373 }
374 
show_path()375 void drvSVM::show_path()
376 {
377     // create poly-polygon from path info
378     // ----------------------------------
379 
380     VectorOfVectorOfPoints polyPolygon;
381     VectorOfPoints		   currPolygon;
382     VectorOfVectorOfFlags  polyPolygonFlags;
383     VectorOfFlags  		   currPolygonFlags;
384 
385     const unsigned int numElems(numberOfElementsInPath());
386 	for(unsigned int n=0; n<numElems; ++n) {
387 		const basedrawingelement& elem( pathElement(n) );
388 		switch (elem.getType()) {
389 		case moveto:
390         {
391             // TODO(P3): lots of copying here...
392             if( !currPolygon.empty() )
393             {
394                 polyPolygon.push_back(currPolygon);
395                 polyPolygonFlags.push_back(currPolygonFlags);
396                 currPolygon.clear();
397                 currPolygonFlags.clear();
398             }
399         }
400 
401         // FALLTHROUGH intended
402 		case lineto:
403         {
404             const Point& p( elem.getPoint(0) );
405             currPolygon.push_back(
406                 std::make_pair( (Int32)l_transX(p.x_),
407                                 (Int32)l_transY(p.y_) ));
408             currPolygonFlags.push_back(0);
409         }
410         break;
411 
412 		case closepath:
413         {
414             if( !currPolygon.empty() )
415             {
416                 // append first point again
417                 currPolygon.push_back( currPolygon.front() );
418                 currPolygonFlags.push_back( currPolygonFlags.front() );
419 
420                 // TODO(P3): lots of copying here...
421                 polyPolygon.push_back(currPolygon);
422                 polyPolygonFlags.push_back(currPolygonFlags);
423                 currPolygon.clear();
424                 currPolygonFlags.clear();
425             }
426         }
427         break;
428 
429 		case curveto:
430         {
431             const Point& c1( elem.getPoint(0) );
432             currPolygon.push_back(
433                 std::make_pair( (Int32)l_transX(c1.x_),
434                                 (Int32)l_transY(c1.y_) ));
435             currPolygonFlags.push_back(2);
436 
437             const Point& c2( elem.getPoint(1) );
438             currPolygon.push_back(
439                 std::make_pair( (Int32)l_transX(c2.x_),
440                                 (Int32)l_transY(c2.y_) ));
441             currPolygonFlags.push_back(2);
442 
443             const Point& p2( elem.getPoint(2) );
444             currPolygon.push_back(
445                 std::make_pair( (Int32)l_transX(p2.x_),
446                                 (Int32)l_transY(p2.y_) ));
447             currPolygonFlags.push_back(0);
448         }
449         break;
450 
451 		default:
452             assert (0 && "Unknown path element type");
453             break;
454 		}
455 	}
456 
457     // finish last active polygon
458     if( !currPolygon.empty() )
459     {
460         // TODO(P3): lots of copying here...
461         polyPolygon.push_back(currPolygon);
462         polyPolygonFlags.push_back(currPolygonFlags);
463         currPolygon.clear();
464         currPolygonFlags.clear();
465     }
466 
467 
468 	// determine path type: fill or line
469     // ---------------------------------
470     const bool need_line_info( currentLineType() != solid ||
471                                currentLineWidth() > 0 );
472 	switch (currentShowType()) {
473         case drvbase::stroke:
474         {
475             setAttrs( lineColor, noFillColor );
476 
477             if (need_line_info)
478             {
479                 write_polyline( polyPolygon, polyPolygonFlags );
480             }
481             else
482             {
483                 write_path( polyPolygon, polyPolygonFlags );
484             }
485         }
486         break;
487 
488         case drvbase::fill:
489         case drvbase::eofill:
490         {
491             if (pathWasMerged())
492             {
493                 setAttrs( lineColor, fillColor );
494 
495                 write_path( polyPolygon, polyPolygonFlags );
496                 if (need_line_info)
497                     write_polyline( polyPolygon, polyPolygonFlags );
498             }
499             else
500             {
501                 setAttrs( noLineColor, fillColor );
502                 write_path( polyPolygon, polyPolygonFlags );
503             }
504         }
505         break;
506 
507         default:
508             assert (0 && "Unknown path show type");
509             break;
510 	}
511 }
512 
513 
show_text(const TextInfo & textinfo)514 void drvSVM::show_text(const TextInfo& textinfo)
515 {
516     if (fontchanged())
517     {
518         static const char* symbolName = "symbol";
519 
520         // TODO(F2): evaluate textinfo.FontMatrix, and emulate
521         // advancements. Or: abort with error, and require user to
522         // re-run with '-pti' given (see largetextspace.ps example).
523 
524         // write out MetaFontAction (selecting the new font into VCL
525         // OutputDevice)
526         const Int16 fontHeight = (short int) (textinfo.currentFontSize + .5);
527         const Int16 fontAngle = (short int) (10 * textinfo.currentFontAngle + .5);
528 
529         Int16 		fontWidth = 0;
530         uInt16		fontWeigth = 0;	// default: don't care
531         uInt16		fontItalic = 0;	// default: no italics
532         uInt16		charSet = 0;	// default: don't know charset
533 
534         const char* fontName = NULL;
535 
536         if (strstr(textinfo.currentFontWeight.c_str(), "Regular"))
537             fontWeigth = 4;	// semi light weight
538 
539         if (strstr(textinfo.currentFontWeight.c_str(), "Normal"))
540             fontWeigth = 5;	// normal weight
541 
542         if (strstr(textinfo.currentFontWeight.c_str(), "Medium"))
543             fontWeigth = 6;	// medium weight
544 
545         if (options->emulateNarrowFonts) {
546             if (strstr(textinfo.currentFontWeight.c_str(), "Thin") ||
547                 strstr(textinfo.currentFontName.c_str(), "Thin") ||
548                 strstr(textinfo.currentFontFullName.c_str(), "Thin")) {
549                 fontWidth = fontHeight / 3;	// narrow font emulation (trial and error value for Arial font)
550             }
551 
552             if (strstr(textinfo.currentFontWeight.c_str(), "Extralight") ||
553                 strstr(textinfo.currentFontName.c_str(), "Extralight") ||
554                 strstr(textinfo.currentFontFullName.c_str(), "Extralight")) {
555                 fontWidth = fontHeight / 4;	// narrow font emulation (trial and error value for Arial font)
556             }
557 
558             if (strstr(textinfo.currentFontWeight.c_str(), "Ultralight") ||
559                 strstr(textinfo.currentFontName.c_str(), "Ultralight") ||
560                 strstr(textinfo.currentFontFullName.c_str(), "Ultralight")) {
561                 fontWidth = fontHeight / 4;	// narrow font emulation (trial and error value for Arial font)
562             }
563 
564             if (strstr(textinfo.currentFontWeight.c_str(), "Light") ||
565                 strstr(textinfo.currentFontName.c_str(), "Light") ||
566                 strstr(textinfo.currentFontFullName.c_str(), "Light") ||
567                 strstr(textinfo.currentFontWeight.c_str(), "Condensed") ||
568                 strstr(textinfo.currentFontName.c_str(), "Condensed") ||
569                 strstr(textinfo.currentFontFullName.c_str(), "Condensed")) {
570                 fontWidth = fontHeight / 3;	// narrow font emulation (trial and error value for Arial font)
571             }
572         }
573         else {
574             if (strstr(textinfo.currentFontWeight.c_str(), "Thin"))
575                 fontWeigth = 1;	// thin
576 
577             if (strstr(textinfo.currentFontWeight.c_str(), "Extralight"))
578                 fontWeigth = 1;	// thin
579 
580             if (strstr(textinfo.currentFontWeight.c_str(), "Ultralight"))
581                 fontWeigth = 2;	// ultra light
582 
583             if (strstr(textinfo.currentFontWeight.c_str(), "Light") ||
584                 strstr(textinfo.currentFontWeight.c_str(), "Condensed"))
585                 fontWeigth = 3;	// light
586         }
587 
588         if (strstr(textinfo.currentFontWeight.c_str(), "Semibold") ||
589             strstr(textinfo.currentFontName.c_str(), "Semibold") ||
590             strstr(textinfo.currentFontFullName.c_str(), "Semibold"))
591             fontWeigth = 7;	// semibold
592 
593         if (strstr(textinfo.currentFontWeight.c_str(), "Demibold") ||
594             strstr(textinfo.currentFontName.c_str(), "Demibold") ||
595             strstr(textinfo.currentFontFullName.c_str(), "Demibold"))
596             fontWeigth = 7;	// semibold
597 
598         if (strstr(textinfo.currentFontWeight.c_str(), "Bold") ||
599             strstr(textinfo.currentFontName.c_str(), "Bold") ||
600             strstr(textinfo.currentFontFullName.c_str(), "Bold"))
601             fontWeigth = 8;	// bold
602 
603         if (strstr(textinfo.currentFontWeight.c_str(), "Extrabold") ||
604             strstr(textinfo.currentFontName.c_str(), "Extrabold") ||
605             strstr(textinfo.currentFontFullName.c_str(), "Extrabold"))
606             fontWeigth = 8;	// bold
607 
608         if (strstr(textinfo.currentFontWeight.c_str(), "Ultrabold") ||
609             strstr(textinfo.currentFontName.c_str(), "Ultrabold") ||
610             strstr(textinfo.currentFontFullName.c_str(), "Ultrabold"))
611             fontWeigth = 9;	// ultrabold
612 
613         if (strstr(textinfo.currentFontWeight.c_str(), "Heavy") ||
614             strstr(textinfo.currentFontName.c_str(), "Heavy") ||
615             strstr(textinfo.currentFontFullName.c_str(), "Heavy"))
616             fontWeigth = 9;	// ultrabold
617 
618         if (strstr(textinfo.currentFontWeight.c_str(), "Black") ||
619             strstr(textinfo.currentFontName.c_str(), "Black") ||
620             strstr(textinfo.currentFontFullName.c_str(), "Black"))
621             fontWeigth = 10;	// black
622 
623         if ((strstr(textinfo.currentFontName.c_str(), "Italic") != NIL) ||
624             (strstr(textinfo.currentFontFullName.c_str(), "Italic") != NIL))
625             fontItalic = 2; // normal italics
626 
627         if ((strstr(textinfo.currentFontName.c_str(), "Oblique") != NIL) ||
628             (strstr(textinfo.currentFontFullName.c_str(), "Oblique") != NIL))
629             fontItalic = 1; // oblique italics
630 
631         if ((strstr(textinfo.currentFontFullName.c_str(), "Symbol") != NIL) ||
632             (strstr(textinfo.currentFontFullName.c_str(), "symbol") != NIL)) {
633             charSet = 10; // symbol charset
634             fontName = symbolName;
635         } else {
636             charSet = 11; // ASCII-US charset
637             fontName = textinfo.currentFontName.c_str();
638         }
639 
640         // write MetaFontAction
641         writePod(outf,
642                  (uInt16)META_FONT_ACTION);
643         fakeVersionCompat(outf, 1, 0);
644 
645         // serialize Font
646         fakeVersionCompat(outf, 2, 0);
647 
648         // font name
649         const size_t stringLen = strlen(fontName);
650         writePod(outf,
651                  (uInt16)stringLen);
652         outf.write(fontName,stringLen);
653 
654         // font style
655         writePod(outf,
656                  (uInt16)0);
657 
658         // font size
659         writePod(outf,
660                  (Int32)fontWidth);
661         writePod(outf,
662                  (Int32)(-fontHeight));
663 
664         // charset
665         writePod(outf,
666                  (uInt16)charSet);
667 
668         // TODO(F3): font family
669         writePod(outf,
670                  (uInt16)0);
671 
672         // font pitch
673         writePod(outf,
674                  (uInt16)0);
675 
676         // font weight
677         writePod(outf,
678                  (uInt16)fontWeigth);
679 
680         // font underline
681         writePod(outf,
682                  (uInt16)0);
683 
684         // font strikeout
685         writePod(outf,
686                  (uInt16)0);
687 
688         // font italic
689         writePod(outf,
690                  (uInt16)fontItalic);
691 
692         // text language
693         writePod(outf,
694                  (uInt16)0);
695 
696         // width type
697         writePod(outf,
698                  (uInt16)0);
699 
700         // font orientation
701         writePod(outf,
702                  (uInt16)fontAngle);
703 
704         // word line
705         writePod(outf,
706                  (uInt8)0);
707 
708         // TODO(F3): detect outline mode
709         writePod(outf,
710                  (uInt8)0);
711 
712         // shadow
713         writePod(outf,
714                  (uInt8)0);
715 
716         // kerning
717         writePod(outf,
718                  (uInt8)0);
719 
720         // relief mode
721         writePod(outf,
722                  (uInt8)0);
723 
724         // CJK language
725         writePod(outf,
726                  (uInt16)0);
727 
728         // vertical writing mode
729         writePod(outf,
730                  (uInt8)0);
731 
732         // emphasis marks (esp. for CJK languages)
733         writePod(outf,
734                  (uInt16)0);
735 
736         ++actionCount;
737     }
738 
739     // write MetaTextColorAction
740     writePod(outf,
741              (uInt16)META_TEXTCOLOR_ACTION);
742     fakeVersionCompat(outf, 1, 0);
743     writePod(outf,
744              (uInt8)(textinfo.currentB*255 + .5));
745     writePod(outf,
746              (uInt8)(textinfo.currentG*255 + .5));
747     writePod(outf,
748              (uInt8)(textinfo.currentR*255 + .5));
749     writePod(outf,(uInt8)0); // dummy
750 
751     ++actionCount;
752 
753     // write text
754     if( textinfo.thetext.c_str() )
755     {
756         writePod(outf,
757                  (uInt16)META_TEXT_ACTION);
758         fakeVersionCompat(outf, 1, 0);
759         writePod(outf,
760                  (uInt32)l_transX(textinfo.x));
761         writePod(outf,
762                  (uInt32)l_transY(textinfo.y));
763 
764         const size_t textLen = strlen(textinfo.thetext.c_str());
765         writePod(outf,
766                  (uInt16)textLen);
767         outf.write(textinfo.thetext.c_str(),
768                    textLen);
769         writePod(outf,
770                  (uInt16)0);
771         writePod(outf,
772                  (uInt16)textLen);
773 
774         // TODO(F1): SVM is Unicode-capable, yank version compat above
775         // to 2 and write out UTF-16 string here
776 
777         ++actionCount;
778     }
779 }
780 
781 
show_image(const PSImage & image)782 void drvSVM::show_image(const PSImage& image)
783 {
784 	// first retrieve bounding box
785 	Point lowerLeft, upperRight;
786 	image.getBoundingBox(lowerLeft, upperRight);
787 
788 	const Int32 width  = abs(l_transX(upperRight.x_) -
789                              l_transX(lowerLeft.x_));
790 	const Int32 height = abs(l_transY(upperRight.y_) -
791                              l_transY(lowerLeft.y_));
792 
793 	// calc long-padded size of scanline
794 	const long int scanlineLen = ((width * 3) + 3) & ~3L;
795 	const long int maskScanlineLen = ((((width + 7) & ~7L) >> 3L) + 3) & ~3L;
796 
797 	// now lets get some mem
798 	unsigned char* const output = new unsigned char[scanlineLen * height];
799 	output[0] = 0; // init for coverity
800 	unsigned char* const outputMask = new unsigned char[maskScanlineLen * height];
801 	outputMask[0] = 0; // init for coverity
802 
803 	// setup inverse transformation matrix
804 	const float matrixScale(image.normalizedImageCurrentMatrix[0] *
805 							image.normalizedImageCurrentMatrix[3] -
806 							image.normalizedImageCurrentMatrix[2] *
807 							image.normalizedImageCurrentMatrix[1]);
808 	const float inverseMatrix[] = {
809 		image.normalizedImageCurrentMatrix[3] / matrixScale,
810 		-image.normalizedImageCurrentMatrix[1] / matrixScale,
811 		-image.normalizedImageCurrentMatrix[2] / matrixScale,
812 		image.normalizedImageCurrentMatrix[0] / matrixScale,
813 		(image.normalizedImageCurrentMatrix[2] *
814 		 image.normalizedImageCurrentMatrix[5] -
815 		 image.normalizedImageCurrentMatrix[4] *
816 		 image.normalizedImageCurrentMatrix[3]) / matrixScale,
817 		(image.normalizedImageCurrentMatrix[4] *
818 		 image.normalizedImageCurrentMatrix[1] -
819 		 image.normalizedImageCurrentMatrix[0] *
820 		 image.normalizedImageCurrentMatrix[5]) / matrixScale
821 	};
822 
823     if (Verbose())
824         errf << "Image matrix: "
825              << "0: " << image.normalizedImageCurrentMatrix[0] << " "
826              << "1: " << image.normalizedImageCurrentMatrix[1] << " "
827              << "2: " << image.normalizedImageCurrentMatrix[2] << " "
828              << "3: " << image.normalizedImageCurrentMatrix[3] << " "
829              << "4: " << image.normalizedImageCurrentMatrix[4] << " "
830              << "5: " << image.normalizedImageCurrentMatrix[5] << " "
831              << endl;
832 
833     // TODO(F2): interpolate
834     // TODO(P3): avoid transformation _at all_ if scale and transform suffices
835     // TODO(P3): avoid mask for the obvious cases
836 
837 	// now transform image
838 	for (long int y=0; y < height; y++) {
839 		unsigned char* currOutput = &output[scanlineLen * y];
840 		unsigned char* currMaskOutput = &outputMask[maskScanlineLen * y] - 1;
841 
842 		for (long int x=0; x < width; x++) {
843 			// now transform from device coordinate space to image space
844 			const Point& currPoint( Point(x + lowerLeft.x_,
845 										  y + lowerLeft.y_).transform(inverseMatrix) );
846 
847 			// round to integers
848 			const long int sourceX = (long int) (currPoint.x_ + .5);
849 			const long int sourceY = (long int) (currPoint.y_ + .5);
850 
851 			// is the pixel within source bitmap bounds?
852 			if (sourceX >= 0L && (unsigned long) sourceX < image.width &&
853 				sourceY >= 0L && (unsigned long) sourceY < image.height) {
854 				// okay, fetch source pixel value into
855 				// RGB triplet
856 
857 				unsigned char r(255), g(255), b(255);
858 
859 				// how many components?
860 				switch (image.ncomp) {
861 				case 1:
862 					r = g = b = image.getComponent(sourceX, sourceY, 0);
863 					break;
864 
865 				case 3:
866 					r = image.getComponent(sourceX, sourceY, 0);
867 					g = image.getComponent(sourceX, sourceY, 1);
868 					b = image.getComponent(sourceX, sourceY, 2);
869 					break;
870 
871 				case 4: {
872 					unsigned char C = image.getComponent(sourceX, sourceY, 0);
873 					unsigned char M = image.getComponent(sourceX, sourceY, 1);
874 					unsigned char Y = image.getComponent(sourceX, sourceY, 2);
875 					unsigned char K = image.getComponent(sourceX, sourceY, 3);
876 
877 					// account for key
878 					C += K;
879 					M += K;
880 					Y += K;
881 
882 					// convert color
883 					r = 255 - C;
884 					g = 255 - M;
885 					b = 255 - Y;
886 					}
887 					break;
888 
889                     default:
890                         assert (0 && "Unexpected bitmap format");
891                         break;
892 				}
893 
894 				// set color triple
895 				*currOutput++ = b;
896 				*currOutput++ = g;
897 				*currOutput++ = r;
898 
899                 if( (x & 7L) == 0 )
900                     ++currMaskOutput;
901 
902                 // set mask to opaque
903                 *currMaskOutput &= ~(1L << (7L - (x & 7L)));
904 			}
905             else
906             {
907                 // pixel outside source bitmap bounds - set to
908                 // white/transparent
909 				*currOutput++ = 255;
910 				*currOutput++ = 255;
911 				*currOutput++ = 255;
912 
913                 if( (x & 7L) == 0 )
914                     ++currMaskOutput;
915 
916                 // set mask to transparent
917                 *currMaskOutput |= 1L << (7L - (x & 7L));
918             }
919 		}
920 	}
921 
922     // write BitmapEx action
923     writePod(outf,
924              (uInt16)META_BMPEXSCALE_ACTION);
925     fakeVersionCompat(outf, 1, 0);
926 
927     // write bitmap
928     // ------------
929 
930     // file header
931 	writePod(outf,
932              (uInt16)0x4D42);
933 	writePod(outf,
934              (uInt32)(14 + 40 + scanlineLen*height));
935 	writePod(outf,
936              (uInt16)0);
937 	writePod(outf,
938              (uInt16)0);
939 	writePod(outf,
940              (uInt32)14 + 40);
941 
942     // info header
943 	writePod(outf,
944              (uInt32)40);
945 	writePod(outf,
946              (uInt32)width);
947 	writePod(outf,
948              (uInt32)height);
949 	writePod(outf,
950              (uInt16)1);
951 	writePod(outf,
952              (uInt16)24);
953 	writePod(outf,
954              (uInt32)0);
955 	writePod(outf,
956              (uInt32)0);
957 	writePod(outf,
958              (uInt32)0);
959 	writePod(outf,
960              (uInt32)0);
961 	writePod(outf,
962              (uInt32)0);
963 	writePod(outf,
964              (uInt32)0);
965 
966     // actual bitmap data
967     outf.write( (char*)output,
968                 scanlineLen*height );
969 
970     // magics
971 	writePod(outf,
972              (uInt32)0x25091962);
973 	writePod(outf,
974              (uInt32)0xACB20201);
975 	writePod(outf,
976              (uInt8)2); // bitmap with mask flag
977 
978 
979     // write mask bitmap
980     // -----------------
981 
982     // file header
983 	writePod(outf,
984              (uInt16)0x4D42);
985 	writePod(outf,
986              (uInt32)(14 + 40 + 8 + maskScanlineLen*height));
987 	writePod(outf,
988              (uInt16)0);
989 	writePod(outf,
990              (uInt16)0);
991 	writePod(outf,
992              (uInt32)14 + 40 + 8);
993 
994     // info header
995 	writePod(outf,
996              (uInt32)40);
997 	writePod(outf,
998              (uInt32)width);
999 	writePod(outf,
1000              (uInt32)height);
1001 	writePod(outf,
1002              (uInt16)1);
1003 	writePod(outf,
1004              (uInt16)1);
1005 	writePod(outf,
1006              (uInt32)0);
1007 	writePod(outf,
1008              (uInt32)0);
1009 	writePod(outf,
1010              (uInt32)0);
1011 	writePod(outf,
1012              (uInt32)0);
1013 	writePod(outf,
1014              (uInt32)2);
1015 	writePod(outf,
1016              (uInt32)2);
1017 
1018     // bitmap palette: black and white
1019     writePod(outf,
1020              (uInt32)0);
1021     writePod(outf,
1022              (uInt32)0x00FFFFFF);
1023 
1024     // actual bitmap data
1025     outf.write( (char*)outputMask,
1026                 maskScanlineLen*height );
1027 
1028     // output position
1029     writePod(outf,
1030              (Int32)l_transX(lowerLeft.x_));
1031     writePod(outf,
1032              (Int32)l_transY(upperRight.y_));
1033 
1034     // output scale
1035     writePod(outf,
1036              (Int32)width);
1037     writePod(outf,
1038              (Int32)height);
1039 
1040     ++actionCount;
1041 
1042 	delete[] output;
1043 	delete[] outputMask;
1044 }
1045 
1046 
1047 static DriverDescriptionT < drvSVM > D_svm("svm",
1048                                            "StarView/OpenOffice.org metafile",
1049                                            "StarView/OpenOffice.org metafile, readable from OpenOffice.org 1.0/StarOffice 6.0 and above.",
1050                                            "svm",
1051                                            true,	// backend supports subpathes
1052 										   true,	// backend does support curves
1053 										   true,	// backend supports elements which are filled and have edges
1054 										   true,	// backend supports text
1055 										   DriverDescription::memoryeps,  // no support for PNG file images
1056 										   DriverDescription::normalopen, // we open output file ourselves
1057 										   false,	// if format supports multiple pages in one file (DEFINETELY not)
1058 										   true     // clipping
1059 										   );
1060 
1061