1 /* -*- mode: c++ -*- */
2 /**
3  * @file   Board.cpp
4  * @author Sebastien Fourey (GREYC)
5  * @date   Aug 2007
6  *
7  * @brief
8  * \@copyright
9  * This source code is part of the Board project, a C++ library whose
10  * purpose is to allow simple drawings in EPS, FIG or SVG files.
11  * Copyright (C) 2007 Sebastien Fourey <http://foureys.users.greyc.fr/>
12  *
13  * This program is free software: you can redistribute it and/or modify
14  * it under the terms of the GNU Lesser General Public License as published
15  * by the Free Software Foundation, either version 3 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU Lesser General Public License for more details
22  *
23  * You should have received a copy of the GNU Lesser General Public License
24  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
25  */
26 
27 #include "Board.h"
28 #include "board/Point.h"
29 #include "board/Rect.h"
30 #include "board/Shapes.h"
31 #include "board/Tools.h"
32 #include "board/PSFonts.h"
33 #include <fstream>
34 #include <iostream>
35 #include <iomanip>
36 #include <typeinfo>
37 #include <ctime>
38 #include <cstring>
39 #include <map>
40 #include <algorithm>
41 #include <cstdio>
42 #include <algorithm>
43 
44 #if defined( max )
45 #undef max
46 #endif
47 
48 namespace {
49 
50 const float pageSizes[][2] = { { 0.0f, 0.0f },               // BoundingBox
51                                { 841.0f, 1189.0f },          // A0
52                                { 594.0f, 841.0f },           // A1
53                                { 420.0f, 594.0f },           // A2
54                                { 297.0f, 420.0f },           // A3
55                                { 210.0f, 297.0f },           // A4
56                                { 148.0f, 210.0f },           // A5
57                                { 105.0f, 148.0f },           // A6
58                                { 74.0f, 105.0f },            // A7
59                                { 52.0f, 74.0f },             // A8
60                                { 37.0f, 52.0f },             // A9
61                                { 26.0f, 37.0f },             // A10
62                                { 8.5f*25.4f, 11.0f*25.4f },  // Letter
63                                { 8.5f*25.4f, 14.0f*25.4f },  // Legal
64                                { 7.2f*25.4f, 10.5f*25.4f }   // Executive
65                              };
66 
67 const float ppmm = 72.0f / 25.4f;
68 }
69 
70 namespace LibBoard {
71 
72 const double Board::Degree =  3.14159265358979323846 / 180.0;
73 
State()74 Board::State::State()
75 {
76   penColor = Color::Black;
77   fillColor =  Color::Null;
78   lineWidth = 0.5;
79   lineStyle = Shape::SolidStyle;
80   lineCap = Shape::ButtCap;
81   lineJoin = Shape::MiterJoin;
82   font = Fonts::TimesRoman;
83   fontSize = 11.0;
84 }
85 
Board(const Color & backgroundColor)86 Board::Board( const Color & backgroundColor )
87   : _backgroundColor( backgroundColor )
88 {
89 }
90 
Board(const Board & other)91 Board::Board( const Board & other )
92   : ShapeList( other ),
93     _state( other._state ),
94     _backgroundColor( other._backgroundColor )
95 {
96 }
97 
98 Board &
operator =(const Board & other)99 Board::operator=( const Board & other )
100 {
101   free();
102   if ( ! other._shapes.size() ) return (*this);
103   _shapes.resize( other._shapes.size(), 0 );
104   std::vector<Shape*>::iterator t = _shapes.begin();
105   std::vector<Shape*>::const_iterator i = other._shapes.begin();
106   std::vector<Shape*>::const_iterator end = other._shapes.end();
107   while ( i != end ) {
108     *t = (*i)->clone();
109     ++i; ++t;
110   }
111   return *this;
112 }
113 
114 Board &
operator <<(const Shape & shape)115 Board::operator<<( const Shape & shape )
116 {
117   ShapeList::addShape( shape, 1.0 );
118   return *this;
119 }
120 
~Board()121 Board::~Board()
122 {
123 }
124 
125 void
clear(const Color & color)126 Board::clear( const Color & color )
127 {
128   ShapeList::clear();
129   _backgroundColor = color;
130 }
131 
132 Board &
rotate(double angle,const Point & center)133 Board::rotate( double angle, const Point & center )
134 {
135   ShapeList::rotate( angle, center );
136   _clippingPath.rotate( angle, center );
137   return (*this);
138 }
139 
140 Board &
rotate(double angle)141 Board::rotate( double angle )
142 {
143   ShapeList::rotate( angle );
144   _clippingPath.rotate( angle, center() );
145   return (*this);
146 }
147 
148 Board &
translate(double dx,double dy)149 Board::translate( double dx, double dy )
150 {
151   ShapeList::translate( dx, dy );
152   _clippingPath.translate( dx, dy );
153   return (*this);
154 }
155 
156 Board &
scale(double sx,double sy)157 Board::scale( double sx, double sy )
158 {
159   if ( _clippingPath.size() ) {
160     Point delta = _clippingPath.center() - center();
161     delta.x *= sx;
162     delta.y *= sy;
163     _clippingPath.scale( sx, sy );
164     ShapeList::scale( sx, sy );
165     delta = ( center() + delta ) - _clippingPath.center();
166     _clippingPath.translate( delta.x, delta.y );
167   } else {
168     ShapeList::scale( sx, sy );
169   }
170   return (*this);
171 }
172 
173 Board &
scale(double s)174 Board::scale( double s )
175 {
176   if ( _clippingPath.size() ) {
177     Point delta = _clippingPath.center() - center();
178     delta *= s;
179     _clippingPath.scale( s );
180     ShapeList::scale( s );
181     delta = ( center() + delta ) - _clippingPath.center();
182     _clippingPath.translate( delta.x, delta.y );
183   } else {
184     ShapeList::scale( s );
185   }
186   return (*this);
187 }
188 
189 Board
rotated(double angle,const Point & center)190 Board::rotated( double angle, const Point & center )
191 {
192   return static_cast<const Board &>( Board( *this ).rotate( angle, center ) );
193 }
194 
195 Board
rotated(double angle)196 Board::rotated( double angle )
197 {
198   return static_cast<const Board &>( Board( *this ).rotate( angle ) );
199 }
200 
201 Board
translated(double dx,double dy)202 Board::translated( double dx, double dy )
203 {
204   return static_cast<const Board &>( Board( *this ).translate( dx, dy ) );
205 }
206 
207 Board
scaled(double sx,double sy)208 Board::scaled( double sx, double sy )
209 {
210   return static_cast<const Board &>( Board( *this ).scale( sx, sy ) );
211 }
212 
213 Board
scaled(double s)214 Board::scaled( double s )
215 {
216   return static_cast<const Board &>( Board( *this ).scale( s ) );
217 }
218 
219 Board &
setPenColorRGBi(unsigned char red,unsigned char green,unsigned char blue,unsigned char alpha)220 Board::setPenColorRGBi( unsigned char red,
221                         unsigned char green,
222                         unsigned char blue,
223                         unsigned char alpha )
224 {
225   _state.penColor.setRGBi( red, green, blue, alpha );
226   return *this;
227 }
228 
229 Board &
setPenColorRGBf(float red,float green,float blue,float alpha)230 Board::setPenColorRGBf(  float red,
231                          float green,
232                          float blue,
233                          float alpha )
234 {
235   _state.penColor.setRGBf( red, green, blue, alpha );
236   return *this;
237 }
238 
239 Board &
setPenColor(const Color & color)240 Board::setPenColor( const Color & color )
241 {
242   _state.penColor = color;
243   return *this;
244 }
245 
246 Board &
setFillColorRGBi(unsigned char red,unsigned char green,unsigned char blue,unsigned char alpha)247 Board::setFillColorRGBi( unsigned char red,
248                          unsigned char green,
249                          unsigned char blue,
250                          unsigned char alpha )
251 {
252   _state.fillColor.setRGBi( red, green, blue, alpha );
253   return *this;
254 }
255 
256 Board &
setFillColorRGBf(float red,float green,float blue,float alpha)257 Board::setFillColorRGBf( float red, float green, float blue, float alpha )
258 {
259   _state.fillColor.setRGBf( red, green, blue, alpha );
260   return *this;
261 }
262 
263 Board &
setFillColor(const Color & color)264 Board::setFillColor( const Color & color )
265 {
266   _state.fillColor = color;
267   return *this;
268 }
269 
270 Board &
setLineWidth(double width)271 Board::setLineWidth( double width )
272 {
273   _state.lineWidth = width;
274   return *this;
275 }
276 
277 Board &
setFont(const Fonts::Font font,double fontSize)278 Board::setFont( const Fonts::Font font, double fontSize )
279 {
280   _state.font = font;
281   _state.fontSize = fontSize;
282   return *this;
283 }
284 
285 Board &
setFontSize(double fontSize)286 Board::setFontSize( double fontSize )
287 {
288   _state.fontSize = fontSize;
289   return *this;
290 }
291 
292 void
backgroundColor(const Color & color)293 Board::backgroundColor( const Color & color )
294 {
295   _backgroundColor = color;
296 }
297 
298 void
drawDot(double x,double y,int depth)299 Board::drawDot( double x, double y, int depth )
300 {
301   if ( depth != -1 )
302     _shapes.push_back( new Dot( x, y, _state.penColor, _state.lineWidth, depth ) );
303   else
304     _shapes.push_back( new Dot( x, y, _state.penColor, _state.lineWidth, _nextDepth-- ) );
305 }
306 
307 void
drawLine(double x1,double y1,double x2,double y2,int depth)308 Board::drawLine( double x1, double y1, double x2, double y2,
309                  int depth /* = -1 */  )
310 {
311   if ( depth != -1 )
312     _shapes.push_back( new Line( x1, y1,
313                                  x2, y2,
314                                  _state.penColor, _state.lineWidth,
315                                  _state.lineStyle, _state.lineCap,
316                                  _state.lineJoin, depth ) );
317   else
318     _shapes.push_back( new Line( x1, y1,
319                                  x2, y2,
320                                  _state.penColor, _state.lineWidth,
321                                  _state.lineStyle, _state.lineCap,
322                                  _state.lineJoin, _nextDepth-- ) );
323 }
324 
325 void
drawLine(Point p,Point q,int depth)326 Board::drawLine( Point p, Point q, int depth /* = -1 */  )
327 {
328   if ( depth != -1 )
329     _shapes.push_back( new Line( p.x, p.y,
330                                  q.x, q.y,
331                                  _state.penColor, _state.lineWidth,
332                                  _state.lineStyle, _state.lineCap,
333                                  _state.lineJoin, depth ) );
334   else
335     _shapes.push_back( new Line( p.x, p.y,
336                                  q.x, q.y,
337                                  _state.penColor, _state.lineWidth,
338                                  _state.lineStyle, _state.lineCap,
339                                  _state.lineJoin, _nextDepth-- ) );
340 }
341 
342 void
drawArrow(double x1,double y1,double x2,double y2,int depth)343 Board::drawArrow( double x1, double y1, double x2, double y2, int depth /* = -1 */  )
344 {
345   if ( depth != -1 )
346     _shapes.push_back( new Arrow( x1, y1,
347                                   x2, y2,
348                                   _state.penColor,
349                                   _state.fillColor,
350                                   _state.lineWidth, _state.lineStyle,
351                                   _state.lineCap, _state.lineJoin, depth ) );
352   else
353     _shapes.push_back( new Arrow( x1, y1,
354                                   x2, y2,
355                                   _state.penColor,
356                                   _state.fillColor,
357                                   _state.lineWidth, _state.lineStyle,
358                                   _state.lineCap, _state.lineJoin,
359                                   _nextDepth-- ) );
360 }
361 
362 void
drawArrow(Point p,Point q,int depth)363 Board::drawArrow( Point p, Point q, int depth /* = -1 */  )
364 {
365   if ( depth != -1 )
366     _shapes.push_back( new Arrow( p.x, p.y,
367                                   q.x, q.y,
368                                   _state.penColor,
369                                   _state.fillColor,
370                                   _state.lineWidth, _state.lineStyle,
371                                   _state.lineCap, _state.lineJoin, depth ) );
372   else
373     _shapes.push_back( new Arrow( p.x, p.y,
374                                   q.x, q.y,
375                                   _state.penColor,
376                                   _state.fillColor,
377                                   _state.lineWidth, _state.lineStyle,
378                                   _state.lineCap, _state.lineJoin,
379                                   _nextDepth-- ) );
380 }
381 
382 void
drawRectangle(double left,double top,double width,double height,int depth)383 Board::drawRectangle( double left, double top,
384                       double width, double height,
385                       int depth /* = -1 */ )
386 {
387   int d = (depth!=-1) ? depth : _nextDepth--;
388   _shapes.push_back( new Rectangle( left,
389                                     top,
390                                     width,
391                                     height,
392                                     _state.penColor, _state.fillColor,
393                                     _state.lineWidth, _state.lineStyle,
394                                     _state.lineCap, _state.lineJoin, d ) );
395 }
396 
397 void
drawRectangle(const Rect & r,int depth)398 Board::drawRectangle(const Rect & r, int depth)
399 {
400   int d = (depth!=-1) ? depth : _nextDepth--;
401   _shapes.push_back( new Rectangle( r.left,
402                                     r.top,
403                                     r.width,
404                                     r.height,
405                                     _state.penColor, _state.fillColor,
406                                     _state.lineWidth, _state.lineStyle,
407                                     _state.lineCap, _state.lineJoin, d ) );
408 }
409 
410 void
fillRectangle(double left,double top,double width,double height,int depth)411 Board::fillRectangle( double left, double top,
412                       double width, double height,
413                       int depth /* = -1 */ )
414 {
415   int d = (depth!=-1) ? depth : _nextDepth--;
416   _shapes.push_back( new Rectangle( left,
417                                     top,
418                                     width,
419                                     height,
420                                     Color::Null, _state.penColor,
421                                     0.0f, _state.lineStyle,
422                                     _state.lineCap, _state.lineJoin,
423                                     d ) );
424 }
425 
426 void
fillRectangle(const Rect & r,int depth)427 Board::fillRectangle(const Rect & r, int depth)
428 {
429   int d = (depth!=-1) ? depth : _nextDepth--;
430   _shapes.push_back( new Rectangle( r.left,
431                                     r.top,
432                                     r.width,
433                                     r.height,
434                                     Color::Null, _state.penColor,
435                                     0.0f, _state.lineStyle,
436                                     _state.lineCap, _state.lineJoin,
437                                     d ) );
438 }
439 
440 void
drawCircle(double x,double y,double radius,int depth)441 Board::drawCircle( double x, double y, double radius,
442                    int depth /* = -1 */  )
443 {
444   int d = (depth!=-1) ? depth : _nextDepth--;
445   _shapes.push_back( new Circle( x, y,
446                                  radius,
447                                  _state.penColor, _state.fillColor,
448                                  _state.lineWidth, _state.lineStyle, d ) );
449 }
450 
451 void
fillCircle(double x,double y,double radius,int depth)452 Board::fillCircle( double x, double y, double radius,
453                    int depth /* = -1 */ )
454 {
455   int d = (depth!=-1) ? depth : _nextDepth--;
456   _shapes.push_back( new Circle( x, y, radius,
457                                  Color::Null, _state.penColor,
458                                  0.0f, _state.lineStyle, d ) );
459 }
460 
461 void
drawEllipse(double x,double y,double xRadius,double yRadius,int depth)462 Board::drawEllipse( double x, double y,
463                     double xRadius, double yRadius,
464                     int depth /* = -1 */  )
465 {
466   int d = (depth!=-1) ? depth : _nextDepth--;
467   _shapes.push_back( new Ellipse( x, y,
468                                   xRadius, yRadius,
469                                   _state.penColor,
470                                   _state.fillColor,
471                                   _state.lineWidth,
472                                   _state.lineStyle,
473                                   d ) );
474 }
475 
476 void
fillEllipse(double x,double y,double xRadius,double yRadius,int depth)477 Board::fillEllipse( double x, double y,
478                     double xRadius, double yRadius,
479                     int depth /* = -1 */ )
480 {
481   int d = depth ? depth : _nextDepth--;
482   _shapes.push_back( new Ellipse( x, y,
483                                   xRadius, yRadius,
484                                   Color::Null,
485                                   _state.penColor,
486                                   0.0f,
487                                   _state.lineStyle,
488                                   d ) );
489 }
490 
491 void
drawPolyline(const std::vector<Point> & points,int depth)492 Board::drawPolyline( const std::vector<Point> & points,
493                      int depth /* = -1 */ )
494 {
495   int d = (depth!=-1) ? depth : _nextDepth--;
496   _shapes.push_back( new Polyline( points,
497                                    false,
498                                    _state.penColor,
499                                    _state.fillColor,
500                                    _state.lineWidth,
501                                    _state.lineStyle,
502                                    _state.lineCap,
503                                    _state.lineJoin,
504                                    d ) );
505 }
506 
507 void
drawClosedPolyline(const std::vector<Point> & points,int depth)508 Board::drawClosedPolyline( const std::vector<Point> & points,
509                            int depth /* = -1 */ )
510 {
511   int d = (depth!=-1) ? depth : _nextDepth--;
512   _shapes.push_back( new Polyline( points, true, _state.penColor, _state.fillColor,
513                                    _state.lineWidth,
514                                    _state.lineStyle,
515                                    _state.lineCap,
516                                    _state.lineJoin,
517                                    d ) );
518 }
519 
520 void
fillPolyline(const std::vector<Point> & points,int depth)521 Board::fillPolyline( const std::vector<Point> & points,
522                      int depth /* = -1 */ )
523 {
524   int d = (depth!=-1) ? depth : _nextDepth--;
525   _shapes.push_back( new Polyline( points, true, Color::Null, _state.penColor,
526                                    0.0f,
527                                    _state.lineStyle,
528                                    _state.lineCap,
529                                    _state.lineJoin,
530                                    d ) );
531 }
532 
533 void
drawTriangle(double x1,double y1,double x2,double y2,double x3,double y3,int depth)534 Board::drawTriangle( double x1, double y1,
535                      double x2, double y2,
536                      double x3, double y3,
537                      int depth /* = -1 */ )
538 {
539   int d = (depth!=-1) ? depth : _nextDepth--;
540   std::vector<Point> points;
541   points.push_back( Point( x1, y1 ) );
542   points.push_back( Point( x2, y2 ) );
543   points.push_back( Point( x3, y3 ) );
544   _shapes.push_back( new Polyline( points, true, _state.penColor, _state.fillColor,
545                                    _state.lineWidth,
546                                    _state.lineStyle,
547                                    _state.lineCap,
548                                    _state.lineJoin,
549                                    d ) );
550 }
551 
552 void
drawTriangle(const Point & p1,const Point & p2,const Point & p3,int depth)553 Board::drawTriangle( const Point & p1,
554                      const Point & p2,
555                      const Point & p3,
556                      int depth /* = -1 */ )
557 {
558   int d = (depth!=-1) ? depth : _nextDepth--;
559   std::vector<Point> points;
560   points.push_back( Point( p1.x, p1.y ) );
561   points.push_back( Point( p2.x, p2.y ) );
562   points.push_back( Point( p3.x, p3.y ) );
563   _shapes.push_back( new Polyline( points, true,
564                                    _state.penColor, _state.fillColor,
565                                    _state.lineWidth,
566                                    _state.lineStyle,
567                                    _state.lineCap,
568                                    _state.lineJoin,
569                                    d ) );
570 }
571 
572 void
fillTriangle(double x1,double y1,double x2,double y2,double x3,double y3,int depth)573 Board::fillTriangle( double x1, double y1,
574                      double x2, double y2,
575                      double x3, double y3,
576                      int depth /* = -1 */ )
577 {
578   int d = (depth!=-1) ? depth : _nextDepth--;
579   std::vector<Point> points;
580   points.push_back( Point( x1, y1 ) );
581   points.push_back( Point( x2, y2 ) );
582   points.push_back( Point( x3, y3 ) );
583   _shapes.push_back( new Polyline( points, true, Color::Null, _state.penColor,
584                                    0.0f,
585                                    _state.lineStyle,
586                                    _state.lineCap,
587                                    _state.lineJoin,
588                                    d ) );
589 }
590 
591 void
fillTriangle(const Point & p1,const Point & p2,const Point & p3,int depth)592 Board::fillTriangle( const Point & p1,
593                      const Point & p2,
594                      const Point & p3,
595                      int depth /* = -1 */ )
596 {
597   int d = (depth!=-1) ? depth : _nextDepth--;
598   std::vector<Point> points;
599   points.push_back( Point( p1.x, p1.y ) );
600   points.push_back( Point( p2.x, p2.y ) );
601   points.push_back( Point( p3.x, p3.y ) );
602   _shapes.push_back( new Polyline( points, true, Color::Null, _state.penColor,
603                                    0.0f,
604                                    _state.lineStyle,
605                                    _state.lineCap,
606                                    _state.lineJoin,
607                                    d ) );
608 }
609 
610 void
fillGouraudTriangle(const Point & p1,const Color & color1,const Point & p2,const Color & color2,const Point & p3,const Color & color3,unsigned char divisions,int depth)611 Board::fillGouraudTriangle( const Point & p1,
612                             const Color & color1,
613                             const Point & p2,
614                             const Color & color2,
615                             const Point & p3,
616                             const Color & color3,
617                             unsigned char divisions,
618                             int depth /* = -1 */ )
619 {
620   int d = (depth!=-1) ? depth : _nextDepth--;
621   _shapes.push_back( new GouraudTriangle( p1, color1,
622                                           p2, color2,
623                                           p3, color3,
624                                           divisions, d ) );
625 }
626 
627 void
fillGouraudTriangle(const Point & p1,const float brightness1,const Point & p2,const float brightness2,const Point & p3,const float brightness3,unsigned char divisions,int depth)628 Board::fillGouraudTriangle( const Point & p1,
629                             const float brightness1,
630                             const Point & p2,
631                             const float brightness2,
632                             const Point & p3,
633                             const float brightness3,
634                             unsigned char divisions,
635                             int depth /* = -1 */ )
636 {
637   Color color1( _state.penColor );
638   Color color2( _state.penColor );
639   Color color3( _state.penColor );
640   color1.red( static_cast<unsigned char>( std::min( 255.0f, color1.red() * brightness1 ) ) );
641   color1.green( static_cast<unsigned char>( std::min( 255.0f, color1.green() * brightness1 ) ) );
642   color1.blue( static_cast<unsigned char>( std::min( 255.0f, color1.blue() * brightness1 ) ) );
643   color2.red( static_cast<unsigned char>( std::min( 255.0f, color2.red() * brightness2 ) ) );
644   color2.green( static_cast<unsigned char>( std::min( 255.0f, color2.green() * brightness2 ) ) );
645   color2.blue( static_cast<unsigned char>( std::min( 255.0f, color2.blue() * brightness2 ) ) );
646   color3.red( static_cast<unsigned char>( std::min( 255.0f, color3.red() * brightness3 ) ) );
647   color3.green( static_cast<unsigned char>( std::min( 255.0f, color3.green() * brightness3 ) ) );
648   color3.blue( static_cast<unsigned char>( std::min( 255.0f, color3.blue() * brightness3 ) ) );
649   fillGouraudTriangle( p1, color1,
650                        p2, color2,
651                        p3, color3,
652                        divisions,
653                        depth );
654 }
655 
656 void
drawText(double x,double y,const char * text,int depth)657 Board::drawText( double x, double y, const char * text, int depth /* = -1 */ )
658 {
659   int d = (depth!=-1) ? depth : _nextDepth--;
660   _shapes.push_back( new Text( x, y, text,
661                                _state.font, _state.fontSize,
662                                _state.penColor, d ) );
663 }
664 
drawText(Point p,const char * text,int depth)665 void Board::drawText( Point p, const char *text, int depth )
666 {
667   int d = (depth!=-1) ? depth : _nextDepth--;
668   _shapes.push_back( new Text( p, text, _state.font, _state.fontSize, _state.penColor, d ) );
669 }
670 
671 void
drawText(double x,double y,const std::string & str,int depth)672 Board::drawText( double x, double y, const std::string & str, int depth /* = -1 */ )
673 {
674   int d = (depth!=-1) ? depth : _nextDepth--;
675   _shapes.push_back( new Text( x,
676                                y,
677                                str,
678                                _state.font,
679                                _state.fontSize,
680                                _state.penColor, d ) );
681 }
682 
drawText(Point p,const std::string & str,int depth)683 void Board::drawText( Point p, const std::string & str, int depth )
684 {
685   int d = (depth!=-1) ? depth : _nextDepth--;
686   _shapes.push_back( new Text( p,
687                                str,
688                                _state.font,
689                                _state.fontSize,
690                                _state.penColor, d ) );
691 }
692 
693 void
drawBoundingBox(LineWidthFlag lineWidthFlag,int depth)694 Board::drawBoundingBox( LineWidthFlag lineWidthFlag, int depth )
695 {
696   int d = (depth!=-1) ? depth : _nextDepth--;
697   Rect bbox = boundingBox(lineWidthFlag);
698   _shapes.push_back( new Rectangle( bbox.left,
699                                     bbox.top,
700                                     bbox.width,
701                                     bbox.height,
702                                     _state.penColor,
703                                     _state.fillColor,
704                                     _state.lineWidth,
705                                     _state.lineStyle,
706                                     _state.lineCap,
707                                     _state.lineJoin,
708                                     d ) );
709 }
710 
711 void
setClippingRectangle(double x,double y,double width,double height)712 Board::setClippingRectangle( double x, double y, double width, double height )
713 {
714   _clippingPath.clear();
715   _clippingPath <<  Point( x, y  );
716   _clippingPath <<  Point( x + width, y  );
717   _clippingPath <<  Point( x + width, y - height );
718   _clippingPath <<  Point( x , y - height  );
719 }
720 
721 void
setClippingRectangle(const Rect & rect)722 Board::setClippingRectangle( const Rect & rect)
723 {
724   setClippingRectangle(rect.left,rect.top,rect.width,rect.height);
725 }
726 
727 void
setClippingPath(const std::vector<Point> & points)728 Board::setClippingPath(  const std::vector<Point> & points  )
729 {
730   _clippingPath.clear();
731   std::vector<Point>::const_iterator it = points.begin();
732   std::vector<Point>::const_iterator end = points.end();
733   while ( it != end ) {
734     _clippingPath <<  *it ;
735     ++it;
736   }
737 }
738 
739 void
setClippingPath(const Path & path)740 Board::setClippingPath(  const Path & path  )
741 {
742   _clippingPath = path;
743   _clippingPath.setClosed( true );
744   if ( _clippingPath.size() > 1 ) {
745     if ( _clippingPath[0] == _clippingPath[ _clippingPath.size() - 1 ] )
746       _clippingPath.pop_back();
747   }
748 }
749 
750 void
addDuplicates(const Shape & shape,std::size_t times,double dx,double dy,double scale)751 Board::addDuplicates( const Shape & shape, std::size_t times, double dx, double dy, double scale )
752 {
753   Shape * s = shape.clone();
754   while ( times-- ) {
755     (*this) << (*s);
756     if ( scale != 1.0 )
757       s->scale( scale );
758     s->translate( dx, dy );
759   }
760   delete s;
761 }
762 
763 void
addDuplicates(const Shape & shape,std::size_t times,double dx,double dy,double scaleX,double scaleY,double angle)764 Board::addDuplicates( const Shape & shape,
765                       std::size_t times,
766                       double dx, double dy,
767                       double scaleX, double scaleY,
768                       double angle )
769 {
770   Shape * s = shape.clone();
771   while ( times-- ) {
772     (*this) << (*s);
773     if ( scaleX != 1.0 || scaleY != 1.0 ) s->scale( scaleX, scaleY );
774     if ( dx != 0.0 || dy != 0.0 ) s->translate( dx, dy );
775     if ( angle != 0.0 ) s->rotate( angle );
776   }
777   delete s;
778 }
779 
780 void
saveEPS(const char * filename,PageSize size,double margin,Unit unit,const std::string & title) const781 Board::saveEPS( const char * filename, PageSize size, double margin, Unit unit, const std::string & title  ) const
782 {
783   if ( size == BoundingBox ) {
784     if ( title == std::string() )
785       saveEPS( filename, 0.0, 0.0, margin, unit, filename );
786     else
787       saveEPS( filename, 0.0, 0.0, margin, unit, title );
788   } else {
789     if ( title == std::string() )
790       saveEPS( filename, pageSizes[size][0], pageSizes[size][1], toMillimeter(margin,unit), UMillimeter, filename );
791     else
792       saveEPS( filename, pageSizes[size][0], pageSizes[size][1], toMillimeter(margin,unit), UMillimeter, title );
793   }
794 }
795 
796 void
saveEPS(std::ostream & out,PageSize size,double margin,Unit unit,const std::string & title) const797 Board::saveEPS(std::ostream & out, PageSize size, double margin, Unit unit, const std::string & title  ) const
798 {
799   if ( size == BoundingBox ) {
800     saveEPS( out, 0.0, 0.0, margin, unit, title );
801   } else {
802     saveEPS( out, pageSizes[size][0], pageSizes[size][1], toMillimeter(margin,unit), UMillimeter, title );
803   }
804 }
805 
806 void
saveEPS(std::ostream & out,double pageWidth,double pageHeight,double margin,Unit unit,const std::string & title) const807 Board::saveEPS( std::ostream & out, double pageWidth, double pageHeight, double margin, Unit unit, const std::string & title ) const
808 {
809   out << "%!PS-Adobe-2.0 EPSF-2.0" << std::endl;
810   out << "%%Title: " << title << std::endl;
811   out << "%%Creator: Board library (v" << _BOARD_VERSION_STRING_ << ") Copyleft 2007 Sebastien Fourey" << std::endl;
812   {
813     time_t t = time(0);
814     char str_time[255];
815     Tools::secured_ctime( str_time, &t, 255 );
816     out << "%%CreationDate: " << str_time;
817   }
818 
819   Rect bbox = boundingBox(UseLineWidth);
820   bool clipping = _clippingPath.size() > 2;
821   if ( clipping ) {
822     bbox = bbox && _clippingPath.boundingBox();
823   }
824   TransformEPS transform;
825   if ( pageWidth == 0.0 && pageHeight == 0.0 ) { // Fit to bounding box using given unit.
826     transform.setBoundingBox( bbox,
827                               toMillimeter(bbox.width,unit),
828                               toMillimeter(bbox.height,unit),
829                               -toMillimeter(margin,unit) );
830 
831     Rect page = transform.pageBoundingBox();
832     out << "%%BoundingBox: "
833         << std::setprecision( 8 )
834         << page.left << " "
835         << page.bottom() << " "
836         << page.right() << " "
837         << page.top << std::endl;
838 
839 
840   } else {
841     transform.setBoundingBox( bbox,
842                               toMillimeter(pageWidth,unit),
843                               toMillimeter(pageHeight,unit),
844                               toMillimeter(margin,unit) );
845     Rect page = transform.pageBoundingBox();
846     out << "%%BoundingBox: "
847         << std::setprecision( 8 )
848         << page.left << " "
849         << page.bottom() << " "
850         << page.right() << " "
851         << page.top << std::endl;
852   }
853 
854 
855   out << "%Magnification: 1.0000" << std::endl;
856   out << "%%EndComments" << std::endl;
857 
858   out << "\n"
859          "/cp {closepath} bind def\n"
860          "/ef {eofill} bind def\n"
861          "/gr {grestore} bind def\n"
862          "/gs {gsave} bind def\n"
863          "/sa {save} bind def\n"
864          "/rs {restore} bind def\n"
865          "/l {lineto} bind def\n"
866          "/m {moveto} bind def\n"
867          "/rm {rmoveto} bind def\n"
868          "/n {newpath} bind def\n"
869          "/s {stroke} bind def\n"
870          "/sh {show} bind def\n"
871          "/slc {setlinecap} bind def\n"
872          "/slj {setlinejoin} bind def\n"
873          "/slw {setlinewidth} bind def\n"
874          "/srgb {setrgbcolor} bind def\n"
875          "/rot {rotate} bind def\n"
876          "/sc {scale} bind def\n"
877          "/sd {setdash} bind def\n"
878          "/ff {findfont} bind def\n"
879          "/sf {setfont} bind def\n"
880          "/scf {scalefont} bind def\n"
881          "/sw {stringwidth} bind def\n"
882          "/sd {setdash} bind def\n"
883          "/tr {translate} bind def\n"
884          " 0.5 setlinewidth\n";
885 
886   if ( clipping ) {
887     out << " newpath ";
888     _clippingPath.flushPostscript( out, transform );
889     out << " 0 slw clip " << std::endl;
890   }
891 
892   // Draw the background color if needed.
893   if ( _backgroundColor != Color::Null ) {
894     Rectangle r( bbox, Color::Null, _backgroundColor, 0.0f );
895     r.flushPostscript( out, transform );
896   }
897 
898   // Draw the shapes
899   std::vector< Shape* > shapes = _shapes;
900 
901   stable_sort( shapes.begin(), shapes.end(), shapeGreaterDepth );
902   std::vector< Shape* >::const_iterator i = shapes.begin();
903   std::vector< Shape* >::const_iterator end = shapes.end();
904 
905   while ( i != end ) {
906     (*i)->flushPostscript( out, transform );
907     ++i;
908   }
909   out << "showpage" << std::endl;
910   out << "%%Trailer" << std::endl;
911   out << "%EOF" << std::endl;
912 }
913 
914 void
saveEPS(const char * filename,double pageWidth,double pageHeight,double margin,Unit unit,const std::string & title) const915 Board::saveEPS( const char * filename, double pageWidth, double pageHeight, double margin, Unit unit, const std::string & title ) const
916 {
917   std::ofstream out(filename);
918   saveEPS(out,pageWidth,pageHeight,margin,unit,title);
919   out.close();
920 }
921 
922 void
saveFIG(const char * filename,PageSize size,double margin,Unit unit) const923 Board::saveFIG( const char * filename, PageSize size, double margin, Unit unit ) const
924 {
925   if ( size == BoundingBox ) {
926     saveFIG( filename, 0.0, 0.0, margin, unit );
927   } else {
928     saveFIG( filename, pageSizes[size][0], pageSizes[size][1], toMillimeter(margin,unit), UMillimeter );
929   }
930 }
931 
932 void
saveFIG(std::ostream & out,PageSize size,double margin,Unit unit) const933 Board::saveFIG( std::ostream & out, PageSize size, double margin, Unit unit ) const
934 {
935   if ( size == BoundingBox ) {
936     saveFIG( out, 0.0, 0.0, margin, unit );
937   } else {
938     saveFIG( out, pageSizes[size][0], pageSizes[size][1], toMillimeter(margin,unit), UMillimeter );
939   }
940 }
941 
942 void
saveFIG(std::ostream & out,double pageWidth,double pageHeight,double margin,Unit unit) const943 Board::saveFIG( std::ostream & out, double pageWidth, double pageHeight, double margin, Unit unit ) const
944 {
945   Rect bbox = boundingBox(UseLineWidth);
946   TransformFIG transform;
947 
948   if ( pageWidth == 0.0 && pageHeight == 0.0 ) {
949     transform.setBoundingBox( bbox,
950                               toMillimeter(bbox.width,unit),
951                               toMillimeter(bbox.height,unit),
952                               -toMillimeter(margin,unit) );
953   } else {
954     transform.setBoundingBox( bbox,
955                               toMillimeter(pageWidth,unit),
956                               toMillimeter(pageHeight,unit),
957                               toMillimeter(margin,unit) );
958   }
959 
960   transform.setDepthRange( *this );
961 
962   out << "#FIG 3.2  Produced by the Board library (v"
963          _BOARD_VERSION_STRING_
964          ") Copyleft 2007 Sebastien Fourey\n";
965   out << "Portrait\n"
966          "Center\n"
967          "Metric\n"
968          "A4\n"
969          "100.00\n"
970          "Single\n"
971          "-2\n"
972          "1200 2\n";
973 
974   std::map<Color,int> colormap;
975   int maxColor = 32;
976 
977 
978   colormap[Color(0,0,0)] = 0;
979   colormap[Color(0,0,255)] = 1;
980   colormap[Color(0,255,0)] = 2;
981   colormap[Color(0,255,255)] = 0;
982   colormap[Color(255,0,0)] = 4;
983   colormap[Color(255,0,255)] = 0;
984   colormap[Color(255,255,0)] = 6;
985   colormap[Color(255,255,255)] = 7;
986 
987 
988   std::vector< Shape* > shapes = _shapes;
989   stable_sort( shapes.begin(), shapes.end(), shapeGreaterDepth );
990   std::vector< Shape* >::const_iterator i = shapes.begin();
991   std::vector< Shape* >::const_iterator end = shapes.end();
992   while ( i != end ) {
993     if ( colormap.find( (*i)->penColor() ) == colormap.end()
994          && (*i)->penColor().valid() )
995       colormap[ (*i)->penColor() ] = maxColor++;
996     if ( colormap.find( (*i)->fillColor() ) == colormap.end()
997          && (*i)->fillColor().valid() )
998       colormap[ (*i)->fillColor() ] = maxColor++;
999     ++i;
1000   }
1001 
1002   if ( colormap.find( _backgroundColor ) == colormap.end()
1003        && _backgroundColor.valid() )
1004     colormap[ _backgroundColor ] = maxColor++;
1005 
1006   // Write the colormap
1007   std::map<Color,int>::const_iterator iColormap = colormap.begin();
1008   std::map<Color,int>::const_iterator endColormap = colormap.end();
1009   char colorString[255];
1010   while ( iColormap != endColormap ) {
1011     secured_sprintf( colorString, 255,
1012                      "0 %d #%02x%02x%02x\n",
1013                      iColormap->second,
1014                      iColormap->first.red(),
1015                      iColormap->first.green(),
1016                      iColormap->first.blue() );
1017     if ( iColormap->second >= 32 ) out << colorString;
1018     ++iColormap;
1019   }
1020 
1021   // Draw the background color if needed.
1022   if ( _backgroundColor != Color::Null ) {
1023     Rectangle r( bbox, Color::Null, _backgroundColor, 0.0f );
1024     r.depth( std::numeric_limits<int>::max() );
1025     r.flushFIG( out, transform, colormap );
1026   }
1027 
1028   // Draw the shapes.
1029   i = shapes.begin();
1030   while ( i != end ) {
1031     (*i)->flushFIG( out, transform, colormap );
1032     ++i;
1033   }
1034 }
1035 
1036 void
saveFIG(const char * filename,double pageWidth,double pageHeight,double margin,Unit unit) const1037 Board::saveFIG( const char * filename, double pageWidth, double pageHeight, double margin, Unit unit ) const
1038 {
1039   std::ofstream out( filename );
1040   saveFIG(out,pageWidth,pageHeight,margin,unit);
1041   out.close();
1042 }
1043 
1044 void
saveSVG(const char * filename,PageSize size,double margin,Unit unit) const1045 Board::saveSVG( const char * filename, PageSize size, double margin, Unit unit ) const
1046 {
1047   if ( size == BoundingBox ) {
1048     saveSVG( filename, 0.0, 0.0, margin, unit );
1049   } else {
1050     saveSVG( filename, pageSizes[size][0], pageSizes[size][1], toMillimeter(margin,unit), UMillimeter );
1051   }
1052 }
1053 
1054 void
saveSVG(std::ostream & out,PageSize size,double margin,Unit unit) const1055 Board::saveSVG( std::ostream & out, PageSize size, double margin, Unit unit ) const
1056 {
1057   if ( size == BoundingBox ) {
1058     saveSVG( out, 0.0, 0.0, margin, unit );
1059   } else {
1060     saveSVG( out, pageSizes[size][0], pageSizes[size][1], toMillimeter(margin,unit), UMillimeter );
1061   }
1062 }
1063 
1064 void
saveSVG(const char * filename,double pageWidth,double pageHeight,double margin,Unit unit) const1065 Board::saveSVG( const char * filename, double pageWidth, double pageHeight, double margin, Unit unit ) const
1066 {
1067   std::ofstream out( filename );
1068   saveSVG(out,pageWidth,pageHeight,margin,unit);
1069   out.close();
1070 }
1071 
1072 void
saveSVG(std::ostream & out,double pageWidth,double pageHeight,double margin,Unit unit) const1073 Board::saveSVG( std::ostream & out, double pageWidth, double pageHeight, double margin, Unit unit ) const
1074 {
1075   Rect bbox = boundingBox(UseLineWidth);
1076   TransformSVG transform;
1077   bool clipping = _clippingPath.size() > 2;
1078   if ( clipping ) {
1079     bbox = bbox && _clippingPath.boundingBox();
1080   }
1081 
1082   out << "<?xml version=\"1.0\" encoding=\"ISO-8859-1\" standalone=\"no\"?>" << std::endl;
1083   out << "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"" << std::endl;
1084   out << " \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">" << std::endl;
1085 
1086   if ( pageWidth == 0.0 && pageHeight == 0.0 ) {
1087     transform.setBoundingBox( bbox,
1088                               toMillimeter(bbox.width,unit),
1089                               toMillimeter(bbox.height,unit),
1090                               -toMillimeter(margin,unit) );
1091     out << "<svg width=\""
1092         << toMillimeter(bbox.width+2*margin,unit) << "mm"
1093         << "\" height=\""
1094         << toMillimeter(bbox.height+2*margin,unit)  << "mm"
1095         << "\" " << std::endl;
1096     out << "     viewBox=\"0 0 "
1097         << toMillimeter(bbox.width+2*margin,unit) * ppmm   << " "
1098         << toMillimeter(bbox.height+2*margin,unit) * ppmm << "\" " << std::endl;
1099     out << "     xmlns=\"http://www.w3.org/2000/svg\""
1100         << " xmlns:xlink=\"http://www.w3.org/1999/xlink\""
1101         << " version=\"1.1\" >"
1102         << std::endl;
1103   } else {
1104     transform.setBoundingBox( bbox,
1105                               toMillimeter(pageWidth,unit),
1106                               toMillimeter(pageHeight,unit),
1107                               toMillimeter(margin,unit) );
1108 
1109     out << "<svg width=\""
1110         << toMillimeter(pageWidth,unit) << "mm\" height=\""
1111         << toMillimeter(pageHeight,unit) << "mm\" " << std::endl;
1112     out << "     viewBox=\"0 0 "
1113         << toMillimeter(pageWidth,unit) * ppmm  << " "
1114         << toMillimeter(pageHeight,unit) * ppmm  << "\" " << std::endl;
1115     out << "     xmlns=\"http://www.w3.org/2000/svg\""
1116         << " xmlns:xlink=\"http://www.w3.org/1999/xlink\""
1117         << " version=\"1.1\" >"
1118         << std::endl;
1119   }
1120 
1121   out << "<desc>"
1122          "Drawing created with the Board library (v" << _BOARD_VERSION_STRING_ << ") Copyleft 2007 Sebastien Fourey"
1123          "</desc>" << std::endl;
1124 
1125   if ( clipping  ) {
1126     out << "<g clip-rule=\"nonzero\">\n"
1127            " <clipPath id=\"GlobalClipPath\">\n"
1128            "  <path clip-rule=\"evenodd\"  d=\"";
1129     _clippingPath.flushSVGCommands( out, transform );
1130     out << "\" />\n"
1131            " </clipPath>\n"
1132            "<g clip-path=\"url(#GlobalClipPath)\">\n";
1133   }
1134 
1135   // Draw the background color if needed.
1136   if ( _backgroundColor != Color::Null ) {
1137     Rectangle r( bbox, Color::Null, _backgroundColor, 0.0 );
1138     r.flushSVG( out, transform );
1139   }
1140 
1141   // Draw the shapes.
1142   std::vector< Shape* > shapes = _shapes;
1143   stable_sort( shapes.begin(), shapes.end(), shapeGreaterDepth );
1144   std::vector< Shape* >::const_iterator i = shapes.begin();
1145   std::vector< Shape* >::const_iterator end = shapes.end();
1146   while ( i != end ) {
1147     (*i)->flushSVG( out, transform );
1148     ++i;
1149   }
1150 
1151   if ( clipping )
1152     out << "</g>\n</g>";
1153   out << "</svg>" << std::endl;
1154 }
1155 
1156 void
saveTikZ(const char * filename,PageSize size,double margin) const1157 Board::saveTikZ( const char * filename, PageSize size, double margin ) const
1158 {
1159   saveTikZ( filename, pageSizes[size][0], pageSizes[size][1], margin );
1160 }
1161 
1162 void
saveTikZ(std::ostream & out,PageSize size,double margin) const1163 Board::saveTikZ( std::ostream & out, PageSize size, double margin ) const
1164 {
1165   saveTikZ( out, pageSizes[size][0], pageSizes[size][1], margin );
1166 }
1167 
1168 void
saveTikZ(std::ostream & out,double pageWidth,double pageHeight,double margin) const1169 Board::saveTikZ( std::ostream & out, double pageWidth, double pageHeight, double margin ) const
1170 {
1171   TransformTikZ transform;
1172   Rect box = boundingBox(UseLineWidth);
1173   bool clipping = _clippingPath.size() > 2;
1174   if ( clipping )
1175     box = box && _clippingPath.boundingBox();
1176   transform.setBoundingBox( box, pageWidth, pageHeight, margin );
1177 
1178   out << "\\begin{tikzpicture}[anchor=south west,text depth=0,x={(1pt,0pt)},y={(0pt,-1pt)}]" << std::endl;
1179 
1180   if ( clipping  ) {
1181     out << "\\clip ";
1182     _clippingPath.flushTikZPoints( out, transform );
1183     out << "\n";
1184   }
1185 
1186   // Draw the background color if needed.
1187   if ( _backgroundColor != Color::Null ) {
1188     Rectangle r( box, Color::Null, _backgroundColor, 0.0 );
1189     r.flushTikZ( out, transform );
1190   }
1191 
1192   // Draw the shapes.
1193   std::vector< Shape* > shapes = _shapes;
1194   stable_sort( shapes.begin(), shapes.end(), shapeGreaterDepth );
1195   std::vector< Shape* >::const_iterator i = shapes.begin();
1196   std::vector< Shape* >::const_iterator end = shapes.end();
1197   while ( i != end ) {
1198     (*i)->flushTikZ( out, transform );
1199     ++i;
1200   }
1201   out << "\\end{tikzpicture}" << std::endl;
1202 }
1203 
1204 Group
makeGrid(Point topLeft,size_t columns,size_t rows,double width,double height,Color penColor,Color fillColor,double lineWidth,const LineStyle style,const LineCap cap,const LineJoin join,int depth)1205 Board::makeGrid( Point topLeft,
1206                  size_t columns, size_t rows,
1207                  double width, double height,
1208                  Color penColor, Color fillColor, double lineWidth,
1209                  const LineStyle style,
1210                  const LineCap cap,
1211                  const LineJoin join,
1212                  int depth )
1213 {
1214   Group group;
1215   double cellSide = width / columns;
1216   group << Rectangle(topLeft.x,topLeft.y,width,height,penColor,fillColor,lineWidth,style,cap,join,depth);
1217   Line vLine(topLeft.x+cellSide,topLeft.y,topLeft.x+cellSide,topLeft.y-height,penColor,lineWidth,style,Shape::RoundCap,join,depth);
1218   while ( --columns ) {
1219     group << vLine;
1220     vLine.translate(cellSide,0);
1221   }
1222   cellSide = height / rows;
1223   Line hLine(topLeft.x,topLeft.y-cellSide,topLeft.x+width,topLeft.y-cellSide,penColor,lineWidth,style,Shape::RoundCap,join,depth);
1224   while ( --rows ) {
1225     group << hLine;
1226     hLine.translate(0,-cellSide);
1227   }
1228 
1229   return group;
1230 }
1231 
penColor() const1232 Color Board::penColor() const
1233 {
1234   return _state.penColor;
1235 }
1236 
fillColor() const1237 Color Board::fillColor() const
1238 {
1239   return _state.fillColor;
1240 }
1241 
1242 double
toMillimeter(double x,Board::Unit unit)1243 Board::toMillimeter(double x, Board::Unit unit)
1244 {
1245   // enum Unit { UPoint, UInche, UCentimeter, UMillimeter };
1246   switch (unit) {
1247   case UPoint:
1248     return x * 25.4 / 72.0;
1249     break;
1250   case UInche:
1251     return x * 25.4;
1252     break;
1253   case UCentimeter:
1254     return x * 10.0;
1255     break;
1256   case UMillimeter:
1257     return x;
1258     break;
1259   default:
1260     Tools::error << "toMillimeter(): bad unit (" << unit << ")\n";
1261     return 0;
1262     break;
1263   }
1264 }
1265 
1266 void
saveTikZ(const char * filename,double pageWidth,double pageHeight,double margin) const1267 Board::saveTikZ( const char * filename, double pageWidth, double pageHeight, double margin ) const
1268 {
1269   std::ofstream out( filename );
1270   saveTikZ(out,pageWidth,pageHeight,margin);
1271   out.close();
1272 }
1273 
1274 void
save(const char * filename,double pageWidth,double pageHeight,double margin,Unit unit) const1275 Board::save(const char * filename, double pageWidth, double pageHeight, double margin , Unit unit) const
1276 {
1277   if ( Tools::stringEndsWith(filename,".eps", Tools::CaseInsensitive) ) {
1278     saveEPS( filename, pageWidth, pageHeight, margin, unit );
1279     return;
1280   }
1281   if ( Tools::stringEndsWith(filename,".fig", Tools::CaseInsensitive) ) {
1282     saveFIG( filename, pageWidth, pageHeight, margin, unit );
1283     return;
1284   }
1285   if ( Tools::stringEndsWith(filename,".svg", Tools::CaseInsensitive) ) {
1286     saveSVG( filename, pageWidth, pageHeight, margin, unit );
1287     return;
1288   }
1289   if ( Tools::stringEndsWith(filename,".tikz", Tools::CaseInsensitive) ) {
1290     saveTikZ( filename, pageWidth, pageHeight, margin );
1291     return;
1292   }
1293 }
1294 
1295 void
save(const char * filename,PageSize size,double margin,Unit unit) const1296 Board::save(const char * filename, PageSize size, double margin , Unit unit) const
1297 {
1298   if ( size == BoundingBox ) {
1299     save( filename, 0.0, 0.0, margin, unit );
1300   } else {
1301     save( filename, pageSizes[size][0], pageSizes[size][1], toMillimeter(margin,unit), UMillimeter );
1302   }
1303 }
1304 
1305 } // namespace LibBoard;
1306 
1307 /**
1308  * @example examples/arithmetic.cpp
1309  * @example examples/arrows.cpp
1310  * @example examples/clipping.cpp
1311  * @example examples/ellipse.cpp
1312  * @example examples/example1.cpp
1313  * @example examples/example2.cpp
1314  * @example examples/example3.cpp
1315  * @example examples/example4.cpp
1316  * @example examples/flag.cpp
1317  * @example examples/graph.cpp
1318  * @example examples/koch.cpp
1319  * @example examples/line_style.cpp
1320  * @example examples/line_segment.cpp
1321  * @example examples/logo.cpp
1322  * @example examples/ruler.cpp
1323  * @example examples/scale_ellipse.cpp
1324  * @example examples/stroke_path.cpp
1325  * @example examples/tilings.cpp
1326  * @example examples/Makefile
1327  */
1328 
1329 /**
1330  * @mainpage LibBoard - A C++ library for simple Postscript, SVG, and XFig drawings
1331  *
1332  * <table border="0"><tr><td><img align=left src="http://foureys.users.greyc.fr/board/doc/LibBoardLogoII_Small.png"/></td>
1333  * <td>(Copyleft) 2007 S&eacute;bastien Fourey - GREYC ENSICAEN</td></tr></table>
1334  *
1335  * @section intro_sec Introduction
1336  *
1337  * The board library allows simple drawings in:
1338  * <ul>
1339  *  <li>Encapsulated Postcript files (EPS) ;
1340  *  <li>XFig files (FIG) ;
1341  *  <li>Scalable Vector Graphics files (SVG).
1342  * </ul>
1343  *
1344  * The main class of the library is the #LibBoard#Board class. It is intented to
1345  * be as simple as possible so that it can be used quickly in programs to
1346  * generate the kind of figure one would rather not draw by hand, but which can
1347  * be easily drawn by a computer (C++) program.
1348  *
1349  * @section examples_sec Code examples
1350  *
1351  * <ul>
1352  * <li>See the "Examples" tab above or visit
1353  *     <a href="https://github.com/c-koi/libboard/blob/v0.9.4/EXAMPLES.md">dedicated page on GitHub</a>.
1354  * </ul>
1355  *
1356  * @section links_sec Links
1357  *
1358  * <ul>
1359  * <li>Visit the <a href="https://github.com/c-koi/libboard">official source code repository</a>.</li>
1360  * </ul>
1361  */
1362