Fix another crash when conversion fails in Unix PostScript code.
[wxWidgets.git] / src / generic / dcpsg.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/generic/dcpsg.cpp
3 // Purpose: Generic wxPostScriptDC implementation
4 // Author: Julian Smart, Robert Roebling, Markus Holzhem
5 // Modified by:
6 // Created: 04/01/98
7 // Copyright: (c) Julian Smart
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10
11 #include "wx/wxprec.h"
12
13 #ifdef __BORLANDC__
14 #pragma hdrstop
15 #endif
16
17 #if wxUSE_PRINTING_ARCHITECTURE && wxUSE_POSTSCRIPT
18
19 #include "wx/generic/dcpsg.h"
20
21 #ifndef WX_PRECOMP
22 #include "wx/intl.h"
23 #include "wx/log.h"
24 #include "wx/utils.h"
25 #include "wx/dcmemory.h"
26 #include "wx/math.h"
27 #include "wx/image.h"
28 #include "wx/icon.h"
29 #endif // WX_PRECOMP
30
31 #include "wx/prntbase.h"
32 #include "wx/generic/prntdlgg.h"
33 #include "wx/paper.h"
34 #include "wx/filename.h"
35 #include "wx/stdpaths.h"
36
37 #ifdef __WXMSW__
38
39 #ifdef DrawText
40 #undef DrawText
41 #endif
42
43 #ifdef StartDoc
44 #undef StartDoc
45 #endif
46
47 #ifdef GetCharWidth
48 #undef GetCharWidth
49 #endif
50
51 #ifdef FindWindow
52 #undef FindWindow
53 #endif
54
55 #endif
56
57 //-----------------------------------------------------------------------------
58 // start and end of document/page
59 //-----------------------------------------------------------------------------
60
61 static const char *wxPostScriptHeaderConicTo = "\
62 /conicto {\n\
63 /to_y exch def\n\
64 /to_x exch def\n\
65 /conic_cntrl_y exch def\n\
66 /conic_cntrl_x exch def\n\
67 currentpoint\n\
68 /p0_y exch def\n\
69 /p0_x exch def\n\
70 /p1_x p0_x conic_cntrl_x p0_x sub 2 3 div mul add def\n\
71 /p1_y p0_y conic_cntrl_y p0_y sub 2 3 div mul add def\n\
72 /p2_x p1_x to_x p0_x sub 1 3 div mul add def\n\
73 /p2_y p1_y to_y p0_y sub 1 3 div mul add def\n\
74 p1_x p1_y p2_x p2_y to_x to_y curveto\n\
75 } bind def\n\
76 ";
77
78 static const char *wxPostScriptHeaderEllipse = "\
79 /ellipsedict 8 dict def\n\
80 ellipsedict /mtrx matrix put\n\
81 /ellipse {\n\
82 ellipsedict begin\n\
83 /endangle exch def\n\
84 /startangle exch def\n\
85 /yrad exch def\n\
86 /xrad exch def\n\
87 /y exch def\n\
88 /x exch def\n\
89 /savematrix mtrx currentmatrix def\n\
90 x y translate\n\
91 xrad yrad scale\n\
92 0 0 1 startangle endangle arc\n\
93 savematrix setmatrix\n\
94 end\n\
95 } def\n\
96 ";
97
98 static const char *wxPostScriptHeaderEllipticArc= "\
99 /ellipticarcdict 8 dict def\n\
100 ellipticarcdict /mtrx matrix put\n\
101 /ellipticarc\n\
102 { ellipticarcdict begin\n\
103 /do_fill exch def\n\
104 /endangle exch def\n\
105 /startangle exch def\n\
106 /yrad exch def\n\
107 /xrad exch def \n\
108 /y exch def\n\
109 /x exch def\n\
110 /savematrix mtrx currentmatrix def\n\
111 x y translate\n\
112 xrad yrad scale\n\
113 do_fill { 0 0 moveto } if\n\
114 0 0 1 startangle endangle arc\n\
115 savematrix setmatrix\n\
116 do_fill { fill }{ stroke } ifelse\n\
117 end\n\
118 } def\n";
119
120 static const char *wxPostScriptHeaderSpline = "\
121 /DrawSplineSection {\n\
122 /y3 exch def\n\
123 /x3 exch def\n\
124 /y2 exch def\n\
125 /x2 exch def\n\
126 /y1 exch def\n\
127 /x1 exch def\n\
128 /xa x1 x2 x1 sub 0.666667 mul add def\n\
129 /ya y1 y2 y1 sub 0.666667 mul add def\n\
130 /xb x3 x2 x3 sub 0.666667 mul add def\n\
131 /yb y3 y2 y3 sub 0.666667 mul add def\n\
132 x1 y1 lineto\n\
133 xa ya xb yb x3 y3 curveto\n\
134 } def\n\
135 ";
136
137 static const char *wxPostScriptHeaderColourImage = "\
138 % define 'colorimage' if it isn't defined\n\
139 % ('colortogray' and 'mergeprocs' come from xwd2ps\n\
140 % via xgrab)\n\
141 /colorimage where % do we know about 'colorimage'?\n\
142 { pop } % yes: pop off the 'dict' returned\n\
143 { % no: define one\n\
144 /colortogray { % define an RGB->I function\n\
145 /rgbdata exch store % call input 'rgbdata'\n\
146 rgbdata length 3 idiv\n\
147 /npixls exch store\n\
148 /rgbindx 0 store\n\
149 0 1 npixls 1 sub {\n\
150 grays exch\n\
151 rgbdata rgbindx get 20 mul % Red\n\
152 rgbdata rgbindx 1 add get 32 mul % Green\n\
153 rgbdata rgbindx 2 add get 12 mul % Blue\n\
154 add add 64 idiv % I = .5G + .31R + .18B\n\
155 put\n\
156 /rgbindx rgbindx 3 add store\n\
157 } for\n\
158 grays 0 npixls getinterval\n\
159 } bind def\n\
160 \n\
161 % Utility procedure for colorimage operator.\n\
162 % This procedure takes two procedures off the\n\
163 % stack and merges them into a single procedure.\n\
164 \n\
165 /mergeprocs { % def\n\
166 dup length\n\
167 3 -1 roll\n\
168 dup\n\
169 length\n\
170 dup\n\
171 5 1 roll\n\
172 3 -1 roll\n\
173 add\n\
174 array cvx\n\
175 dup\n\
176 3 -1 roll\n\
177 0 exch\n\
178 putinterval\n\
179 dup\n\
180 4 2 roll\n\
181 putinterval\n\
182 } bind def\n\
183 \n\
184 /colorimage { % def\n\
185 pop pop % remove 'false 3' operands\n\
186 {colortogray} mergeprocs\n\
187 image\n\
188 } bind def\n\
189 } ifelse % end of 'false' case\n\
190 ";
191
192 static const char wxPostScriptHeaderReencodeISO1[] =
193 "\n/reencodeISO {\n"
194 "dup dup findfont dup length dict begin\n"
195 "{ 1 index /FID ne { def }{ pop pop } ifelse } forall\n"
196 "/Encoding ISOLatin1Encoding def\n"
197 "currentdict end definefont\n"
198 "} def\n"
199 "/ISOLatin1Encoding [\n"
200 "/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef\n"
201 "/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef\n"
202 "/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef\n"
203 "/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef\n"
204 "/space/exclam/quotedbl/numbersign/dollar/percent/ampersand/quoteright\n"
205 "/parenleft/parenright/asterisk/plus/comma/minus/period/slash\n"
206 "/zero/one/two/three/four/five/six/seven/eight/nine/colon/semicolon\n"
207 "/less/equal/greater/question/at/A/B/C/D/E/F/G/H/I/J/K/L/M/N\n"
208 "/O/P/Q/R/S/T/U/V/W/X/Y/Z/bracketleft/backslash/bracketright\n"
209 "/asciicircum/underscore/quoteleft/a/b/c/d/e/f/g/h/i/j/k/l/m\n"
210 "/n/o/p/q/r/s/t/u/v/w/x/y/z/braceleft/bar/braceright/asciitilde\n"
211 "/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef\n"
212 "/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef\n"
213 "/.notdef/dotlessi/grave/acute/circumflex/tilde/macron/breve\n"
214 "/dotaccent/dieresis/.notdef/ring/cedilla/.notdef/hungarumlaut\n";
215
216 static const char wxPostScriptHeaderReencodeISO2[] =
217 "/ogonek/caron/space/exclamdown/cent/sterling/currency/yen/brokenbar\n"
218 "/section/dieresis/copyright/ordfeminine/guillemotleft/logicalnot\n"
219 "/hyphen/registered/macron/degree/plusminus/twosuperior/threesuperior\n"
220 "/acute/mu/paragraph/periodcentered/cedilla/onesuperior/ordmasculine\n"
221 "/guillemotright/onequarter/onehalf/threequarters/questiondown\n"
222 "/Agrave/Aacute/Acircumflex/Atilde/Adieresis/Aring/AE/Ccedilla\n"
223 "/Egrave/Eacute/Ecircumflex/Edieresis/Igrave/Iacute/Icircumflex\n"
224 "/Idieresis/Eth/Ntilde/Ograve/Oacute/Ocircumflex/Otilde/Odieresis\n"
225 "/multiply/Oslash/Ugrave/Uacute/Ucircumflex/Udieresis/Yacute\n"
226 "/Thorn/germandbls/agrave/aacute/acircumflex/atilde/adieresis\n"
227 "/aring/ae/ccedilla/egrave/eacute/ecircumflex/edieresis/igrave\n"
228 "/iacute/icircumflex/idieresis/eth/ntilde/ograve/oacute/ocircumflex\n"
229 "/otilde/odieresis/divide/oslash/ugrave/uacute/ucircumflex/udieresis\n"
230 "/yacute/thorn/ydieresis\n"
231 "] def\n\n";
232
233 //-------------------------------------------------------------------------------
234 // wxPostScriptDC
235 //-------------------------------------------------------------------------------
236
237
238 IMPLEMENT_DYNAMIC_CLASS(wxPostScriptDC, wxDC)
239
240 wxPostScriptDC::wxPostScriptDC()
241 : wxDC(new wxPostScriptDCImpl(this))
242 {
243 }
244
245 wxPostScriptDC::wxPostScriptDC(const wxPrintData& printData)
246 : wxDC(new wxPostScriptDCImpl(this, printData))
247 {
248 }
249
250 // conversion
251 static const double RAD2DEG = 180.0 / M_PI;
252
253 // we don't want to use only 72 dpi from PS print
254 static const int DPI = 600;
255 static const double PS2DEV = 600.0 / 72.0;
256 static const double DEV2PS = 72.0 / 600.0;
257
258 #define XLOG2DEV(x) ((double)(LogicalToDeviceX(x)) * DEV2PS)
259 #define XLOG2DEVREL(x) ((double)(LogicalToDeviceXRel(x)) * DEV2PS)
260 #define YLOG2DEV(x) ((m_pageHeight - (double)LogicalToDeviceY(x)) * DEV2PS)
261 #define YLOG2DEVREL(x) ((double)(LogicalToDeviceYRel(x)) * DEV2PS)
262
263
264 IMPLEMENT_ABSTRACT_CLASS(wxPostScriptDCImpl, wxDCImpl)
265
266 //-------------------------------------------------------------------------------
267
268 wxPostScriptDCImpl::wxPostScriptDCImpl( wxPostScriptDC *owner ) :
269 wxDCImpl( owner )
270 {
271 Init();
272
273 m_pageHeight = 842 * PS2DEV;
274
275 m_ok = true;
276 }
277
278 wxPostScriptDCImpl::wxPostScriptDCImpl( wxPostScriptDC *owner, const wxPrintData& data ) :
279 wxDCImpl( owner )
280 {
281 Init();
282
283 // this calculates m_pageHeight required for
284 // taking the inverted Y axis into account
285 SetPrintData( data );
286
287 m_ok = true;
288 }
289
290
291 wxPostScriptDCImpl::wxPostScriptDCImpl( wxPrinterDC *owner ) :
292 wxDCImpl( owner )
293 {
294 Init();
295
296 m_pageHeight = 842 * PS2DEV;
297
298 m_ok = true;
299 }
300
301 wxPostScriptDCImpl::wxPostScriptDCImpl( wxPrinterDC *owner, const wxPrintData& data ) :
302 wxDCImpl( owner )
303 {
304 Init();
305
306 // this calculates m_pageHeight required for
307 // taking the inverted Y axis into account
308 SetPrintData( data );
309
310 m_ok = true;
311 }
312
313 void wxPostScriptDCImpl::Init()
314 {
315 m_pstream = NULL;
316
317 m_currentRed = 0;
318 m_currentGreen = 0;
319 m_currentBlue = 0;
320
321 m_pageNumber = 0;
322
323 m_clipping = false;
324
325 m_underlinePosition = 0.0;
326 m_underlineThickness = 0.0;
327
328 }
329
330 wxPostScriptDCImpl::~wxPostScriptDCImpl ()
331 {
332 if (m_pstream)
333 {
334 fclose( m_pstream );
335 m_pstream = NULL;
336 }
337 }
338
339 bool wxPostScriptDCImpl::IsOk() const
340 {
341 return m_ok;
342 }
343
344 wxRect wxPostScriptDCImpl::GetPaperRect() const
345 {
346 int w = 0;
347 int h = 0;
348 DoGetSize( &w, &h );
349 return wxRect(0,0,w,h);
350 }
351
352 int wxPostScriptDCImpl::GetResolution() const
353 {
354 return DPI;
355 }
356
357 void wxPostScriptDCImpl::DoSetClippingRegion (wxCoord x, wxCoord y, wxCoord w, wxCoord h)
358 {
359 wxCHECK_RET( m_ok , wxT("invalid postscript dc") );
360
361 if (m_clipping)
362 DestroyClippingRegion();
363
364 m_clipX1 = x;
365 m_clipY1 = y;
366 m_clipX2 = x + w;
367 m_clipY2 = y + h;
368
369 m_clipping = true;
370
371 wxString buffer;
372 buffer.Printf( "gsave\n"
373 "newpath\n"
374 "%f %f moveto\n"
375 "%f %f lineto\n"
376 "%f %f lineto\n"
377 "%f %f lineto\n"
378 "closepath clip newpath\n",
379 XLOG2DEV(x), YLOG2DEV(y),
380 XLOG2DEV(x+w), YLOG2DEV(y),
381 XLOG2DEV(x+w), YLOG2DEV(y+h),
382 XLOG2DEV(x), YLOG2DEV(y+h) );
383 buffer.Replace( ",", "." );
384 PsPrint( buffer );
385 }
386
387
388 void wxPostScriptDCImpl::DestroyClippingRegion()
389 {
390 wxCHECK_RET( m_ok , wxT("invalid postscript dc") );
391
392 if (m_clipping)
393 {
394 m_clipping = false;
395 PsPrint( "grestore\n" );
396 }
397
398 wxDCImpl::DestroyClippingRegion();
399 }
400
401 void wxPostScriptDCImpl::Clear()
402 {
403 // This should fail silently to avoid unnecessary
404 // asserts
405 // wxFAIL_MSG( wxT("wxPostScriptDCImpl::Clear not implemented.") );
406 }
407
408 bool wxPostScriptDCImpl::DoFloodFill (wxCoord WXUNUSED(x), wxCoord WXUNUSED(y), const wxColour &WXUNUSED(col), wxFloodFillStyle WXUNUSED(style))
409 {
410 wxFAIL_MSG( wxT("wxPostScriptDCImpl::FloodFill not implemented.") );
411 return false;
412 }
413
414 bool wxPostScriptDCImpl::DoGetPixel (wxCoord WXUNUSED(x), wxCoord WXUNUSED(y), wxColour * WXUNUSED(col)) const
415 {
416 wxFAIL_MSG( wxT("wxPostScriptDCImpl::GetPixel not implemented.") );
417 return false;
418 }
419
420 void wxPostScriptDCImpl::DoCrossHair (wxCoord WXUNUSED(x), wxCoord WXUNUSED(y))
421 {
422 wxFAIL_MSG( wxT("wxPostScriptDCImpl::CrossHair not implemented.") );
423 }
424
425 void wxPostScriptDCImpl::DoDrawLine (wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2)
426 {
427 wxCHECK_RET( m_ok, wxT("invalid postscript dc") );
428
429 if ( m_pen.IsTransparent() )
430 return;
431
432 SetPen( m_pen );
433
434 wxString buffer;
435 buffer.Printf( "newpath\n"
436 "%f %f moveto\n"
437 "%f %f lineto\n"
438 "stroke\n",
439 XLOG2DEV(x1), YLOG2DEV(y1),
440 XLOG2DEV(x2), YLOG2DEV(y2) );
441 buffer.Replace( ",", "." );
442 PsPrint( buffer );
443
444 CalcBoundingBox( x1, y1 );
445 CalcBoundingBox( x2, y2 );
446 }
447
448 void wxPostScriptDCImpl::DoDrawArc (wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2, wxCoord xc, wxCoord yc)
449 {
450 wxCHECK_RET( m_ok, wxT("invalid postscript dc") );
451
452 wxCoord dx = x1 - xc;
453 wxCoord dy = y1 - yc;
454 double radius = sqrt( (double)(dx*dx+dy*dy) );
455 double alpha1, alpha2;
456
457 if (x1 == x2 && y1 == y2)
458 {
459 alpha1 = 0.0;
460 alpha2 = 360.0;
461 }
462 else if ( wxIsNullDouble(radius) )
463 {
464 alpha1 =
465 alpha2 = 0.0;
466 }
467 else
468 {
469 alpha1 = (x1 - xc == 0) ?
470 (y1 - yc < 0) ? 90.0 : -90.0 :
471 -atan2(double(y1-yc), double(x1-xc)) * RAD2DEG;
472 alpha2 = (x2 - xc == 0) ?
473 (y2 - yc < 0) ? 90.0 : -90.0 :
474 -atan2(double(y2-yc), double(x2-xc)) * RAD2DEG;
475 }
476 while (alpha1 <= 0) alpha1 += 360;
477 while (alpha2 <= 0) alpha2 += 360; // adjust angles to be between
478 while (alpha1 > 360) alpha1 -= 360; // 0 and 360 degree
479 while (alpha2 > 360) alpha2 -= 360;
480
481 int i_radius = wxRound( radius );
482
483 if ( m_brush.IsNonTransparent() )
484 {
485 SetBrush( m_brush );
486
487 wxString buffer;
488 buffer.Printf( "newpath\n"
489 "%f %f %f %f %f %f ellipse\n"
490 "%f %f lineto\n"
491 "closepath\n"
492 "fill\n",
493 XLOG2DEV(xc), YLOG2DEV(yc),
494 XLOG2DEVREL(i_radius), YLOG2DEVREL(i_radius),
495 alpha1, alpha2,
496 XLOG2DEV(xc), YLOG2DEV(yc) );
497 buffer.Replace( ",", "." );
498 PsPrint( buffer );
499
500 CalcBoundingBox( xc-i_radius, yc-i_radius );
501 CalcBoundingBox( xc+i_radius, yc+i_radius );
502 }
503
504 if ( m_pen.IsNonTransparent() )
505 {
506 SetPen( m_pen );
507
508 wxString buffer;
509 buffer.Printf( "newpath\n"
510 "%f %f %f %f %f %f ellipse\n"
511 "stroke\n",
512 XLOG2DEV(xc), YLOG2DEV(yc),
513 XLOG2DEVREL(i_radius), YLOG2DEVREL(i_radius),
514 alpha1, alpha2 );
515 buffer.Replace( ",", "." );
516 PsPrint( buffer );
517
518 CalcBoundingBox( xc-i_radius, yc-i_radius );
519 CalcBoundingBox( xc+i_radius, yc+i_radius );
520 }
521 }
522
523 void wxPostScriptDCImpl::DoDrawEllipticArc(wxCoord x,wxCoord y,wxCoord w,wxCoord h,double sa,double ea)
524 {
525 wxCHECK_RET( m_ok, wxT("invalid postscript dc") );
526
527 if ( sa >= 360 || sa <= -360 )
528 sa -= int(sa/360)*360;
529 if ( ea >= 360 || ea <=- 360 )
530 ea -= int(ea/360)*360;
531 if ( sa < 0 )
532 sa += 360;
533 if ( ea < 0 )
534 ea += 360;
535
536 if ( wxIsSameDouble(sa, ea) )
537 {
538 DoDrawEllipse(x,y,w,h);
539 return;
540 }
541
542 if ( m_brush.IsNonTransparent() )
543 {
544 SetBrush( m_brush );
545
546 wxString buffer;
547 buffer.Printf( "newpath\n"
548 "%f %f %f %f %f %f true ellipticarc\n",
549 XLOG2DEV(x+w/2), YLOG2DEV(y+h/2),
550 XLOG2DEVREL(w/2), YLOG2DEVREL(h/2),
551 sa, ea );
552 buffer.Replace( ",", "." );
553 PsPrint( buffer );
554
555 CalcBoundingBox( x ,y );
556 CalcBoundingBox( x+w, y+h );
557 }
558
559 if ( m_pen.IsNonTransparent() )
560 {
561 SetPen( m_pen );
562
563 wxString buffer;
564 buffer.Printf( "newpath\n"
565 "%f %f %f %f %f %f false ellipticarc\n",
566 XLOG2DEV(x+w/2), YLOG2DEV(y+h/2),
567 XLOG2DEVREL(w/2), YLOG2DEVREL(h/2),
568 sa, ea );
569 buffer.Replace( ",", "." );
570 PsPrint( buffer );
571
572 CalcBoundingBox( x ,y );
573 CalcBoundingBox( x+w, y+h );
574 }
575 }
576
577 void wxPostScriptDCImpl::DoDrawPoint (wxCoord x, wxCoord y)
578 {
579 wxCHECK_RET( m_ok, wxT("invalid postscript dc") );
580
581 if ( m_pen.IsTransparent() )
582 return;
583
584 SetPen (m_pen);
585
586 wxString buffer;
587 buffer.Printf( "newpath\n"
588 "%f %f moveto\n"
589 "%f %f lineto\n"
590 "stroke\n",
591 XLOG2DEV(x), YLOG2DEV(y),
592 XLOG2DEV(x+1), YLOG2DEV(y) );
593 buffer.Replace( ",", "." );
594 PsPrint( buffer );
595
596 CalcBoundingBox( x, y );
597 }
598
599 void wxPostScriptDCImpl::DoDrawPolygon (int n, const wxPoint points[], wxCoord xoffset, wxCoord yoffset, wxPolygonFillMode fillStyle)
600 {
601 wxCHECK_RET( m_ok, wxT("invalid postscript dc") );
602
603 if (n <= 0) return;
604
605 if ( m_brush.IsNonTransparent() )
606 {
607 SetBrush( m_brush );
608
609 PsPrint( "newpath\n" );
610
611 double xx = XLOG2DEV(points[0].x + xoffset);
612 double yy = YLOG2DEV(points[0].y + yoffset);
613
614 wxString buffer;
615 buffer.Printf( "%f %f moveto\n", xx, yy );
616 buffer.Replace( ",", "." );
617 PsPrint( buffer );
618
619 CalcBoundingBox( points[0].x + xoffset, points[0].y + yoffset );
620
621 for (int i = 1; i < n; i++)
622 {
623 xx = XLOG2DEV(points[i].x + xoffset);
624 yy = YLOG2DEV(points[i].y + yoffset);
625
626 buffer.Printf( "%f %f lineto\n", xx, yy );
627 buffer.Replace( ",", "." );
628 PsPrint( buffer );
629
630 CalcBoundingBox( points[i].x + xoffset, points[i].y + yoffset);
631 }
632
633 PsPrint( (fillStyle == wxODDEVEN_RULE ? "eofill\n" : "fill\n") );
634 }
635
636 if ( m_pen.IsNonTransparent() )
637 {
638 SetPen( m_pen );
639
640 PsPrint( "newpath\n" );
641
642 double xx = XLOG2DEV(points[0].x + xoffset);
643 double yy = YLOG2DEV(points[0].y + yoffset);
644
645 wxString buffer;
646 buffer.Printf( "%f %f moveto\n", xx, yy );
647 buffer.Replace( ",", "." );
648 PsPrint( buffer );
649
650 CalcBoundingBox( points[0].x + xoffset, points[0].y + yoffset );
651
652 for (int i = 1; i < n; i++)
653 {
654 xx = XLOG2DEV(points[i].x + xoffset);
655 yy = YLOG2DEV(points[i].y + yoffset);
656
657 buffer.Printf( "%f %f lineto\n", xx, yy );
658 buffer.Replace( ",", "." );
659 PsPrint( buffer );
660
661 CalcBoundingBox( points[i].x + xoffset, points[i].y + yoffset);
662 }
663
664 PsPrint( "closepath\n" );
665 PsPrint( "stroke\n" );
666 }
667 }
668
669 void wxPostScriptDCImpl::DoDrawPolyPolygon (int n, const int count[], const wxPoint points[], wxCoord xoffset, wxCoord yoffset, wxPolygonFillMode fillStyle)
670 {
671 wxCHECK_RET( m_ok, wxT("invalid postscript dc") );
672
673 if (n <= 0) return;
674
675 if ( m_brush.IsNonTransparent() )
676 {
677 SetBrush( m_brush );
678
679 PsPrint( "newpath\n" );
680
681 int ofs = 0;
682 for (int i = 0; i < n; ofs += count[i++])
683 {
684 double xx = XLOG2DEV(points[ofs].x + xoffset);
685 double yy = YLOG2DEV(points[ofs].y + yoffset);
686
687 wxString buffer;
688 buffer.Printf( "%f %f moveto\n", xx, yy );
689 buffer.Replace( ",", "." );
690 PsPrint( buffer );
691
692 CalcBoundingBox( points[ofs].x + xoffset, points[ofs].y + yoffset );
693
694 for (int j = 1; j < count[i]; j++)
695 {
696 xx = XLOG2DEV(points[ofs+j].x + xoffset);
697 yy = YLOG2DEV(points[ofs+j].y + yoffset);
698
699 buffer.Printf( "%f %f lineto\n", xx, yy );
700 buffer.Replace( ",", "." );
701 PsPrint( buffer );
702
703 CalcBoundingBox( points[ofs+j].x + xoffset, points[ofs+j].y + yoffset);
704 }
705 }
706 PsPrint( (fillStyle == wxODDEVEN_RULE ? "eofill\n" : "fill\n") );
707 }
708
709 if ( m_pen.IsNonTransparent() )
710 {
711 SetPen( m_pen );
712
713 PsPrint( "newpath\n" );
714
715 int ofs = 0;
716 for (int i = 0; i < n; ofs += count[i++])
717 {
718 double xx = XLOG2DEV(points[ofs].x + xoffset);
719 double yy = YLOG2DEV(points[ofs].y + yoffset);
720
721 wxString buffer;
722 buffer.Printf( "%f %f moveto\n", xx, yy );
723 buffer.Replace( ",", "." );
724 PsPrint( buffer );
725
726 CalcBoundingBox( points[ofs].x + xoffset, points[ofs].y + yoffset );
727
728 for (int j = 1; j < count[i]; j++)
729 {
730 xx = XLOG2DEV(points[ofs+j].x + xoffset);
731 yy = YLOG2DEV(points[ofs+j].y + yoffset);
732
733 buffer.Printf( "%f %f lineto\n", xx, yy );
734 buffer.Replace( ",", "." );
735 PsPrint( buffer );
736
737 CalcBoundingBox( points[ofs+j].x + xoffset, points[ofs+j].y + yoffset);
738 }
739 }
740 PsPrint( "closepath\n" );
741 PsPrint( "stroke\n" );
742 }
743 }
744
745 void wxPostScriptDCImpl::DoDrawLines (int n, const wxPoint points[], wxCoord xoffset, wxCoord yoffset)
746 {
747 wxCHECK_RET( m_ok, wxT("invalid postscript dc") );
748
749 if ( m_pen.IsTransparent() )
750 return;
751
752 if (n <= 0) return;
753
754 SetPen (m_pen);
755
756 int i;
757 for ( i =0; i<n ; i++ )
758 CalcBoundingBox( points[i].x+xoffset, points[i].y+yoffset );
759
760 wxString buffer;
761 buffer.Printf( "newpath\n"
762 "%f %f moveto\n",
763 XLOG2DEV(points[0].x+xoffset),
764 YLOG2DEV(points[0].y+yoffset) );
765 buffer.Replace( ",", "." );
766 PsPrint( buffer );
767
768 for (i = 1; i < n; i++)
769 {
770 buffer.Printf( "%f %f lineto\n",
771 XLOG2DEV(points[i].x+xoffset),
772 YLOG2DEV(points[i].y+yoffset) );
773 buffer.Replace( ",", "." );
774 PsPrint( buffer );
775 }
776
777 PsPrint( "stroke\n" );
778 }
779
780 void wxPostScriptDCImpl::DoDrawRectangle (wxCoord x, wxCoord y, wxCoord width, wxCoord height)
781 {
782 wxCHECK_RET( m_ok, wxT("invalid postscript dc") );
783
784 width--;
785 height--;
786
787 if ( m_brush.IsNonTransparent() )
788 {
789 SetBrush( m_brush );
790
791 wxString buffer;
792 buffer.Printf( "newpath\n"
793 "%f %f moveto\n"
794 "%f %f lineto\n"
795 "%f %f lineto\n"
796 "%f %f lineto\n"
797 "closepath\n"
798 "fill\n",
799 XLOG2DEV(x), YLOG2DEV(y),
800 XLOG2DEV(x + width), YLOG2DEV(y),
801 XLOG2DEV(x + width), YLOG2DEV(y + height),
802 XLOG2DEV(x), YLOG2DEV(y + height) );
803 buffer.Replace( ",", "." );
804 PsPrint( buffer );
805
806 CalcBoundingBox( x, y );
807 CalcBoundingBox( x + width, y + height );
808 }
809
810 if ( m_pen.IsNonTransparent() )
811 {
812 SetPen (m_pen);
813
814 wxString buffer;
815 buffer.Printf( "newpath\n"
816 "%f %f moveto\n"
817 "%f %f lineto\n"
818 "%f %f lineto\n"
819 "%f %f lineto\n"
820 "closepath\n"
821 "stroke\n",
822 XLOG2DEV(x), YLOG2DEV(y),
823 XLOG2DEV(x + width), YLOG2DEV(y),
824 XLOG2DEV(x + width), YLOG2DEV(y + height),
825 XLOG2DEV(x), YLOG2DEV(y + height) );
826 buffer.Replace( ",", "." );
827 PsPrint( buffer );
828
829 CalcBoundingBox( x, y );
830 CalcBoundingBox( x + width, y + height );
831 }
832 }
833
834 void wxPostScriptDCImpl::DoDrawRoundedRectangle (wxCoord x, wxCoord y, wxCoord width, wxCoord height, double radius)
835 {
836 wxCHECK_RET( m_ok, wxT("invalid postscript dc") );
837
838 width--;
839 height--;
840
841 if (radius < 0.0)
842 {
843 // Now, a negative radius is interpreted to mean
844 // 'the proportion of the smallest X or Y dimension'
845 double smallest = width < height ? width : height;
846 radius = (-radius * smallest);
847 }
848
849 wxCoord rad = (wxCoord) radius;
850
851 if ( m_brush.IsNonTransparent() )
852 {
853 SetBrush( m_brush );
854
855 /* Draw rectangle anticlockwise */
856 wxString buffer;
857 buffer.Printf( "newpath\n"
858 "%f %f %f 90 180 arc\n"
859 "%f %f lineto\n"
860 "%f %f %f 180 270 arc\n"
861 "%f %f lineto\n"
862 "%f %f %f 270 0 arc\n"
863 "%f %f lineto\n"
864 "%f %f %f 0 90 arc\n"
865 "%f %f lineto\n"
866 "closepath\n"
867 "fill\n",
868 XLOG2DEV(x + rad), YLOG2DEV(y + rad), XLOG2DEVREL(rad),
869 XLOG2DEV(x), YLOG2DEV(y + height - rad),
870 XLOG2DEV(x + rad), YLOG2DEV(y + height - rad), XLOG2DEVREL(rad),
871 XLOG2DEV(x + width - rad), YLOG2DEV(y + height),
872 XLOG2DEV(x + width - rad), YLOG2DEV(y + height - rad), XLOG2DEVREL(rad),
873 XLOG2DEV(x + width), YLOG2DEV(y + rad),
874 XLOG2DEV(x + width - rad), YLOG2DEV(y + rad), XLOG2DEVREL(rad),
875 XLOG2DEV(x + rad), YLOG2DEV(y) );
876 buffer.Replace( ",", "." );
877 PsPrint( buffer );
878
879 CalcBoundingBox( x, y );
880 CalcBoundingBox( x + width, y + height );
881 }
882
883 if ( m_pen.IsNonTransparent() )
884 {
885 SetPen (m_pen);
886
887 /* Draw rectangle anticlockwise */
888 wxString buffer;
889 buffer.Printf( "newpath\n"
890 "%f %f %f 90 180 arc\n"
891 "%f %f lineto\n"
892 "%f %f %f 180 270 arc\n"
893 "%f %f lineto\n"
894 "%f %f %f 270 0 arc\n"
895 "%f %f lineto\n"
896 "%f %f %f 0 90 arc\n"
897 "%f %f lineto\n"
898 "closepath\n"
899 "stroke\n",
900 XLOG2DEV(x + rad), YLOG2DEV(y + rad), XLOG2DEVREL(rad),
901 XLOG2DEV(x), YLOG2DEV(y + height - rad),
902 XLOG2DEV(x + rad), YLOG2DEV(y + height - rad), XLOG2DEVREL(rad),
903 XLOG2DEV(x + width - rad), YLOG2DEV(y + height),
904 XLOG2DEV(x + width - rad), YLOG2DEV(y + height - rad), XLOG2DEVREL(rad),
905 XLOG2DEV(x + width), YLOG2DEV(y + rad),
906 XLOG2DEV(x + width - rad), YLOG2DEV(y + rad), XLOG2DEVREL(rad),
907 XLOG2DEV(x + rad), YLOG2DEV(y) );
908 buffer.Replace( ",", "." );
909 PsPrint( buffer );
910
911 CalcBoundingBox( x, y );
912 CalcBoundingBox( x + width, y + height );
913 }
914 }
915
916 void wxPostScriptDCImpl::DoDrawEllipse (wxCoord x, wxCoord y, wxCoord width, wxCoord height)
917 {
918 wxCHECK_RET( m_ok, wxT("invalid postscript dc") );
919
920 width--;
921 height--;
922
923 if ( m_brush.IsNonTransparent() )
924 {
925 SetBrush (m_brush);
926
927 wxString buffer;
928 buffer.Printf( "newpath\n"
929 "%f %f %f %f 0 360 ellipse\n"
930 "fill\n",
931 XLOG2DEV(x + width / 2), YLOG2DEV(y + height / 2),
932 XLOG2DEVREL(width / 2), YLOG2DEVREL(height / 2) );
933 buffer.Replace( ",", "." );
934 PsPrint( buffer );
935
936 CalcBoundingBox( x - width, y - height );
937 CalcBoundingBox( x + width, y + height );
938 }
939
940 if ( m_pen.IsNonTransparent() )
941 {
942 SetPen (m_pen);
943
944 wxString buffer;
945 buffer.Printf( "newpath\n"
946 "%f %f %f %f 0 360 ellipse\n"
947 "stroke\n",
948 XLOG2DEV(x + width / 2), YLOG2DEV(y + height / 2),
949 XLOG2DEVREL(width / 2), YLOG2DEVREL(height / 2) );
950 buffer.Replace( ",", "." );
951 PsPrint( buffer );
952
953 CalcBoundingBox( x - width, y - height );
954 CalcBoundingBox( x + width, y + height );
955 }
956 }
957
958 void wxPostScriptDCImpl::DoDrawIcon( const wxIcon& icon, wxCoord x, wxCoord y )
959 {
960 DoDrawBitmap( icon, x, y, true );
961 }
962
963 /* this has to be char, not wxChar */
964 static const char hexArray[] = "0123456789ABCDEF";
965
966 void wxPostScriptDCImpl::DoDrawBitmap( const wxBitmap& bitmap, wxCoord x, wxCoord y, bool WXUNUSED(useMask) )
967 {
968 wxCHECK_RET( m_ok, wxT("invalid postscript dc") );
969
970 if (!bitmap.IsOk()) return;
971
972 wxImage image = bitmap.ConvertToImage();
973
974 if (!image.IsOk()) return;
975
976 wxCoord w = image.GetWidth();
977 wxCoord h = image.GetHeight();
978
979 double ww = XLOG2DEVREL(image.GetWidth());
980 double hh = YLOG2DEVREL(image.GetHeight());
981
982 double xx = XLOG2DEV(x);
983 double yy = YLOG2DEV(y + bitmap.GetHeight());
984
985 wxString buffer;
986 buffer.Printf( "/origstate save def\n"
987 "20 dict begin\n"
988 "/pix %d string def\n"
989 "/grays %d string def\n"
990 "/npixels 0 def\n"
991 "/rgbindx 0 def\n"
992 "%f %f translate\n"
993 "%f %f scale\n"
994 "%d %d 8\n"
995 "[%d 0 0 %d 0 %d]\n"
996 "{currentfile pix readhexstring pop}\n"
997 "false 3 colorimage\n",
998 w, w, xx, yy, ww, hh, w, h, w, -h, h );
999 buffer.Replace( ",", "." );
1000 PsPrint( buffer );
1001
1002 unsigned char* data = image.GetData();
1003
1004 // size of the buffer = width*rgb(3)*hexa(2)+'\n'
1005 wxCharBuffer charbuffer(w*6 + 1);
1006 int firstDigit, secondDigit;
1007
1008 //rows
1009 for (int j = 0; j < h; j++)
1010 {
1011 char* bufferindex = charbuffer.data();
1012
1013 //cols
1014 for (int i = 0; i < w*3; i++)
1015 {
1016 firstDigit = (int)(*data/16.0);
1017 secondDigit = (int)(*data - (firstDigit*16.0));
1018 *(bufferindex++) = hexArray[firstDigit];
1019 *(bufferindex++) = hexArray[secondDigit];
1020
1021 data++;
1022 }
1023 *(bufferindex++) = '\n';
1024 *bufferindex = 0;
1025
1026 if (m_pstream)
1027 fwrite( charbuffer, 1, strlen( charbuffer ), m_pstream );
1028 else
1029 PsPrint( charbuffer );
1030 }
1031
1032 PsPrint( "end\n" );
1033 PsPrint( "origstate restore\n" );
1034 }
1035
1036 void wxPostScriptDCImpl::SetFont( const wxFont& font )
1037 {
1038 wxCHECK_RET( m_ok, wxT("invalid postscript dc") );
1039
1040 if (!font.IsOk()) return;
1041
1042 m_font = font;
1043
1044 int Style = m_font.GetStyle();
1045 int Weight = m_font.GetWeight();
1046
1047 const char *name;
1048 switch (m_font.GetFamily())
1049 {
1050 case wxTELETYPE:
1051 case wxMODERN:
1052 {
1053 if (Style == wxITALIC)
1054 {
1055 if (Weight == wxBOLD)
1056 name = "/Courier-BoldOblique";
1057 else
1058 name = "/Courier-Oblique";
1059 }
1060 else
1061 {
1062 if (Weight == wxBOLD)
1063 name = "/Courier-Bold";
1064 else
1065 name = "/Courier";
1066 }
1067 break;
1068 }
1069 case wxROMAN:
1070 {
1071 if (Style == wxITALIC)
1072 {
1073 if (Weight == wxBOLD)
1074 name = "/Times-BoldItalic";
1075 else
1076 name = "/Times-Italic";
1077 }
1078 else
1079 {
1080 if (Weight == wxBOLD)
1081 name = "/Times-Bold";
1082 else
1083 name = "/Times-Roman";
1084 }
1085 break;
1086 }
1087 case wxSCRIPT:
1088 {
1089 name = "/ZapfChancery-MediumItalic";
1090 break;
1091 }
1092 case wxSWISS:
1093 default:
1094 {
1095 if (Style == wxITALIC)
1096 {
1097 if (Weight == wxBOLD)
1098 name = "/Helvetica-BoldOblique";
1099 else
1100 name = "/Helvetica-Oblique";
1101 }
1102 else
1103 {
1104 if (Weight == wxBOLD)
1105 name = "/Helvetica-Bold";
1106 else
1107 name = "/Helvetica";
1108 }
1109 break;
1110 }
1111 }
1112
1113 // We may legitimately call SetFont before BeginDoc
1114 if (!m_pstream)
1115 return;
1116
1117 PsPrint( name );
1118 PsPrint( " reencodeISO def\n" );
1119 PsPrint( name );
1120 PsPrint( " findfont\n" );
1121
1122
1123 float size = float(m_font.GetPointSize());
1124 size = size * GetFontPointSizeAdjustment(DPI);
1125 wxString buffer;
1126 buffer.Printf( "%f scalefont setfont\n", size * m_scaleX );
1127 buffer.Replace( ",", "." );
1128 PsPrint( buffer );
1129 }
1130
1131 void wxPostScriptDCImpl::SetPen( const wxPen& pen )
1132 {
1133 wxCHECK_RET( m_ok, wxT("invalid postscript dc") );
1134
1135 if (!pen.IsOk()) return;
1136
1137 int oldStyle = m_pen.IsOk() ? m_pen.GetStyle() : wxPENSTYLE_INVALID;
1138 wxPenCap oldCap = m_pen.IsOk() ? m_pen.GetCap() : wxCAP_INVALID;
1139 wxPenJoin oldJoin = m_pen.IsOk() ? m_pen.GetJoin() : wxJOIN_INVALID;
1140
1141 m_pen = pen;
1142 wxPenCap cap = m_pen.IsOk() ? m_pen.GetCap() : wxCAP_INVALID;
1143 wxPenJoin join = m_pen.IsOk() ? m_pen.GetJoin() : wxJOIN_INVALID;
1144
1145 double width;
1146
1147 if (m_pen.GetWidth() <= 0)
1148 width = 0.1;
1149 else
1150 width = (double) m_pen.GetWidth();
1151
1152 wxString buffer;
1153 buffer.Printf( "%f setlinewidth\n", width * DEV2PS * m_scaleX );
1154 buffer.Replace( ",", "." );
1155 PsPrint( buffer );
1156
1157 /*
1158 Line style - WRONG: 2nd arg is OFFSET
1159
1160 Here, I'm afraid you do not conceive meaning of parameters of 'setdash'
1161 operator correctly. You should look-up this in the Red Book: the 2nd parame-
1162 ter is not number of values in the array of the first one, but an offset
1163 into this description of the pattern. I mean a real *offset* not index
1164 into array. I.e. If the command is [3 4] 1 setdash is used, then there
1165 will be first black line *2* units wxCoord, then space 4 units, then the
1166 pattern of *3* units black, 4 units space will be repeated.
1167 */
1168
1169 static const char *dotted = "[2 5] 2";
1170 static const char *short_dashed = "[4 4] 2";
1171 static const char *wxCoord_dashed = "[4 8] 2";
1172 static const char *dotted_dashed = "[6 6 2 6] 4";
1173
1174 const char *psdash;
1175
1176 switch (m_pen.GetStyle())
1177 {
1178 case wxPENSTYLE_DOT: psdash = dotted; break;
1179 case wxPENSTYLE_SHORT_DASH: psdash = short_dashed; break;
1180 case wxPENSTYLE_LONG_DASH: psdash = wxCoord_dashed; break;
1181 case wxPENSTYLE_DOT_DASH: psdash = dotted_dashed; break;
1182 case wxPENSTYLE_USER_DASH:
1183 {
1184 wxDash *dashes;
1185 int nDashes = m_pen.GetDashes (&dashes);
1186 PsPrint ("[");
1187 for (int i = 0; i < nDashes; ++i)
1188 {
1189 buffer.Printf( "%d ", dashes [i] );
1190 PsPrint( buffer );
1191 }
1192 PsPrint ("] 0 setdash\n");
1193 psdash = 0;
1194 }
1195 break;
1196 case wxPENSTYLE_SOLID:
1197 case wxPENSTYLE_TRANSPARENT:
1198 default: psdash = "[] 0"; break;
1199 }
1200
1201 if ( psdash && (oldStyle != m_pen.GetStyle()) )
1202 {
1203 PsPrint( psdash );
1204 PsPrint( " setdash\n" );
1205 }
1206
1207 if ( cap != wxCAP_INVALID && cap != oldCap )
1208 {
1209 switch ( cap )
1210 {
1211 case wxCAP_ROUND: buffer = "1"; break;
1212 case wxCAP_PROJECTING: buffer = "2"; break;
1213 case wxCAP_BUTT: buffer = "0"; break;
1214
1215 // This case is just to fix compiler warning, this is impossible
1216 // due to the test above.
1217 case wxCAP_INVALID: break;
1218 }
1219 buffer << " setlinecap\n";
1220 PsPrint( buffer );
1221 }
1222
1223 if ( join != wxJOIN_INVALID && join != oldJoin )
1224 {
1225 switch ( join )
1226 {
1227 case wxJOIN_BEVEL: buffer = "2"; break;
1228 case wxJOIN_ROUND: buffer = "1"; break;
1229 case wxJOIN_MITER: buffer = "0"; break;
1230 case wxJOIN_INVALID: break;
1231 }
1232 buffer << " setlinejoin\n";
1233 PsPrint( buffer );
1234 }
1235
1236 // Line colour
1237 unsigned char red = m_pen.GetColour().Red();
1238 unsigned char blue = m_pen.GetColour().Blue();
1239 unsigned char green = m_pen.GetColour().Green();
1240
1241 if (!m_colour)
1242 {
1243 // Anything not white is black
1244 if (! (red == (unsigned char) 255 &&
1245 blue == (unsigned char) 255 &&
1246 green == (unsigned char) 255) )
1247 {
1248 red = (unsigned char) 0;
1249 green = (unsigned char) 0;
1250 blue = (unsigned char) 0;
1251 }
1252 // setgray here ?
1253 }
1254
1255 if (!(red == m_currentRed && green == m_currentGreen && blue == m_currentBlue))
1256 {
1257 double redPS = (double)(red) / 255.0;
1258 double bluePS = (double)(blue) / 255.0;
1259 double greenPS = (double)(green) / 255.0;
1260
1261 buffer.Printf( "%f %f %f setrgbcolor\n", redPS, greenPS, bluePS );
1262 buffer.Replace( ",", "." );
1263 PsPrint( buffer );
1264
1265 m_currentRed = red;
1266 m_currentBlue = blue;
1267 m_currentGreen = green;
1268 }
1269 }
1270
1271 void wxPostScriptDCImpl::SetBrush( const wxBrush& brush )
1272 {
1273 wxCHECK_RET( m_ok, wxT("invalid postscript dc") );
1274
1275 if (!brush.IsOk()) return;
1276
1277 m_brush = brush;
1278
1279 // Brush colour
1280 unsigned char red = m_brush.GetColour().Red();
1281 unsigned char blue = m_brush.GetColour().Blue();
1282 unsigned char green = m_brush.GetColour().Green();
1283
1284 if (!m_colour)
1285 {
1286 // Anything not white is black
1287 if (! (red == (unsigned char) 255 &&
1288 blue == (unsigned char) 255 &&
1289 green == (unsigned char) 255) )
1290 {
1291 red = (unsigned char) 0;
1292 green = (unsigned char) 0;
1293 blue = (unsigned char) 0;
1294 }
1295 // setgray here ?
1296 }
1297
1298 if (!(red == m_currentRed && green == m_currentGreen && blue == m_currentBlue))
1299 {
1300 double redPS = (double)(red) / 255.0;
1301 double bluePS = (double)(blue) / 255.0;
1302 double greenPS = (double)(green) / 255.0;
1303
1304 wxString buffer;
1305 buffer.Printf( "%f %f %f setrgbcolor\n", redPS, greenPS, bluePS );
1306 buffer.Replace( ",", "." );
1307 PsPrint( buffer );
1308
1309 m_currentRed = red;
1310 m_currentBlue = blue;
1311 m_currentGreen = green;
1312 }
1313 }
1314
1315 void wxPostScriptDCImpl::DoDrawText( const wxString& text, wxCoord x, wxCoord y )
1316 {
1317 wxCHECK_RET( m_ok, wxT("invalid postscript dc") );
1318
1319 const wxWX2MBbuf textbuf = text.mb_str();
1320 if ( !textbuf )
1321 return;
1322
1323 if (m_textForegroundColour.IsOk())
1324 {
1325 unsigned char red = m_textForegroundColour.Red();
1326 unsigned char blue = m_textForegroundColour.Blue();
1327 unsigned char green = m_textForegroundColour.Green();
1328
1329 if (!m_colour)
1330 {
1331 // Anything not white is black
1332 if (! (red == (unsigned char) 255 &&
1333 blue == (unsigned char) 255 &&
1334 green == (unsigned char) 255))
1335 {
1336 red = (unsigned char) 0;
1337 green = (unsigned char) 0;
1338 blue = (unsigned char) 0;
1339 }
1340 }
1341
1342 // maybe setgray here ?
1343 if (!(red == m_currentRed && green == m_currentGreen && blue == m_currentBlue))
1344 {
1345 double redPS = (double)(red) / 255.0;
1346 double bluePS = (double)(blue) / 255.0;
1347 double greenPS = (double)(green) / 255.0;
1348
1349 wxString buffer;
1350 buffer.Printf( "%f %f %f setrgbcolor\n", redPS, greenPS, bluePS );
1351 buffer.Replace( ",", "." );
1352 PsPrint( buffer );
1353
1354 m_currentRed = red;
1355 m_currentBlue = blue;
1356 m_currentGreen = green;
1357 }
1358 }
1359
1360 wxCoord text_w, text_h, text_descent;
1361
1362 GetOwner()->GetTextExtent(text, &text_w, &text_h, &text_descent);
1363
1364 int size = m_font.GetPointSize();
1365
1366 // wxCoord by = y + (wxCoord)floor( double(size) * 2.0 / 3.0 ); // approximate baseline
1367 // commented by V. Slavik and replaced by accurate version
1368 // - note that there is still rounding error in text_descent!
1369 wxCoord by = y + size - text_descent; // baseline
1370
1371 wxString buffer;
1372 buffer.Printf( "%f %f moveto\n", XLOG2DEV(x), YLOG2DEV(by) );
1373 buffer.Replace( ",", "." );
1374 PsPrint( buffer );
1375 PsPrint( "(" );
1376
1377 for ( const char *p = textbuf; *p != '\0'; p++ )
1378 {
1379 int c = (unsigned char)*p;
1380 if (c == ')' || c == '(' || c == '\\')
1381 {
1382 /* Cope with special characters */
1383 PsPrint( "\\" );
1384 PsPrint( (char) c );
1385 }
1386 else if ( c >= 128 )
1387 {
1388 /* Cope with character codes > 127 */
1389 buffer.Printf( "\\%o", c );
1390 PsPrint( buffer );
1391 }
1392 else
1393 {
1394 PsPrint( (char) c );
1395 }
1396 }
1397
1398 PsPrint( ") show\n" );
1399
1400 if (m_font.GetUnderlined())
1401 {
1402 wxCoord uy = (wxCoord)(y + size - m_underlinePosition);
1403
1404 buffer.Printf( "gsave\n"
1405 "%f %f moveto\n"
1406 "%f setlinewidth\n"
1407 "%f %f lineto\n"
1408 "stroke\n"
1409 "grestore\n",
1410 XLOG2DEV(x), YLOG2DEV(uy),
1411 m_underlineThickness,
1412 XLOG2DEV(x + text_w), YLOG2DEV(uy) );
1413 buffer.Replace( ",", "." );
1414 PsPrint( buffer );
1415 }
1416
1417 CalcBoundingBox( x, y );
1418 CalcBoundingBox( x + size * text.length() * 2/3 , y );
1419 }
1420
1421 void wxPostScriptDCImpl::DoDrawRotatedText( const wxString& text, wxCoord x, wxCoord y, double angle )
1422 {
1423 if ( wxIsNullDouble(angle) )
1424 {
1425 DoDrawText(text, x, y);
1426 return;
1427 }
1428
1429 wxCHECK_RET( m_ok, wxT("invalid postscript dc") );
1430
1431 SetFont( m_font );
1432
1433 if (m_textForegroundColour.IsOk())
1434 {
1435 unsigned char red = m_textForegroundColour.Red();
1436 unsigned char blue = m_textForegroundColour.Blue();
1437 unsigned char green = m_textForegroundColour.Green();
1438
1439 if (!m_colour)
1440 {
1441 // Anything not white is black
1442 if (! (red == (unsigned char) 255 &&
1443 blue == (unsigned char) 255 &&
1444 green == (unsigned char) 255))
1445 {
1446 red = (unsigned char) 0;
1447 green = (unsigned char) 0;
1448 blue = (unsigned char) 0;
1449 }
1450 }
1451
1452 // maybe setgray here ?
1453 if (!(red == m_currentRed && green == m_currentGreen && blue == m_currentBlue))
1454 {
1455 double redPS = (double)(red) / 255.0;
1456 double bluePS = (double)(blue) / 255.0;
1457 double greenPS = (double)(green) / 255.0;
1458
1459 wxString buffer;
1460 buffer.Printf( "%f %f %f setrgbcolor\n", redPS, greenPS, bluePS );
1461 buffer.Replace( ",", "." );
1462 PsPrint( buffer );
1463
1464 m_currentRed = red;
1465 m_currentBlue = blue;
1466 m_currentGreen = green;
1467 }
1468 }
1469
1470 int size = m_font.GetPointSize();
1471
1472 wxString buffer;
1473 buffer.Printf( "%f %f moveto\n", XLOG2DEV(x), YLOG2DEV(y));
1474 buffer.Replace( ",", "." );
1475 PsPrint( buffer );
1476
1477 buffer.Printf( "%f rotate\n", angle );
1478 buffer.Replace( ",", "." );
1479 PsPrint( buffer );
1480
1481 PsPrint( "(" );
1482 const wxWX2MBbuf textbuf = text.mb_str();
1483 if ( textbuf )
1484 {
1485 for ( const char *p = textbuf; *p != '\0'; p++ )
1486 {
1487 int c = (unsigned char)*p;
1488 if (c == ')' || c == '(' || c == '\\')
1489 {
1490 /* Cope with special characters */
1491 PsPrint( "\\" );
1492 PsPrint( (char) c );
1493 }
1494 else if ( c >= 128 )
1495 {
1496 /* Cope with character codes > 127 */
1497 buffer.Printf( "\\%o", c);
1498 PsPrint( buffer );
1499 }
1500 else
1501 {
1502 PsPrint( (char) c );
1503 }
1504 }
1505 }
1506
1507 PsPrint( ") show\n" );
1508
1509 buffer.Printf( "%f rotate\n", -angle );
1510 buffer.Replace( ",", "." );
1511 PsPrint( buffer );
1512
1513 if (m_font.GetUnderlined())
1514 {
1515 wxCoord uy = (wxCoord)(y + size - m_underlinePosition);
1516 wxCoord w, h;
1517 GetOwner()->GetTextExtent(text, &w, &h);
1518
1519 buffer.Printf(
1520 "gsave\n"
1521 "%f %f moveto\n"
1522 "%f setlinewidth\n"
1523 "%f %f lineto\n"
1524 "stroke\n"
1525 "grestore\n",
1526 XLOG2DEV(x), YLOG2DEV(uy),
1527 m_underlineThickness,
1528 XLOG2DEV(x + w), YLOG2DEV(uy) );
1529 buffer.Replace( ",", "." );
1530 PsPrint( buffer );
1531 }
1532
1533 CalcBoundingBox( x, y );
1534 CalcBoundingBox( x + size * text.length() * 2/3 , y );
1535 }
1536
1537 void wxPostScriptDCImpl::SetBackground (const wxBrush& brush)
1538 {
1539 m_backgroundBrush = brush;
1540 }
1541
1542 void wxPostScriptDCImpl::SetLogicalFunction(wxRasterOperationMode WXUNUSED(function))
1543 {
1544 wxFAIL_MSG( wxT("wxPostScriptDCImpl::SetLogicalFunction not implemented.") );
1545 }
1546
1547 #if wxUSE_SPLINES
1548 void wxPostScriptDCImpl::DoDrawSpline( const wxPointList *points )
1549 {
1550 wxCHECK_RET( m_ok, wxT("invalid postscript dc") );
1551
1552 SetPen( m_pen );
1553
1554 // a and b are not used
1555 //double a, b;
1556 double c, d, x1, y1, x2, y2, x3, y3;
1557 wxPoint *p, *q;
1558
1559 wxPointList::compatibility_iterator node = points->GetFirst();
1560 p = node->GetData();
1561 x1 = p->x;
1562 y1 = p->y;
1563
1564 node = node->GetNext();
1565 p = node->GetData();
1566 c = p->x;
1567 d = p->y;
1568 x3 =
1569 #if 0
1570 a =
1571 #endif
1572 (double)(x1 + c) / 2;
1573 y3 =
1574 #if 0
1575 b =
1576 #endif
1577 (double)(y1 + d) / 2;
1578
1579 wxString buffer;
1580 buffer.Printf( "newpath\n"
1581 "%f %f moveto\n"
1582 "%f %f lineto\n",
1583 XLOG2DEV(wxRound(x1)), YLOG2DEV(wxRound(y1)),
1584 XLOG2DEV(wxRound(x3)), YLOG2DEV(wxRound(y3)) );
1585 buffer.Replace( ",", "." );
1586 PsPrint( buffer );
1587
1588 CalcBoundingBox( (wxCoord)x1, (wxCoord)y1 );
1589 CalcBoundingBox( (wxCoord)x3, (wxCoord)y3 );
1590
1591 node = node->GetNext();
1592 while (node)
1593 {
1594 q = node->GetData();
1595
1596 x1 = x3;
1597 y1 = y3;
1598 x2 = c;
1599 y2 = d;
1600 c = q->x;
1601 d = q->y;
1602 x3 = (double)(x2 + c) / 2;
1603 y3 = (double)(y2 + d) / 2;
1604
1605 buffer.Printf( "%f %f %f %f %f %f DrawSplineSection\n",
1606 XLOG2DEV(wxRound(x1)), YLOG2DEV(wxRound(y1)),
1607 XLOG2DEV(wxRound(x2)), YLOG2DEV(wxRound(y2)),
1608 XLOG2DEV(wxRound(x3)), YLOG2DEV(wxRound(y3)) );
1609 buffer.Replace( ",", "." );
1610 PsPrint( buffer );
1611
1612 CalcBoundingBox( (wxCoord)x1, (wxCoord)y1 );
1613 CalcBoundingBox( (wxCoord)x3, (wxCoord)y3 );
1614
1615 node = node->GetNext();
1616 }
1617
1618 /*
1619 At this point, (x2,y2) and (c,d) are the position of the
1620 next-to-last and last point respectively, in the point list
1621 */
1622
1623 buffer.Printf( "%f %f lineto\nstroke\n", XLOG2DEV(wxRound(c)), YLOG2DEV(wxRound(d)) );
1624 buffer.Replace( ",", "." );
1625 PsPrint( buffer );
1626 }
1627 #endif // wxUSE_SPLINES
1628
1629 wxCoord wxPostScriptDCImpl::GetCharWidth() const
1630 {
1631 // Chris Breeze: reasonable approximation using wxMODERN/Courier
1632 return (wxCoord) (GetCharHeight() * 72.0 / 120.0);
1633 }
1634
1635 void wxPostScriptDCImpl::SetPrintData(const wxPrintData& data)
1636 {
1637 m_printData = data;
1638
1639 wxPaperSize id = m_printData.GetPaperId();
1640 wxPrintPaperType *paper = wxThePrintPaperDatabase->FindPaperType(id);
1641 if (!paper) paper = wxThePrintPaperDatabase->FindPaperType(wxPAPER_A4);
1642 int w = 595;
1643 int h = 842;
1644 if (paper)
1645 {
1646 w = paper->GetSizeDeviceUnits().x;
1647 h = paper->GetSizeDeviceUnits().y;
1648 }
1649
1650 if (m_printData.GetOrientation() == wxLANDSCAPE)
1651 m_pageHeight = w * PS2DEV;
1652 else
1653 m_pageHeight = h * PS2DEV;
1654 }
1655
1656 void wxPostScriptDCImpl::ComputeScaleAndOrigin()
1657 {
1658 const wxRealPoint origScale(m_scaleX, m_scaleY);
1659
1660 wxDCImpl::ComputeScaleAndOrigin();
1661
1662 // If scale has changed call SetPen to recalulate the line width
1663 // and SetFont to recalculate font size
1664 if ( wxRealPoint(m_scaleX, m_scaleY) != origScale && m_pen.IsOk() )
1665 {
1666 SetPen( m_pen );
1667 SetFont( m_font );
1668 }
1669 }
1670
1671 void wxPostScriptDCImpl::DoGetSize(int* width, int* height) const
1672 {
1673 wxPaperSize id = m_printData.GetPaperId();
1674
1675 wxPrintPaperType *paper = wxThePrintPaperDatabase->FindPaperType(id);
1676
1677 if (!paper) paper = wxThePrintPaperDatabase->FindPaperType(wxPAPER_A4);
1678
1679 int w = 595;
1680 int h = 842;
1681 if (paper)
1682 {
1683 w = paper->GetSizeDeviceUnits().x;
1684 h = paper->GetSizeDeviceUnits().y;
1685 }
1686
1687 if (m_printData.GetOrientation() == wxLANDSCAPE)
1688 {
1689 int tmp = w;
1690 w = h;
1691 h = tmp;
1692 }
1693
1694 if (width)
1695 *width = wxRound( w * PS2DEV );
1696
1697 if (height)
1698 *height = wxRound( h * PS2DEV );
1699 }
1700
1701 void wxPostScriptDCImpl::DoGetSizeMM(int *width, int *height) const
1702 {
1703 wxPaperSize id = m_printData.GetPaperId();
1704
1705 wxPrintPaperType *paper = wxThePrintPaperDatabase->FindPaperType(id);
1706
1707 if (!paper) paper = wxThePrintPaperDatabase->FindPaperType(wxPAPER_A4);
1708
1709 int w = 210;
1710 int h = 297;
1711 if (paper)
1712 {
1713 w = paper->GetWidth() / 10;
1714 h = paper->GetHeight() / 10;
1715 }
1716
1717 if (m_printData.GetOrientation() == wxLANDSCAPE)
1718 {
1719 int tmp = w;
1720 w = h;
1721 h = tmp;
1722 }
1723
1724 if (width) *width = w;
1725 if (height) *height = h;
1726 }
1727
1728 // Resolution in pixels per logical inch
1729 wxSize wxPostScriptDCImpl::GetPPI(void) const
1730 {
1731 return wxSize( DPI, DPI );
1732 }
1733
1734
1735 bool wxPostScriptDCImpl::StartDoc( const wxString& WXUNUSED(message) )
1736 {
1737 wxCHECK_MSG( m_ok, false, wxT("invalid postscript dc") );
1738
1739 if (m_printData.GetPrintMode() != wxPRINT_MODE_STREAM )
1740 {
1741 if (m_printData.GetFilename() == wxEmptyString)
1742 {
1743 wxString filename = wxFileName::CreateTempFileName( wxT("ps") );
1744 m_printData.SetFilename(filename);
1745 }
1746
1747 m_pstream = wxFopen( m_printData.GetFilename(), wxT("w+") );
1748
1749 if (!m_pstream)
1750 {
1751 wxLogError( _("Cannot open file for PostScript printing!"));
1752 m_ok = false;
1753 return false;
1754 }
1755 }
1756
1757 m_ok = true;
1758
1759 wxString buffer;
1760
1761 PsPrint( "%!PS-Adobe-2.0\n" );
1762
1763 PsPrint( "%%Creator: wxWidgets PostScript renderer\n" );
1764
1765 buffer.Printf( "%%%%CreationDate: %s\n", wxNow() );
1766 PsPrint( buffer );
1767
1768 if (m_printData.GetOrientation() == wxLANDSCAPE)
1769 PsPrint( "%%Orientation: Landscape\n" );
1770 else
1771 PsPrint( "%%Orientation: Portrait\n" );
1772
1773 const wxChar *paper;
1774 switch (m_printData.GetPaperId())
1775 {
1776 case wxPAPER_LETTER: paper = wxT("Letter"); break; // Letter: paper ""; 8 1/2 by 11 inches
1777 case wxPAPER_LEGAL: paper = wxT("Legal"); break; // Legal, 8 1/2 by 14 inches
1778 case wxPAPER_A4: paper = wxT("A4"); break; // A4 Sheet, 210 by 297 millimeters
1779 case wxPAPER_TABLOID: paper = wxT("Tabloid"); break; // Tabloid, 11 by 17 inches
1780 case wxPAPER_LEDGER: paper = wxT("Ledger"); break; // Ledger, 17 by 11 inches
1781 case wxPAPER_STATEMENT: paper = wxT("Statement"); break; // Statement, 5 1/2 by 8 1/2 inches
1782 case wxPAPER_EXECUTIVE: paper = wxT("Executive"); break; // Executive, 7 1/4 by 10 1/2 inches
1783 case wxPAPER_A3: paper = wxT("A3"); break; // A3 sheet, 297 by 420 millimeters
1784 case wxPAPER_A5: paper = wxT("A5"); break; // A5 sheet, 148 by 210 millimeters
1785 case wxPAPER_B4: paper = wxT("B4"); break; // B4 sheet, 250 by 354 millimeters
1786 case wxPAPER_B5: paper = wxT("B5"); break; // B5 sheet, 182-by-257-millimeter paper
1787 case wxPAPER_FOLIO: paper = wxT("Folio"); break; // Folio, 8-1/2-by-13-inch paper
1788 case wxPAPER_QUARTO: paper = wxT("Quaro"); break; // Quarto, 215-by-275-millimeter paper
1789 case wxPAPER_10X14: paper = wxT("10x14"); break; // 10-by-14-inch sheet
1790 default: paper = wxT("A4");
1791 }
1792
1793 buffer.Printf( "%%%%DocumentPaperSizes: %s\n", paper );
1794 PsPrint( buffer );
1795
1796 PsPrint( "%%EndComments\n\n" );
1797
1798 PsPrint( "%%BeginProlog\n" );
1799 PsPrint( wxPostScriptHeaderConicTo );
1800 PsPrint( wxPostScriptHeaderEllipse );
1801 PsPrint( wxPostScriptHeaderEllipticArc );
1802 PsPrint( wxPostScriptHeaderColourImage );
1803 PsPrint( wxPostScriptHeaderReencodeISO1 );
1804 PsPrint( wxPostScriptHeaderReencodeISO2 );
1805 if (wxPostScriptHeaderSpline)
1806 PsPrint( wxPostScriptHeaderSpline );
1807 PsPrint( "%%EndProlog\n" );
1808
1809 SetBrush( *wxBLACK_BRUSH );
1810 SetPen( *wxBLACK_PEN );
1811 SetBackground( *wxWHITE_BRUSH );
1812 SetTextForeground( *wxBLACK );
1813
1814 // set origin according to paper size
1815 SetDeviceOrigin( 0,0 );
1816
1817 m_pageNumber = 1;
1818 return true;
1819 }
1820
1821 void wxPostScriptDCImpl::EndDoc ()
1822 {
1823 wxCHECK_RET( m_ok, wxT("invalid postscript dc") );
1824
1825 if (m_clipping)
1826 {
1827 m_clipping = false;
1828 PsPrint( "grestore\n" );
1829 }
1830
1831 if ( m_pstream ) {
1832 fclose( m_pstream );
1833 m_pstream = NULL;
1834 }
1835
1836 #if 0
1837 // THE FOLLOWING HAS BEEN CONTRIBUTED BY Andy Fyfe <andy@hyperparallel.com>
1838 wxCoord wx_printer_translate_x, wx_printer_translate_y;
1839 double wx_printer_scale_x, wx_printer_scale_y;
1840
1841 wx_printer_translate_x = (wxCoord)m_printData.GetPrinterTranslateX();
1842 wx_printer_translate_y = (wxCoord)m_printData.GetPrinterTranslateY();
1843
1844 wx_printer_scale_x = m_printData.GetPrinterScaleX();
1845 wx_printer_scale_y = m_printData.GetPrinterScaleY();
1846
1847 // Compute the bounding box. Note that it is in the default user
1848 // coordinate system, thus we have to convert the values.
1849 wxCoord minX = (wxCoord) XLOG2DEV(m_minX);
1850 wxCoord minY = (wxCoord) YLOG2DEV(m_minY);
1851 wxCoord maxX = (wxCoord) XLOG2DEV(m_maxX);
1852 wxCoord maxY = (wxCoord) YLOG2DEV(m_maxY);
1853
1854 // LOG2DEV may have changed the minimum to maximum vice versa
1855 if ( minX > maxX ) { wxCoord tmp = minX; minX = maxX; maxX = tmp; }
1856 if ( minY > maxY ) { wxCoord tmp = minY; minY = maxY; maxY = tmp; }
1857
1858 // account for used scaling (boundingbox is before scaling in ps-file)
1859 double scale_x = m_printData.GetPrinterScaleX() / ms_PSScaleFactor;
1860 double scale_y = m_printData.GetPrinterScaleY() / ms_PSScaleFactor;
1861
1862 wxCoord llx, lly, urx, ury;
1863 llx = (wxCoord) ((minX+wx_printer_translate_x)*scale_x);
1864 lly = (wxCoord) ((minY+wx_printer_translate_y)*scale_y);
1865 urx = (wxCoord) ((maxX+wx_printer_translate_x)*scale_x);
1866 ury = (wxCoord) ((maxY+wx_printer_translate_y)*scale_y);
1867 // (end of bounding box computation)
1868
1869
1870 // If we're landscape, our sense of "x" and "y" is reversed.
1871 if (m_printData.GetOrientation() == wxLANDSCAPE)
1872 {
1873 wxCoord tmp;
1874 tmp = llx; llx = lly; lly = tmp;
1875 tmp = urx; urx = ury; ury = tmp;
1876
1877 // We need either the two lines that follow, or we need to subtract
1878 // min_x from real_translate_y, which is commented out below.
1879 llx = llx - (wxCoord)(m_minX*wx_printer_scale_y);
1880 urx = urx - (wxCoord)(m_minX*wx_printer_scale_y);
1881 }
1882
1883 // The Adobe specifications call for integers; we round as to make
1884 // the bounding larger.
1885 PsPrintf( wxT("%%%%BoundingBox: %d %d %d %d\n"),
1886 (wxCoord)floor((double)llx), (wxCoord)floor((double)lly),
1887 (wxCoord)ceil((double)urx), (wxCoord)ceil((double)ury) );
1888
1889 // To check the correctness of the bounding box, postscript commands
1890 // to draw a box corresponding to the bounding box are generated below.
1891 // But since we typically don't want to print such a box, the postscript
1892 // commands are generated within comments. These lines appear before any
1893 // adjustment of scale, rotation, or translation, and hence are in the
1894 // default user coordinates.
1895 PsPrint( "% newpath\n" );
1896 PsPrintf( wxT("%% %d %d moveto\n"), llx, lly );
1897 PsPrintf( wxT("%% %d %d lineto\n"), urx, lly );
1898 PsPrintf( wxT("%% %d %d lineto\n"), urx, ury );
1899 PsPrintf( wxT("%% %d %d lineto closepath stroke\n"), llx, ury );
1900 #endif
1901
1902 #ifndef __WXMSW__
1903 wxPostScriptPrintNativeData *data =
1904 (wxPostScriptPrintNativeData *) m_printData.GetNativeData();
1905
1906 if (m_ok && (m_printData.GetPrintMode() == wxPRINT_MODE_PRINTER))
1907 {
1908 wxString command;
1909 command += data->GetPrinterCommand();
1910 command += wxT(" ");
1911 command += data->GetPrinterOptions();
1912 command += wxT(" ");
1913 command += m_printData.GetFilename();
1914
1915 wxExecute( command, true );
1916 wxRemoveFile( m_printData.GetFilename() );
1917 }
1918 #endif
1919 }
1920
1921 void wxPostScriptDCImpl::StartPage()
1922 {
1923 wxCHECK_RET( m_ok, wxT("invalid postscript dc") );
1924
1925 wxString buffer;
1926 buffer.Printf( wxT("%%%%Page: %d\n"), m_pageNumber++ );
1927 PsPrint( buffer );
1928
1929 #if 0
1930 wxPostScriptPrintNativeData *data =
1931 (wxPostScriptPrintNativeData *) m_printData.GetNativeData();
1932
1933 wxCoord translate_x = (wxCoord)data->GetPrinterTranslateX();
1934 wxCoord translate_y = (wxCoord)data->GetPrinterTranslateY();
1935
1936 buffer.Printf( "%d %d translate\n", translate_x, translate_y );
1937 PsPrint( buffer );
1938
1939 double scale_x = data->GetPrinterScaleX();
1940 double scale_y = data->GetPrinterScaleY();
1941
1942 buffer.Printf( "%f %f scale\n", scale_x, scale_y );
1943 buffer.Replace( ",", "." );
1944 PsPrint( buffer );
1945
1946 #endif
1947
1948 // Each page starts with an "initgraphics" which resets the
1949 // transformation and so we need to rotate the page for
1950 // landscape printing)
1951
1952 // I copied this one from a PostScript tutorial, but to no avail. RR.
1953 // PsPrint( "90 rotate llx neg ury nef translate\n" );
1954
1955 if (m_printData.GetOrientation() == wxLANDSCAPE)
1956 PsPrint( "90 rotate\n" );
1957 }
1958
1959 void wxPostScriptDCImpl::EndPage ()
1960 {
1961 wxCHECK_RET( m_ok , wxT("invalid postscript dc") );
1962
1963 PsPrint( "showpage\n" );
1964 }
1965
1966 bool wxPostScriptDCImpl::DoBlit( wxCoord xdest, wxCoord ydest,
1967 wxCoord fwidth, wxCoord fheight,
1968 wxDC *source,
1969 wxCoord xsrc, wxCoord ysrc,
1970 wxRasterOperationMode rop,
1971 bool WXUNUSED(useMask), wxCoord WXUNUSED(xsrcMask), wxCoord WXUNUSED(ysrcMask) )
1972 {
1973 wxCHECK_MSG( m_ok, false, wxT("invalid postscript dc") );
1974
1975 wxCHECK_MSG( source, false, wxT("invalid source dc") );
1976
1977 // blit into a bitmap
1978 wxBitmap bitmap( (int)fwidth, (int)fheight );
1979 wxMemoryDC memDC;
1980 memDC.SelectObject(bitmap);
1981 memDC.Blit(0, 0, fwidth, fheight, source, xsrc, ysrc, rop); /* TODO: Blit transparently? */
1982 memDC.SelectObject(wxNullBitmap);
1983
1984 //draw bitmap. scaling and positioning is done there
1985 GetOwner()->DrawBitmap( bitmap, xdest, ydest );
1986
1987 return true;
1988 }
1989
1990 wxCoord wxPostScriptDCImpl::GetCharHeight() const
1991 {
1992 if (m_font.IsOk())
1993 return m_font.GetPointSize();
1994 else
1995 return 12;
1996 }
1997
1998 void wxPostScriptDCImpl::PsPrint( const wxString& str )
1999 {
2000 const wxCharBuffer psdata(str.utf8_str());
2001
2002 wxPostScriptPrintNativeData *data =
2003 (wxPostScriptPrintNativeData *) m_printData.GetNativeData();
2004
2005 switch (m_printData.GetPrintMode())
2006 {
2007 #if wxUSE_STREAMS
2008 // append to output stream
2009 case wxPRINT_MODE_STREAM:
2010 {
2011 wxOutputStream* outputstream = data->GetOutputStream();
2012 wxCHECK_RET( outputstream, wxT("invalid outputstream") );
2013 outputstream->Write( psdata, strlen( psdata ) );
2014 }
2015 break;
2016 #endif // wxUSE_STREAMS
2017
2018 // save data into file
2019 default:
2020 wxCHECK_RET( m_pstream, wxT("invalid postscript dc") );
2021 fwrite( psdata, 1, strlen( psdata ), m_pstream );
2022 }
2023 }
2024
2025 void wxPostScriptDCImpl::DoGetTextExtent(const wxString& string,
2026 wxCoord *x, wxCoord *y,
2027 wxCoord *descent, wxCoord *externalLeading,
2028 const wxFont *theFont ) const
2029 {
2030 const wxFont *fontToUse = theFont;
2031
2032 if (!fontToUse) fontToUse = &m_font;
2033
2034 const float fontSize =
2035 fontToUse->GetPointSize() * GetFontPointSizeAdjustment(72.0);
2036
2037 if (string.empty())
2038 {
2039 if (x) (*x) = 0;
2040 if (y) (*y) = 0;
2041 if (descent) (*descent) = 0;
2042 if (externalLeading) (*externalLeading) = 0;
2043 return;
2044 }
2045
2046 // GTK 2.0
2047
2048 const wxWX2MBbuf strbuf = string.mb_str();
2049
2050 // conversion failed (non e.g. ISO characters)
2051 if ( !strbuf )
2052 return;
2053
2054 #if !wxUSE_AFM_FOR_POSTSCRIPT
2055 /* Provide a VERY rough estimate (avoid using it).
2056 * Produces accurate results for mono-spaced font
2057 * such as Courier (aka wxMODERN) */
2058
2059 if ( x )
2060 *x = strlen (strbuf) * fontSize * 72.0 / 120.0;
2061 if ( y )
2062 *y = (wxCoord) (fontSize * 1.32); /* allow for descender */
2063 if (descent) *descent = 0;
2064 if (externalLeading) *externalLeading = 0;
2065 #else
2066
2067 /* method for calculating string widths in postscript:
2068 / read in the AFM (adobe font metrics) file for the
2069 / actual font, parse it and extract the character widths
2070 / and also the descender. this may be improved, but for now
2071 / it works well. the AFM file is only read in if the
2072 / font is changed. this may be chached in the future.
2073 / calls to GetTextExtent with the font unchanged are rather
2074 / efficient!!!
2075 /
2076 / for each font and style used there is an AFM file necessary.
2077 / currently i have only files for the roman font family.
2078 / I try to get files for the other ones!
2079 /
2080 / CAVE: the size of the string is currently always calculated
2081 / in 'points' (1/72 of an inch). this should later on be
2082 / changed to depend on the mapping mode.
2083 / CAVE: the path to the AFM files must be set before calling this
2084 / fun3B3Bction. this is usually done by a call like the following:
2085 / wxSetAFMPath("d:\\wxw161\\afm\\");
2086 /
2087 / example:
2088 /
2089 / wxPostScriptDC dc(NULL, true);
2090 / if (dc.IsOk()){
2091 / wxSetAFMPath("d:\\wxw161\\afm\\");
2092 / dc.StartDoc("Test");
2093 / dc.StartPage();
2094 / wxCoord w,h;
2095 / dc.SetFont(new wxFont(10, wxROMAN, wxNORMAL, wxNORMAL));
2096 / dc.GetTextExtent("Hallo",&w,&h);
2097 / dc.EndPage();
2098 / dc.EndDoc();
2099 / }
2100 /
2101 / by steve (stefan.hammes@urz.uni-heidelberg.de)
2102 / created: 10.09.94
2103 / updated: 14.05.95 */
2104
2105 /* these static vars are for storing the state between calls */
2106 static int lastFamily= INT_MIN;
2107 static int lastSize= INT_MIN;
2108 static int lastStyle= INT_MIN;
2109 static int lastWeight= INT_MIN;
2110 static int lastDescender = INT_MIN;
2111 static int lastWidths[256]; /* widths of the characters */
2112
2113 double UnderlinePosition = 0.0;
2114 double UnderlineThickness = 0.0;
2115
2116 // Get actual parameters
2117 int Family = fontToUse->GetFamily();
2118 int Size = fontToUse->GetPointSize();
2119 int Style = fontToUse->GetStyle();
2120 int Weight = fontToUse->GetWeight();
2121
2122 // If we have another font, read the font-metrics
2123 if (Family!=lastFamily || Size!=lastSize || Style!=lastStyle || Weight!=lastWeight)
2124 {
2125 // Store actual values
2126 lastFamily = Family;
2127 lastSize = Size;
2128 lastStyle = Style;
2129 lastWeight = Weight;
2130
2131 const wxChar *name;
2132
2133 switch (Family)
2134 {
2135 case wxMODERN:
2136 case wxTELETYPE:
2137 {
2138 if ((Style == wxITALIC) && (Weight == wxBOLD)) name = wxT("CourBoO.afm");
2139 else if ((Style != wxITALIC) && (Weight == wxBOLD)) name = wxT("CourBo.afm");
2140 else if ((Style == wxITALIC) && (Weight != wxBOLD)) name = wxT("CourO.afm");
2141 else name = wxT("Cour.afm");
2142 break;
2143 }
2144 case wxROMAN:
2145 {
2146 if ((Style == wxITALIC) && (Weight == wxBOLD)) name = wxT("TimesBoO.afm");
2147 else if ((Style != wxITALIC) && (Weight == wxBOLD)) name = wxT("TimesBo.afm");
2148 else if ((Style == wxITALIC) && (Weight != wxBOLD)) name = wxT("TimesO.afm");
2149 else name = wxT("TimesRo.afm");
2150 break;
2151 }
2152 case wxSCRIPT:
2153 {
2154 name = wxT("Zapf.afm");
2155 break;
2156 }
2157 case wxSWISS:
2158 default:
2159 {
2160 if ((Style == wxITALIC) && (Weight == wxBOLD)) name = wxT("HelvBoO.afm");
2161 else if ((Style != wxITALIC) && (Weight == wxBOLD)) name = wxT("HelvBo.afm");
2162 else if ((Style == wxITALIC) && (Weight != wxBOLD)) name = wxT("HelvO.afm");
2163 else name = wxT("Helv.afm");
2164 break;
2165 }
2166 }
2167
2168 FILE *afmFile = NULL;
2169
2170 // Get the directory of the AFM files
2171 wxString afmName;
2172
2173 // VZ: I don't know if the cast always works under Unix but it clearly
2174 // never does under Windows where the pointer is
2175 // wxWindowsPrintNativeData and so calling GetFontMetricPath() on
2176 // it just crashes
2177 #ifndef __WIN32__
2178 wxPostScriptPrintNativeData *data =
2179 wxDynamicCast(m_printData.GetNativeData(), wxPostScriptPrintNativeData);
2180
2181 if (data && !data->GetFontMetricPath().empty())
2182 {
2183 afmName = data->GetFontMetricPath();
2184 afmName << wxFILE_SEP_PATH << name;
2185 }
2186 #endif // __WIN32__
2187
2188 if ( !afmName.empty() )
2189 afmFile = wxFopen(afmName, wxT("r"));
2190
2191 if ( !afmFile )
2192 {
2193 #if defined(__UNIX__) && !defined(__VMS__)
2194 afmName = wxGetDataDir();
2195 #else // !__UNIX__
2196 afmName = wxStandardPaths::Get().GetDataDir();
2197 #endif // __UNIX__/!__UNIX__
2198
2199 afmName << wxFILE_SEP_PATH
2200 #if defined(__LINUX__) || defined(__FREEBSD__)
2201 << wxT("gs_afm") << wxFILE_SEP_PATH
2202 #else
2203 << wxT("afm") << wxFILE_SEP_PATH
2204 #endif
2205 << name;
2206 afmFile = wxFopen(afmName,wxT("r"));
2207 }
2208
2209 /* 2. open and process the file
2210 / a short explanation of the AFM format:
2211 / we have for each character a line, which gives its size
2212 / e.g.:
2213 /
2214 / C 63 ; WX 444 ; N question ; B 49 -14 395 676 ;
2215 /
2216 / that means, we have a character with ascii code 63, and width
2217 / (444/1000 * fontSize) points.
2218 / the other data is ignored for now!
2219 /
2220 / when the font has changed, we read in the right AFM file and store the
2221 / character widths in an array, which is processed below (see point 3.). */
2222 if (afmFile==NULL)
2223 {
2224 wxLogDebug( wxT("GetTextExtent: can't open AFM file '%s'"), afmName.c_str() );
2225 wxLogDebug( wxT(" using approximate values"));
2226 for (int i=0; i<256; i++) lastWidths[i] = 500; /* an approximate value */
2227 lastDescender = -150; /* dito. */
2228 }
2229 else
2230 {
2231 /* init the widths array */
2232 for(int i=0; i<256; i++) lastWidths[i] = INT_MIN;
2233 /* some variables for holding parts of a line */
2234 char cString[10], semiString[10], WXString[10];
2235 char descString[20];
2236 char upString[30], utString[30];
2237 char encString[50];
2238 char line[256];
2239 int ascii,cWidth;
2240 /* read in the file and parse it */
2241 while(fgets(line,sizeof(line),afmFile)!=NULL)
2242 {
2243 /* A.) check for descender definition */
2244 if (strncmp(line,"Descender",9)==0)
2245 {
2246 if ((sscanf(line,"%s%d",descString,&lastDescender)!=2) ||
2247 (strcmp(descString,"Descender")!=0))
2248 {
2249 wxLogDebug( wxT("AFM-file '%s': line '%s' has error (bad descender)"), afmName.c_str(),line );
2250 }
2251 }
2252 /* JC 1.) check for UnderlinePosition */
2253 else if(strncmp(line,"UnderlinePosition",17)==0)
2254 {
2255 if ((sscanf(line,"%s%lf",upString,&UnderlinePosition)!=2) ||
2256 (strcmp(upString,"UnderlinePosition")!=0))
2257 {
2258 wxLogDebug( wxT("AFM-file '%s': line '%s' has error (bad UnderlinePosition)"), afmName.c_str(), line );
2259 }
2260 }
2261 /* JC 2.) check for UnderlineThickness */
2262 else if(strncmp(line,"UnderlineThickness",18)==0)
2263 {
2264 if ((sscanf(line,"%s%lf",utString,&UnderlineThickness)!=2) ||
2265 (strcmp(utString,"UnderlineThickness")!=0))
2266 {
2267 wxLogDebug( wxT("AFM-file '%s': line '%s' has error (bad UnderlineThickness)"), afmName.c_str(), line );
2268 }
2269 }
2270 /* JC 3.) check for EncodingScheme */
2271 else if(strncmp(line,"EncodingScheme",14)==0)
2272 {
2273 if ((sscanf(line,"%s%s",utString,encString)!=2) ||
2274 (strcmp(utString,"EncodingScheme")!=0))
2275 {
2276 wxLogDebug( wxT("AFM-file '%s': line '%s' has error (bad EncodingScheme)"), afmName.c_str(), line );
2277 }
2278 else if (strncmp(encString, "AdobeStandardEncoding", 21))
2279 {
2280 wxLogDebug( wxT("AFM-file '%s': line '%s' has error (unsupported EncodingScheme %s)"),
2281 afmName.c_str(),line, encString);
2282 }
2283 }
2284 /* B.) check for char-width */
2285 else if(strncmp(line,"C ",2)==0)
2286 {
2287 if (sscanf(line,"%s%d%s%s%d",cString,&ascii,semiString,WXString,&cWidth)!=5)
2288 {
2289 wxLogDebug(wxT("AFM-file '%s': line '%s' has an error (bad character width)"),afmName.c_str(),line);
2290 }
2291 if(strcmp(cString,"C")!=0 || strcmp(semiString,";")!=0 || strcmp(WXString,"WX")!=0)
2292 {
2293 wxLogDebug(wxT("AFM-file '%s': line '%s' has a format error"),afmName.c_str(),line);
2294 }
2295 /* printf(" char '%c'=%d has width '%d'\n",ascii,ascii,cWidth); */
2296 if (ascii>=0 && ascii<256)
2297 {
2298 lastWidths[ascii] = cWidth; /* store width */
2299 }
2300 else
2301 {
2302 /* MATTHEW: this happens a lot; don't print an error */
2303 /* wxLogDebug("AFM-file '%s': ASCII value %d out of range",afmName.c_str(),ascii); */
2304 }
2305 }
2306 /* C.) ignore other entries. */
2307 }
2308 fclose(afmFile);
2309 }
2310 /* hack to compute correct values for german 'Umlaute'
2311 / the correct way would be to map the character names
2312 / like 'adieresis' to corresp. positions of ISOEnc and read
2313 / these values from AFM files, too. Maybe later ... */
2314
2315 // NB: casts to int are needed to suppress gcc 3.3 warnings
2316 lastWidths[196] = lastWidths[(int)'A']; // U+00C4 A Umlaute
2317 lastWidths[228] = lastWidths[(int)'a']; // U+00E4 a Umlaute
2318 lastWidths[214] = lastWidths[(int)'O']; // U+00D6 O Umlaute
2319 lastWidths[246] = lastWidths[(int)'o']; // U+00F6 o Umlaute
2320 lastWidths[220] = lastWidths[(int)'U']; // U+00DC U Umlaute
2321 lastWidths[252] = lastWidths[(int)'u']; // U+00FC u Umlaute
2322 lastWidths[223] = lastWidths[(int)251]; // U+00DF eszett (scharfes s)
2323
2324 /* JC: calculate UnderlineThickness/UnderlinePosition */
2325
2326 // VS: dirty, but is there any better solution?
2327 double *pt;
2328 pt = (double*) &m_underlinePosition;
2329 *pt = YLOG2DEVREL((wxCoord)(UnderlinePosition * fontSize)) / 1000.0f;
2330 pt = (double*) &m_underlineThickness;
2331 *pt = YLOG2DEVREL((wxCoord)(UnderlineThickness * fontSize)) / 1000.0f;
2332
2333 }
2334
2335
2336 /* 3. now the font metrics are read in, calc size this
2337 / is done by adding the widths of the characters in the
2338 / string. they are given in 1/1000 of the size! */
2339
2340 long sum=0;
2341 float height=fontSize; /* by default */
2342 unsigned char *p=(unsigned char *)wxMBSTRINGCAST strbuf;
2343 if(!p)
2344 {
2345 // String couldn't be converted which used to SEGV as reported here:
2346 // http://bugs.debian.org/702378
2347 // http://trac.wxwidgets.org/ticket/15300
2348 // Upstream suggests "just return if the conversion failed".
2349 if (x) (*x) = 0;
2350 if (y) (*y) = 0;
2351 if (descent) (*descent) = 0;
2352 if (externalLeading) (*externalLeading) = 0;
2353 return;
2354 }
2355 for(; *p; p++)
2356 {
2357 if(lastWidths[*p]== INT_MIN)
2358 {
2359 wxLogDebug(wxT("GetTextExtent: undefined width for character '%c' (%d)"), *p,*p);
2360 sum += lastWidths[(unsigned char)' ']; /* assume space */
2361 }
2362 else
2363 {
2364 sum += lastWidths[*p];
2365 }
2366 }
2367
2368 double widthSum = sum;
2369 widthSum *= fontSize;
2370 widthSum /= 1000.0F;
2371
2372 /* add descender to height (it is usually a negative value) */
2373 //if (lastDescender != INT_MIN)
2374 //{
2375 // height += (wxCoord)(((-lastDescender)/1000.0F) * Size); /* MATTHEW: forgot scale */
2376 //}
2377 // - commented by V. Slavik - height already contains descender in it
2378 // (judging from few experiments)
2379
2380 /* return size values */
2381 if ( x )
2382 *x = (wxCoord)widthSum;
2383 if ( y )
2384 *y = (wxCoord)height;
2385
2386 /* return other parameters */
2387 if (descent)
2388 {
2389 if(lastDescender!=INT_MIN)
2390 {
2391 *descent = (wxCoord)(((-lastDescender)/1000.0F) * fontSize); /* MATTHEW: forgot scale */
2392 }
2393 else
2394 {
2395 *descent = 0;
2396 }
2397 }
2398
2399 /* currently no idea how to calculate this! */
2400 if (externalLeading) *externalLeading = 0;
2401 #endif
2402 // Use AFM
2403 }
2404
2405
2406 #endif // wxUSE_PRINTING_ARCHITECTURE && wxUSE_POSTSCRIPT
2407
2408 // vi:sts=4:sw=4:et