]> git.saurik.com Git - wxWidgets.git/blob - src/gtk/dcps.cpp
OnExit() is called for modules which were initialized even if the init of
[wxWidgets.git] / src / gtk / dcps.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: dcps.cpp
3 // Purpose: wxPostScriptDC implementation
4 // Author: Julian Smart, Robert Roebling, Markus Holzhem
5 // Modified by:
6 // Created: 04/01/98
7 // RCS-ID: $Id$
8 // Copyright: (c) Julian Smart and Markus Holzem
9 // Licence: wxWindows license
10 /////////////////////////////////////////////////////////////////////////////
11
12 #ifdef __GNUG__
13 #pragma implementation
14 #pragma interface
15 #endif
16
17 #include "wx/defs.h"
18
19 #if wxUSE_POSTSCRIPT
20
21 #include "wx/postscrp.h"
22 #include "wx/dcmemory.h"
23 #include "wx/utils.h"
24 #include "wx/intl.h"
25 #include "wx/filedlg.h"
26 #include "wx/app.h"
27 #include "wx/msgdlg.h"
28 #include "wx/image.h"
29 #include "wx/log.h"
30
31 #include "gdk/gdk.h"
32 #include "gtk/gtk.h"
33
34 //-----------------------------------------------------------------------------
35 // start and end of document/page
36 //-----------------------------------------------------------------------------
37
38 static const char *wxPostScriptHeaderEllipse = "\
39 /ellipsedict 8 dict def\n\
40 ellipsedict /mtrx matrix put\n\
41 /ellipse {\n\
42 ellipsedict begin\n\
43 /endangle exch def\n\
44 /startangle exch def\n\
45 /yrad exch def\n\
46 /xrad exch def\n\
47 /y exch def\n\
48 /x exch def\n\
49 /savematrix mtrx currentmatrix def\n\
50 x y translate\n\
51 xrad yrad scale\n\
52 0 0 1 startangle endangle arc\n\
53 savematrix setmatrix\n\
54 end\n\
55 } def\n\
56 ";
57
58 static const char *wxPostScriptHeaderEllipticArc= "\
59 /ellipticarcdict 8 dict def\n\
60 ellipticarcdict /mtrx matrix put\n\
61 /ellipticarc\n\
62 { ellipticarcdict begin\n\
63 /do_fill exch def\n\
64 /endangle exch def\n\
65 /startangle exch def\n\
66 /yrad exch def\n\
67 /xrad exch def \n\
68 /y exch def\n\
69 /x exch def\n\
70 /savematrix mtrx currentmatrix def\n\
71 x y translate\n\
72 xrad yrad scale\n\
73 do_fill { 0 0 moveto } if\n\
74 0 0 1 startangle endangle arc\n\
75 savematrix setmatrix\n\
76 do_fill { fill }{ stroke } ifelse\n\
77 end\n\
78 } def\n";
79
80 static const char *wxPostScriptHeaderSpline = "\
81 /DrawSplineSection {\n\
82 /y3 exch def\n\
83 /x3 exch def\n\
84 /y2 exch def\n\
85 /x2 exch def\n\
86 /y1 exch def\n\
87 /x1 exch def\n\
88 /xa x1 x2 x1 sub 0.666667 mul add def\n\
89 /ya y1 y2 y1 sub 0.666667 mul add def\n\
90 /xb x3 x2 x3 sub 0.666667 mul add def\n\
91 /yb y3 y2 y3 sub 0.666667 mul add def\n\
92 x1 y1 lineto\n\
93 xa ya xb yb x3 y3 curveto\n\
94 } def\n\
95 ";
96
97 static const char *wxPostScriptHeaderColourImage = "\
98 % define 'colorimage' if it isn't defined\n\
99 % ('colortogray' and 'mergeprocs' come from xwd2ps\n\
100 % via xgrab)\n\
101 /colorimage where % do we know about 'colorimage'?\n\
102 { pop } % yes: pop off the 'dict' returned\n\
103 { % no: define one\n\
104 /colortogray { % define an RGB->I function\n\
105 /rgbdata exch store % call input 'rgbdata'\n\
106 rgbdata length 3 idiv\n\
107 /npixls exch store\n\
108 /rgbindx 0 store\n\
109 0 1 npixls 1 sub {\n\
110 grays exch\n\
111 rgbdata rgbindx get 20 mul % Red\n\
112 rgbdata rgbindx 1 add get 32 mul % Green\n\
113 rgbdata rgbindx 2 add get 12 mul % Blue\n\
114 add add 64 idiv % I = .5G + .31R + .18B\n\
115 put\n\
116 /rgbindx rgbindx 3 add store\n\
117 } for\n\
118 grays 0 npixls getinterval\n\
119 } bind def\n\
120 \n\
121 % Utility procedure for colorimage operator.\n\
122 % This procedure takes two procedures off the\n\
123 % stack and merges them into a single procedure.\n\
124 \n\
125 /mergeprocs { % def\n\
126 dup length\n\
127 3 -1 roll\n\
128 dup\n\
129 length\n\
130 dup\n\
131 5 1 roll\n\
132 3 -1 roll\n\
133 add\n\
134 array cvx\n\
135 dup\n\
136 3 -1 roll\n\
137 0 exch\n\
138 putinterval\n\
139 dup\n\
140 4 2 roll\n\
141 putinterval\n\
142 } bind def\n\
143 \n\
144 /colorimage { % def\n\
145 pop pop % remove 'false 3' operands\n\
146 {colortogray} mergeprocs\n\
147 image\n\
148 } bind def\n\
149 } ifelse % end of 'false' case\n\
150 ";
151
152 static char wxPostScriptHeaderReencodeISO1[] =
153 "\n/reencodeISO {\n"
154 "dup dup findfont dup length dict begin\n"
155 "{ 1 index /FID ne { def }{ pop pop } ifelse } forall\n"
156 "/Encoding ISOLatin1Encoding def\n"
157 "currentdict end definefont\n"
158 "} def\n"
159 "/ISOLatin1Encoding [\n"
160 "/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef\n"
161 "/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef\n"
162 "/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef\n"
163 "/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef\n"
164 "/space/exclam/quotedbl/numbersign/dollar/percent/ampersand/quoteright\n"
165 "/parenleft/parenright/asterisk/plus/comma/minus/period/slash\n"
166 "/zero/one/two/three/four/five/six/seven/eight/nine/colon/semicolon\n"
167 "/less/equal/greater/question/at/A/B/C/D/E/F/G/H/I/J/K/L/M/N\n"
168 "/O/P/Q/R/S/T/U/V/W/X/Y/Z/bracketleft/backslash/bracketright\n"
169 "/asciicircum/underscore/quoteleft/a/b/c/d/e/f/g/h/i/j/k/l/m\n"
170 "/n/o/p/q/r/s/t/u/v/w/x/y/z/braceleft/bar/braceright/asciitilde\n"
171 "/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef\n"
172 "/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef\n"
173 "/.notdef/dotlessi/grave/acute/circumflex/tilde/macron/breve\n"
174 "/dotaccent/dieresis/.notdef/ring/cedilla/.notdef/hungarumlaut\n";
175
176 static char wxPostScriptHeaderReencodeISO2[] =
177 "/ogonek/caron/space/exclamdown/cent/sterling/currency/yen/brokenbar\n"
178 "/section/dieresis/copyright/ordfeminine/guillemotleft/logicalnot\n"
179 "/hyphen/registered/macron/degree/plusminus/twosuperior/threesuperior\n"
180 "/acute/mu/paragraph/periodcentered/cedilla/onesuperior/ordmasculine\n"
181 "/guillemotright/onequarter/onehalf/threequarters/questiondown\n"
182 "/Agrave/Aacute/Acircumflex/Atilde/Adieresis/Aring/AE/Ccedilla\n"
183 "/Egrave/Eacute/Ecircumflex/Edieresis/Igrave/Iacute/Icircumflex\n"
184 "/Idieresis/Eth/Ntilde/Ograve/Oacute/Ocircumflex/Otilde/Odieresis\n"
185 "/multiply/Oslash/Ugrave/Uacute/Ucircumflex/Udieresis/Yacute\n"
186 "/Thorn/germandbls/agrave/aacute/acircumflex/atilde/adieresis\n"
187 "/aring/ae/ccedilla/egrave/eacute/ecircumflex/edieresis/igrave\n"
188 "/iacute/icircumflex/idieresis/eth/ntilde/ograve/oacute/ocircumflex\n"
189 "/otilde/odieresis/divide/oslash/ugrave/uacute/ucircumflex/udieresis\n"
190 "/yacute/thorn/ydieresis\n"
191 "] def\n\n";
192
193 //-------------------------------------------------------------------------------
194 // wxPostScriptDC
195 //-------------------------------------------------------------------------------
196
197 wxPostScriptDC::wxPostScriptDC ()
198 {
199 m_pstream = (ofstream*) NULL;
200
201 m_currentRed = 0;
202 m_currentGreen = 0;
203 m_currentBlue = 0;
204
205 m_pageNumber = 0;
206
207 m_clipping = FALSE;
208
209 m_underlinePosition = 0.0;
210 m_underlineThickness = 0.0;
211
212 m_signX = 1; // default x-axis left to right
213 m_signY = -1; // default y-axis bottom up -> top down
214 }
215
216 wxPostScriptDC::wxPostScriptDC (const wxString& file, bool interactive, wxWindow *parent)
217 {
218 m_pstream = (ofstream*) NULL;
219
220 m_currentRed = 0;
221 m_currentGreen = 0;
222 m_currentBlue = 0;
223
224 m_pageNumber = 0;
225
226 m_clipping = FALSE;
227
228 m_underlinePosition = 0.0;
229 m_underlineThickness = 0.0;
230
231 m_signX = 1; // default x-axis left to right
232 m_signY = -1; // default y-axis bottom up -> top down
233
234 Create(file, interactive, parent);
235 }
236
237 bool wxPostScriptDC::Create(const wxString& file, bool interactive, wxWindow *parent)
238 {
239 m_isInteractive = interactive;
240
241 m_title = "";
242 m_filename = file;
243
244 #ifdef __WXMSW__
245 // Can only send to file in Windows
246 wxThePrintSetupData->SetPrinterMode(PS_FILE);
247 #endif
248
249 if (m_isInteractive)
250 {
251 if ((m_ok = PrinterDialog (parent) ) == FALSE) return FALSE;
252 }
253 else
254 {
255 m_ok = TRUE;
256 }
257
258 return m_ok;
259 }
260
261 wxPostScriptDC::~wxPostScriptDC ()
262 {
263 if (m_pstream) delete m_pstream;
264 }
265
266 bool wxPostScriptDC::Ok() const
267 {
268 return m_ok;
269 }
270
271 bool wxPostScriptDC::PrinterDialog(wxWindow *parent)
272 {
273 wxPostScriptPrintDialog dialog( parent, _("Printer Settings"), wxPoint(150, 150), wxSize(400, 400),
274 wxDEFAULT_DIALOG_STYLE | wxDIALOG_MODAL );
275 m_ok = (dialog.ShowModal () == wxID_OK);
276
277 if (!m_ok) return FALSE;
278
279 if ((m_filename == "") &&
280 (wxThePrintSetupData->GetPrinterMode() == PS_PREVIEW ||
281 wxThePrintSetupData->GetPrinterMode() == PS_PRINTER))
282 {
283 // steve, 05.09.94
284 #ifdef __VMS__
285 wxThePrintSetupData->SetPrinterFile("preview");
286 #else
287 // For PS_PRINTER action this depends on a Unix-style print spooler
288 // since the wx_printer_file can be destroyed during a session
289 // @@@ TODO: a Windows-style answer for non-Unix
290 char userId[256];
291 wxGetUserId (userId, sizeof (userId) / sizeof (char));
292 char tmp[256];
293 strcpy (tmp, "/tmp/preview_");
294 strcat (tmp, userId);
295 wxThePrintSetupData->SetPrinterFile(tmp);
296 #endif
297 char tmp2[256];
298 strcpy(tmp2, wxThePrintSetupData->GetPrinterFile());
299 strcat (tmp2, ".ps");
300 wxThePrintSetupData->SetPrinterFile(tmp2);
301 m_filename = tmp2;
302 }
303 else if ((m_filename == "") && (wxThePrintSetupData->GetPrinterMode() == PS_FILE))
304 {
305 char *file = wxSaveFileSelector (_("PostScript"), "ps");
306 if (!file)
307 {
308 m_ok = FALSE;
309 return FALSE;
310 }
311 wxThePrintSetupData->SetPrinterFile(file);
312 m_filename = file;
313 m_ok = TRUE;
314 }
315
316 return m_ok;
317 }
318
319 void wxPostScriptDC::SetClippingRegion (long x, long y, long w, long h)
320 {
321 wxCHECK_RET( m_ok && m_pstream, "invalid postscript dc" );
322
323 if (m_clipping) return;
324
325 wxDC::SetClippingRegion( x, y, w, h );
326
327 m_clipping = TRUE;
328 *m_pstream << "gsave\n"
329 << "newpath\n"
330 << XLOG2DEV(x) << " " << YLOG2DEV(y) << " moveto\n"
331 << XLOG2DEV(x+w) << " " << YLOG2DEV(y) << " lineto\n"
332 << XLOG2DEV(x+w) << " " << YLOG2DEV(y+h) << " lineto\n"
333 << XLOG2DEV(x) << " " << YLOG2DEV(y+h) << " lineto\n"
334 << "closepath clip newpath\n";
335 }
336
337 void wxPostScriptDC::SetClippingRegion( const wxRegion &WXUNUSED(region) )
338 {
339 }
340
341 void wxPostScriptDC::DestroyClippingRegion()
342 {
343 wxCHECK_RET( m_ok && m_pstream, "invalid postscript dc" );
344
345 wxDC::DestroyClippingRegion();
346
347 if (m_clipping)
348 {
349 m_clipping = FALSE;
350 *m_pstream << "grestore\n";
351 }
352 }
353
354 void wxPostScriptDC::Clear()
355 {
356 wxFAIL_MSG( "wxPostScriptDC::Clear not implemented." );
357 }
358
359 void wxPostScriptDC::FloodFill (long WXUNUSED(x), long WXUNUSED(y), const wxColour &WXUNUSED(col), int WXUNUSED(style))
360 {
361 wxFAIL_MSG( "wxPostScriptDC::FloodFill not implemented." );
362 }
363
364 bool wxPostScriptDC::GetPixel (long WXUNUSED(x), long WXUNUSED(y), wxColour * WXUNUSED(col)) const
365 {
366 wxFAIL_MSG( "wxPostScriptDC::GetPixel not implemented." );
367 return FALSE;
368 }
369
370 void wxPostScriptDC::CrossHair (long WXUNUSED(x), long WXUNUSED(y))
371 {
372 wxFAIL_MSG( "wxPostScriptDC::CrossHair not implemented." );
373 }
374
375 void wxPostScriptDC::DrawLine (long x1, long y1, long x2, long y2)
376 {
377 wxCHECK_RET( m_ok && m_pstream, "invalid postscript dc" );
378
379 if (m_pen.GetStyle() == wxTRANSPARENT) return;
380
381 SetPen( m_pen );
382
383 *m_pstream << "newpath\n"
384 << XLOG2DEV(x1) << " " << YLOG2DEV (y1) << " moveto\n"
385 << XLOG2DEV(x2) << " " << YLOG2DEV (y2) << " lineto\n"
386 << "stroke\n";
387
388 CalcBoundingBox( x1, y1 );
389 CalcBoundingBox( x2, y2 );
390 }
391
392 #define RAD2DEG 57.29577951308
393
394 void wxPostScriptDC::DrawArc (long x1, long y1, long x2, long y2, long xc, long yc)
395 {
396 wxCHECK_RET( m_ok && m_pstream, "invalid postscript dc" );
397
398 long dx = x1 - xc;
399 long dy = y1 - yc;
400 long radius = (long) sqrt(dx*dx+dy*dy);
401 double alpha1, alpha2;
402
403 if (x1 == x2 && y1 == y2)
404 {
405 alpha1 = 0.0;
406 alpha2 = 360.0;
407 } else if (radius == 0.0)
408 {
409 alpha1 = alpha2 = 0.0;
410 } else
411 {
412 alpha1 = (x1 - xc == 0) ?
413 (y1 - yc < 0) ? 90.0 : -90.0 :
414 -atan2(double(y1-yc), double(x1-xc)) * RAD2DEG;
415 alpha2 = (x2 - xc == 0) ?
416 (y2 - yc < 0) ? 90.0 : -90.0 :
417 -atan2(double(y2-yc), double(x2-xc)) * RAD2DEG;
418 }
419 while (alpha1 <= 0) alpha1 += 360;
420 while (alpha2 <= 0) alpha2 += 360; // adjust angles to be between
421 while (alpha1 > 360) alpha1 -= 360; // 0 and 360 degree
422 while (alpha2 > 360) alpha2 -= 360;
423
424 if (m_brush.GetStyle() != wxTRANSPARENT)
425 {
426 SetBrush( m_brush );
427 *m_pstream << "newpath\n"
428 << XLOG2DEV(xc) << " "
429 << YLOG2DEV(yc) << " "
430 << XLOG2DEVREL(radius) << " "
431 << YLOG2DEVREL(radius) << " "
432 << alpha1 << " "
433 << alpha2 << " ellipse\n"
434 << XLOG2DEV(xc) << " "
435 << YLOG2DEV(yc) << " lineto\n"
436 << "closepath\n"
437 << "fill\n";
438 }
439
440 if (m_pen.GetStyle() != wxTRANSPARENT)
441 {
442 SetPen( m_pen );
443 *m_pstream << "newpath\n"
444 << XLOG2DEV(xc) << " "
445 << YLOG2DEV(yc) << " "
446 << XLOG2DEVREL(radius) << " "
447 << YLOG2DEVREL(radius) << " "
448 << alpha1 << " "
449 << alpha2 << " ellipse\n"
450 << "stroke\n";
451 }
452
453 CalcBoundingBox( xc-radius, yc-radius );
454 CalcBoundingBox( xc+radius, yc+radius );
455 }
456
457 void wxPostScriptDC::DrawEllipticArc(long x,long y,long w,long h,double sa,double ea)
458 {
459 wxCHECK_RET( m_ok && m_pstream, "invalid postscript dc" );
460
461 if (sa>=360 || sa<=-360) sa=sa-int(sa/360)*360;
462 if (ea>=360 || ea<=-360) ea=ea-int(ea/360)*360;
463 if (sa<0) sa+=360;
464 if (ea<0) ea+=360;
465
466 if (sa==ea)
467 {
468 DrawEllipse(x,y,w,h);
469 return;
470 }
471
472 if (m_brush.GetStyle () != wxTRANSPARENT)
473 {
474 SetBrush( m_brush );
475
476 *m_pstream << "newpath\n"
477 << XLOG2DEV(x+w/2) << " " << YLOG2DEV(y+h/2) << " "
478 << XLOG2DEVREL(w/2) << " " << YLOG2DEVREL(h/2) << " "
479 << int(sa) <<" "<< int(ea) << " true ellipticarc\n";
480
481 CalcBoundingBox( x ,y );
482 CalcBoundingBox( x+w, y+h );
483 }
484
485 if (m_pen.GetStyle () != wxTRANSPARENT)
486 {
487 SetPen( m_pen );
488
489 *m_pstream << "newpath\n"
490 << XLOG2DEV(x+w/2) << " " << YLOG2DEV(y+h/2) << " "
491 << XLOG2DEVREL(w/2) << " " << XLOG2DEVREL(h/2) << " "
492 << int(sa) <<" "<< int(ea) << " false ellipticarc\n";
493
494 CalcBoundingBox( x, y );
495 CalcBoundingBox( x+w, y+h );
496 }
497 }
498
499 void wxPostScriptDC::DrawPoint (long x, long y)
500 {
501 wxCHECK_RET( m_ok && m_pstream, "invalid postscript dc" );
502
503 if (m_pen.GetStyle() == wxTRANSPARENT) return;
504
505 SetPen (m_pen);
506
507 *m_pstream << "newpath\n"
508 << XLOG2DEV(x) << " " << YLOG2DEV (y) << " moveto\n"
509 << XLOG2DEV(x+1) << " " << YLOG2DEV (y) << " lineto\n"
510 << "stroke\n";
511
512 CalcBoundingBox( x, y );
513 }
514
515 void wxPostScriptDC::DrawPolygon (int n, wxPoint points[], long xoffset, long yoffset, int WXUNUSED(fillStyle))
516 {
517 wxCHECK_RET( m_ok && m_pstream, "invalid postscript dc" );
518
519 if (n <= 0) return;
520
521 if (m_brush.GetStyle () != wxTRANSPARENT)
522 {
523 SetBrush( m_brush );
524
525 *m_pstream << "newpath\n";
526
527 long xx = XLOG2DEV(points[0].x + xoffset);
528 long yy = YLOG2DEV(points[0].y + yoffset);
529 *m_pstream << xx << " " << yy << " moveto\n";
530 CalcBoundingBox( points[0].x + xoffset, points[0].y + yoffset );
531
532 for (int i = 1; i < n; i++)
533 {
534 xx = XLOG2DEV(points[i].x + xoffset);
535 yy = YLOG2DEV(points[i].y + yoffset);
536 *m_pstream << xx << " " << yy << " lineto\n";
537 CalcBoundingBox( points[i].x + xoffset, points[i].y + yoffset);
538 }
539 *m_pstream << "fill\n";
540 }
541
542 if (m_pen.GetStyle () != wxTRANSPARENT)
543 {
544 SetPen( m_pen );
545
546 *m_pstream << "newpath\n";
547
548 long xx = XLOG2DEV(points[0].x + xoffset);
549 long yy = YLOG2DEV(points[0].y + yoffset);
550 *m_pstream << xx << " " << yy << " moveto\n";
551 CalcBoundingBox( points[0].x + xoffset, points[0].y + yoffset );
552
553 for (int i = 1; i < n; i++)
554 {
555 xx = XLOG2DEV(points[i].x + xoffset);
556 yy = YLOG2DEV(points[i].y + yoffset);
557 *m_pstream << xx << " " << yy << " lineto\n";
558 CalcBoundingBox( points[i].x + xoffset, points[i].y + yoffset);
559 }
560
561 *m_pstream << "stroke\n";
562 }
563 }
564
565 void wxPostScriptDC::DrawLines (int n, wxPoint points[], long xoffset, long yoffset)
566 {
567 wxCHECK_RET( m_ok && m_pstream, "invalid postscript dc" );
568
569 if (m_pen.GetStyle() == wxTRANSPARENT) return;
570 if (n <= 0) return;
571
572 SetPen (m_pen);
573
574 for (int i=0; i<n ; ++i)
575 {
576 CalcBoundingBox( XLOG2DEV(points[i].x+xoffset), YLOG2DEV(points[i].y+yoffset));
577 }
578
579 *m_pstream << "newpath\n"
580 << XLOG2DEV(points[0].x+xoffset) << " "
581 << YLOG2DEV(points[0].y+yoffset) << " moveto\n";
582
583 for (int i = 1; i < n; i++)
584 {
585 *m_pstream << XLOG2DEV(points[i].x+xoffset) << " "
586 << YLOG2DEV(points[i].y+yoffset) << " lineto\n";
587 }
588
589 *m_pstream << "stroke\n";
590 }
591
592 void wxPostScriptDC::DrawRectangle (long x, long y, long width, long height)
593 {
594 wxCHECK_RET( m_ok && m_pstream, "invalid postscript dc" );
595
596 if (m_brush.GetStyle () != wxTRANSPARENT)
597 {
598 SetBrush( m_brush );
599
600 *m_pstream << "newpath\n"
601 << XLOG2DEV(x) << " " << YLOG2DEV(y) << " moveto\n"
602 << XLOG2DEV(x + width) << " " << YLOG2DEV(y) << " lineto\n"
603 << XLOG2DEV(x + width) << " " << YLOG2DEV(y + height) << " lineto\n"
604 << XLOG2DEV(x) << " " << YLOG2DEV(y + height) << " lineto\n"
605 << "closepath\n"
606 << "fill\n";
607
608 CalcBoundingBox( x, y );
609 CalcBoundingBox( x + width, y + height );
610 }
611
612 if (m_pen.GetStyle () != wxTRANSPARENT)
613 {
614 SetPen (m_pen);
615
616 *m_pstream << "newpath\n"
617 << XLOG2DEV(x) << " " << YLOG2DEV(y) << " moveto\n"
618 << XLOG2DEV(x + width) << " " << YLOG2DEV(y) << " lineto\n"
619 << XLOG2DEV(x + width) << " " << YLOG2DEV(y + height) << " lineto\n"
620 << XLOG2DEV(x) << " " << YLOG2DEV(y + height) << " lineto\n"
621 << "closepath\n"
622 << "stroke\n";
623
624 CalcBoundingBox( x, y );
625 CalcBoundingBox( x + width, y + height );
626 }
627 }
628
629 void wxPostScriptDC::DrawRoundedRectangle (long x, long y, long width, long height, double radius)
630 {
631 wxCHECK_RET( m_ok && m_pstream, "invalid postscript dc" );
632
633 if (radius < 0.0)
634 {
635 // Now, a negative radius is interpreted to mean
636 // 'the proportion of the smallest X or Y dimension'
637 double smallest = 0.0;
638 if (width < height)
639 smallest = width;
640 else
641 smallest = height;
642 radius = (-radius * smallest);
643 }
644
645 long rad = (long) radius;
646
647 if (m_brush.GetStyle () != wxTRANSPARENT)
648 {
649 SetBrush( m_brush );
650
651 // Draw rectangle anticlockwise
652 *m_pstream << "newpath\n"
653 << XLOG2DEV(x + rad) << " " << YLOG2DEV(y + rad) << " " << XLOG2DEVREL(rad) << " 90 180 arc\n"
654 << XLOG2DEV(x) << " " << YLOG2DEV(y + rad) << " moveto\n"
655 << XLOG2DEV(x + rad) << " " << YLOG2DEV(y + height - rad) << " " << XLOG2DEVREL(rad) << " 180 270 arc\n"
656 << XLOG2DEV(x + width - rad) << " " << YLOG2DEV(y + height) << " lineto\n"
657 << XLOG2DEV(x + width - rad) << " " << YLOG2DEV(y + height - rad) << " " << XLOG2DEVREL(rad) << " 270 0 arc\n"
658 << XLOG2DEV(x + width) << " " << YLOG2DEV(y + rad) << " lineto\n"
659 << XLOG2DEV(x + width - rad) << " " << YLOG2DEV(y + rad) << " " << XLOG2DEVREL(rad) << " 0 90 arc\n"
660 << XLOG2DEV(x + rad) << " " << YLOG2DEV(y) << " lineto\n"
661 << "closepath\n"
662 << "fill\n";
663
664 CalcBoundingBox( x, y );
665 CalcBoundingBox( x + width, y + height );
666 }
667
668 if (m_pen.GetStyle () != wxTRANSPARENT)
669 {
670 SetPen (m_pen);
671
672 // Draw rectangle anticlockwise
673 *m_pstream << "newpath\n"
674 << XLOG2DEV(x + rad) << " " << YLOG2DEV(y + rad) << " " << XLOG2DEVREL(rad) << " 90 180 arc\n"
675 << XLOG2DEV(x) << " " << YLOG2DEV(y + rad) << " moveto\n"
676 << XLOG2DEV(x + rad) << " " << YLOG2DEV(y + height - rad) << " " << XLOG2DEVREL(rad) << " 180 270 arc\n"
677 << XLOG2DEV(x + width - rad) << " " << YLOG2DEV(y + height) << " lineto\n"
678 << XLOG2DEV(x + width - rad) << " " << YLOG2DEV(y + height - rad) << " " << XLOG2DEVREL(rad) << " 270 0 arc\n"
679 << XLOG2DEV(x + width) << " " << YLOG2DEV(y + rad) << " lineto\n"
680 << XLOG2DEV(x + width - rad) << " " << YLOG2DEV(y + rad) << " " << XLOG2DEVREL(rad) << " 0 90 arc\n"
681 << XLOG2DEV(x + rad) << " " << YLOG2DEV(y) << " lineto\n"
682 << "closepath\n"
683 << "stroke\n";
684
685 CalcBoundingBox( x, y );
686 CalcBoundingBox( x + width, y + height );
687 }
688 }
689
690 void wxPostScriptDC::DrawEllipse (long x, long y, long width, long height)
691 {
692 wxCHECK_RET( m_ok && m_pstream, "invalid postscript dc" );
693
694 if (m_brush.GetStyle () != wxTRANSPARENT)
695 {
696 SetBrush (m_brush);
697
698 *m_pstream << "newpath\n"
699 << XLOG2DEV(x + width / 2) << " " << YLOG2DEV(y + height / 2) << " "
700 << XLOG2DEV(width / 2) << " " << YLOG2DEVREL(height / 2) << " 0 360 ellipse\n"
701 << "fill\n";
702
703 CalcBoundingBox( x - width, y - height );
704 CalcBoundingBox( x + width, y + height );
705 }
706
707 if (m_pen.Ok() && m_pen.GetStyle () != wxTRANSPARENT)
708 {
709 SetPen (m_pen);
710
711 *m_pstream << "newpath\n"
712 << XLOG2DEV(x + width / 2) << " " << YLOG2DEV(y + height / 2) << " "
713 << XLOG2DEV(width / 2) << " " << YLOG2DEVREL(height / 2) << " 0 360 ellipse\n"
714 << "stroke\n";
715
716 CalcBoundingBox( x - width, y - height );
717 CalcBoundingBox( x + width, y + height );
718 }
719 }
720
721 void wxPostScriptDC::DrawIcon (const wxIcon& icon, long x, long y)
722 {
723 DrawBitmap( icon, x, y, TRUE );
724 }
725
726 void wxPostScriptDC::DrawBitmap( const wxBitmap& bitmap, long x, long y, bool WXUNUSED(useMask) )
727 {
728 wxCHECK_RET( m_ok && m_pstream, "invalid postscript dc" );
729
730 if (!bitmap.Ok()) return;
731
732 wxImage image( bitmap );
733
734 if (!image.Ok()) return;
735
736 int ww = XLOG2DEVREL(image.GetWidth());
737 int hh = YLOG2DEVREL(image.GetHeight());
738
739 image = image.Scale( ww, hh );
740
741 if (!image.Ok()) return;
742
743 int xx = XLOG2DEV(x);
744 int yy = YLOG2DEV(y + bitmap.GetHeight());
745
746 *m_pstream << "/origstate save def\n"
747 << "20 dict begin\n"
748 << "/pix " << ww << " string def\n"
749 << "/grays " << ww << " string def\n"
750 << "/npixels 0 def\n"
751 << "/rgbindx 0 def\n"
752 << xx << " " << yy << " translate\n"
753 << ww << " " << hh << " scale\n"
754 << ww << " " << hh << " 8\n"
755 << "[" << ww << " 0 0 " << (-hh) << " 0 " << hh << "]\n"
756 << "{currentfile pix readhexstring pop}\n"
757 << "false 3 colorimage\n";
758
759 for (int j = 0; j < hh; j++)
760 {
761 for (int i = 0; i < ww; i++)
762 {
763 char buffer[5];
764 buffer[2] = 0;
765 wxDecToHex( image.GetRed(i,j), buffer );
766 *m_pstream << buffer;
767 wxDecToHex( image.GetGreen(i,j), buffer );
768 *m_pstream << buffer;
769 wxDecToHex( image.GetBlue(i,j), buffer );
770 *m_pstream << buffer;
771 }
772 *m_pstream << "\n";
773 }
774
775 *m_pstream << "end\n";
776 *m_pstream << "origstate restore\n";
777
778 }
779
780 void wxPostScriptDC::SetFont (const wxFont& font)
781 {
782 wxCHECK_RET( m_ok && m_pstream, "invalid postscript dc" );
783
784 if (!font.Ok()) return;
785
786 m_font = font;
787
788 char *name = wxTheFontNameDirectory->GetPostScriptName( m_font.GetFamily(),
789 m_font.GetWeight(),
790 m_font.GetStyle() );
791 if (!name) name = "Times-Roman";
792
793 *m_pstream << "/" << name << " reencodeISO def\n"
794 << "/" << name << " findfont\n"
795 << YLOG2DEVREL(font.GetPointSize())
796 << " scalefont setfont\n";
797 }
798
799 void wxPostScriptDC::SetPen( const wxPen& pen )
800 {
801 wxCHECK_RET( m_ok && m_pstream, "invalid postscript dc" );
802
803 if (!pen.Ok()) return;
804
805 int oldStyle = m_pen.GetStyle();
806
807 m_pen = pen;
808
809 *m_pstream << XLOG2DEVREL(m_pen.GetWidth()) << " setlinewidth\n";
810
811 /*
812 Line style - WRONG: 2nd arg is OFFSET
813
814 Here, I'm afraid you do not conceive meaning of parameters of 'setdash'
815 operator correctly. You should look-up this in the Red Book: the 2nd parame-
816 ter is not number of values in the array of the first one, but an offset
817 into this description of the pattern. I mean a real *offset* not index
818 into array. I.e. If the command is [3 4] 1 setdash is used, then there
819 will be first black line *2* units long, then space 4 units, then the
820 pattern of *3* units black, 4 units space will be repeated.
821 */
822
823 static const char *dotted = "[2 5] 2";
824 static const char *short_dashed = "[4 4] 2";
825 static const char *long_dashed = "[4 8] 2";
826 static const char *dotted_dashed = "[6 6 2 6] 4";
827
828 const char *psdash = (char *) NULL;
829 switch (m_pen.GetStyle ())
830 {
831 case wxDOT: psdash = dotted; break;
832 case wxSHORT_DASH: psdash = short_dashed; break;
833 case wxLONG_DASH: psdash = long_dashed; break;
834 case wxDOT_DASH: psdash = dotted_dashed; break;
835 case wxSOLID:
836 case wxTRANSPARENT:
837 default: psdash = "[] 0"; break;
838 }
839
840 if (oldStyle != m_pen.GetStyle())
841 {
842 *m_pstream << psdash << " setdash\n";
843 }
844
845 // Line colour
846 unsigned char red = m_pen.GetColour().Red();
847 unsigned char blue = m_pen.GetColour().Blue();
848 unsigned char green = m_pen.GetColour().Green();
849
850 if (!m_colour)
851 {
852 // Anything not white is black
853 if (!(red == (unsigned char) 255 && blue == (unsigned char) 255
854 && green == (unsigned char) 255))
855 {
856 red = (unsigned char) 0;
857 green = (unsigned char) 0;
858 blue = (unsigned char) 0;
859 }
860
861 // setgray here ?
862 }
863
864 if (!(red == m_currentRed && green == m_currentGreen && blue == m_currentBlue))
865 {
866 long redPS = (long) (((int) red) / 255.0);
867 long bluePS = (long) (((int) blue) / 255.0);
868 long greenPS = (long) (((int) green) / 255.0);
869
870 *m_pstream << redPS << " " << greenPS << " " << bluePS << " setrgbcolor\n";
871
872 m_currentRed = red;
873 m_currentBlue = blue;
874 m_currentGreen = green;
875 }
876 }
877
878 void wxPostScriptDC::SetBrush( const wxBrush& brush )
879 {
880 wxCHECK_RET( m_ok && m_pstream, "invalid postscript dc" );
881
882 if (!brush.Ok()) return;
883
884 m_brush = brush;
885
886 // Brush colour
887 unsigned char red = m_brush.GetColour ().Red ();
888 unsigned char blue = m_brush.GetColour ().Blue ();
889 unsigned char green = m_brush.GetColour ().Green ();
890
891 if (!m_colour)
892 {
893 // Anything not black is white
894 if (!(red == (unsigned char) 0 && blue == (unsigned char) 0
895 && green == (unsigned char) 0))
896 {
897 red = (unsigned char) 255;
898 green = (unsigned char) 255;
899 blue = (unsigned char) 255;
900 }
901
902 // setgray here ?
903 }
904
905 if (!(red == m_currentRed && green == m_currentGreen && blue == m_currentBlue))
906 {
907 long redPS = (long) (((int) red) / 255.0);
908 long bluePS = (long) (((int) blue) / 255.0);
909 long greenPS = (long) (((int) green) / 255.0);
910 *m_pstream << redPS << " " << greenPS << " " << bluePS << " setrgbcolor\n";
911 m_currentRed = red;
912 m_currentBlue = blue;
913 m_currentGreen = green;
914 }
915 }
916
917 void wxPostScriptDC::DrawText( const wxString& text, long x, long y, bool WXUNUSED(use16bit) )
918 {
919 wxCHECK_RET( m_ok && m_pstream, "invalid postscript dc" );
920
921 SetFont( m_font );
922
923 if (m_textForegroundColour.Ok ())
924 {
925 unsigned char red = m_textForegroundColour.Red ();
926 unsigned char blue = m_textForegroundColour.Blue ();
927 unsigned char green = m_textForegroundColour.Green ();
928
929 if (!m_colour)
930 {
931 // Anything not white is black
932 if (!(red == (unsigned char) 255 && blue == (unsigned char) 255
933 && green == (unsigned char) 255))
934 {
935 red = (unsigned char) 0;
936 green = (unsigned char) 0;
937 blue = (unsigned char) 0;
938 }
939 }
940
941 // maybe setgray here ?
942
943 if (!(red == m_currentRed && green == m_currentGreen && blue == m_currentBlue))
944 {
945 long redPS = (long) (((int) red) / 255.0);
946 long bluePS = (long) (((int) blue) / 255.0);
947 long greenPS = (long) (((int) green) / 255.0);
948 *m_pstream << redPS << " " << greenPS << " " << bluePS << " setrgbcolor\n";
949
950 m_currentRed = red;
951 m_currentBlue = blue;
952 m_currentGreen = green;
953 }
954 }
955
956 int size = m_font.GetPointSize();
957
958 long by = y + (long)floor( float(size) * 2.0 / 3.0 ); // approximate baseline
959 *m_pstream << XLOG2DEV(x) << " " << YLOG2DEV(by) << " moveto\n";
960
961 *m_pstream << "(";
962 int len = strlen ((char *)(const char *)text);
963 int i;
964 for (i = 0; i < len; i++)
965 {
966 int c = (unsigned char) text[i];
967 if ( c == ')' || c == '(' || c == '\\')
968 {
969 *m_pstream << "\\" << (char) c;
970 }
971 else if ( c >= 128 )
972 {
973 // Cope with character codes > 127
974 char tmp[5];
975 sprintf(tmp, "\\%o", c);
976 *m_pstream << tmp;
977 }
978 else
979 *m_pstream << (char) c;
980 }
981
982 *m_pstream << ")" << " show\n";
983
984 if (m_font.GetUnderlined())
985 {
986 long uy = (long)(y + size - m_underlinePosition);
987 long w, h;
988 GetTextExtent(text, &w, &h);
989
990 *m_pstream << "gsave " << XLOG2DEV(x) << " " << YLOG2DEV(uy)
991 << " moveto\n"
992 << (long)m_underlineThickness << " setlinewidth "
993 << XLOG2DEV(x + w) << " " << YLOG2DEV(uy)
994 << " lineto stroke grestore\n";
995 }
996
997 CalcBoundingBox( x, y );
998 CalcBoundingBox( x + size * text.Length() * 2/3 , y );
999 }
1000
1001
1002 void wxPostScriptDC::SetBackground (const wxBrush& brush)
1003 {
1004 m_backgroundBrush = brush;
1005 }
1006
1007 void wxPostScriptDC::SetLogicalFunction (int WXUNUSED(function))
1008 {
1009 wxFAIL_MSG( "wxPostScriptDC::SetLogicalFunction not implemented." );
1010 }
1011
1012 void wxPostScriptDC::DrawSpline( wxList *points )
1013 {
1014 wxCHECK_RET( m_ok && m_pstream, "invalid postscript dc" );
1015
1016 SetPen( m_pen );
1017
1018 double a, b, c, d, x1, y1, x2, y2, x3, y3;
1019 wxPoint *p, *q;
1020
1021 wxNode *node = points->First();
1022 p = (wxPoint *)node->Data();
1023 x1 = p->x;
1024 y1 = p->y;
1025
1026 node = node->Next();
1027 p = (wxPoint *)node->Data();
1028 c = p->x;
1029 d = p->y;
1030 x3 = a = (double)(x1 + c) / 2;
1031 y3 = b = (double)(y1 + d) / 2;
1032
1033 *m_pstream << "newpath "
1034 << XLOG2DEV((long)x1) << " " << YLOG2DEV((long)y1) << " moveto "
1035 << XLOG2DEV((long)x3) << " " << YLOG2DEV((long)y3) << " lineto\n";
1036
1037 CalcBoundingBox( (long)x1, (long)y1 );
1038 CalcBoundingBox( (long)x3, (long)y3 );
1039
1040 while ((node = node->Next()) != NULL)
1041 {
1042 q = (wxPoint *)node->Data();
1043
1044 x1 = x3;
1045 y1 = y3;
1046 x2 = c;
1047 y2 = d;
1048 c = q->x;
1049 d = q->y;
1050 x3 = (double)(x2 + c) / 2;
1051 y3 = (double)(y2 + d) / 2;
1052 *m_pstream << XLOG2DEV((long)x1) << " " << YLOG2DEV((long)y1) << " "
1053 << XLOG2DEV((long)x2) << " " << YLOG2DEV((long)y2) << " "
1054 << XLOG2DEV((long)x3) << " " << YLOG2DEV((long)y3) << " DrawSplineSection\n";
1055
1056 CalcBoundingBox( (long)x1, (long)y1 );
1057 CalcBoundingBox( (long)x3, (long)y3 );
1058 }
1059
1060 /*
1061 At this point, (x2,y2) and (c,d) are the position of the
1062 next-to-last and last point respectively, in the point list
1063 */
1064
1065 *m_pstream << XLOG2DEV((long)c) << " " << YLOG2DEV((long)d) << " lineto stroke\n";
1066 }
1067
1068 long wxPostScriptDC::GetCharWidth ()
1069 {
1070 // Chris Breeze: reasonable approximation using wxMODERN/Courier
1071 return (long) (GetCharHeight() * 72.0 / 120.0);
1072 }
1073
1074
1075 void wxPostScriptDC::SetAxisOrientation( bool xLeftRight, bool yBottomUp )
1076 {
1077 wxCHECK_RET( m_ok && m_pstream, "invalid postscript dc" );
1078
1079 m_signX = (xLeftRight ? 1 : -1);
1080 m_signY = (yBottomUp ? 1 : -1);
1081
1082 ComputeScaleAndOrigin();
1083 }
1084
1085 void wxPostScriptDC::SetDeviceOrigin( long x, long y )
1086 {
1087 wxCHECK_RET( m_ok && m_pstream, "invalid postscript dc" );
1088
1089 int h = 0;
1090 int w = 0;
1091 GetSize( &w, &h );
1092
1093 wxDC::SetDeviceOrigin( x, h-y );
1094 }
1095
1096 void wxPostScriptDC::GetSize(int* width, int* height) const
1097 {
1098 const char *paperType = wxThePrintSetupData->GetPaperName();
1099
1100 if (!paperType) paperType = _("A4 210 x 297 mm");
1101
1102 wxPrintPaperType *paper = wxThePrintPaperDatabase->FindPaperType(paperType);
1103
1104 if (!paper) paper = wxThePrintPaperDatabase->FindPaperType(_("A4 210 x 297 mm"));
1105
1106 if (paper)
1107 {
1108 if (width) *width = paper->widthPixels;
1109 if (height) *height = paper->heightPixels;
1110 }
1111 else
1112 {
1113 if (width) *width = 595;
1114 if (height) *height = 842;
1115 }
1116 }
1117
1118 bool wxPostScriptDC::StartDoc (const wxString& message)
1119 {
1120 wxCHECK_MSG( m_ok, FALSE, "invalid postscript dc" );
1121
1122 if (m_filename == "")
1123 {
1124 m_filename = wxGetTempFileName("ps");
1125 wxThePrintSetupData->SetPrinterFile((char *)(const char *)m_filename);
1126 m_ok = TRUE;
1127 }
1128 else
1129 {
1130 wxThePrintSetupData->SetPrinterFile((char *)(const char *)m_filename);
1131 }
1132
1133 m_pstream = new ofstream (wxThePrintSetupData->GetPrinterFile());
1134
1135 if (!m_pstream || !m_pstream->good())
1136 {
1137 wxMessageBox (_("Cannot open file!"), _("Error"), wxOK);
1138 m_ok = FALSE;
1139 return FALSE;
1140 }
1141
1142 m_ok = TRUE;
1143
1144 SetBrush( *wxBLACK_BRUSH );
1145 SetPen( *wxBLACK_PEN );
1146 SetBackground( *wxWHITE_BRUSH );
1147 SetTextForeground( *wxBLACK );
1148
1149 // set origin according to paper size
1150 SetDeviceOrigin( 0,0 );
1151
1152 wxPageNumber = 1;
1153 m_pageNumber = 1;
1154 m_title = message;
1155 return TRUE;
1156 }
1157
1158 void wxPostScriptDC::EndDoc ()
1159 {
1160 wxCHECK_RET( m_ok && m_pstream, "invalid postscript dc" );
1161
1162 if (m_clipping)
1163 {
1164 m_clipping = FALSE;
1165 *m_pstream << "grestore\n";
1166 }
1167
1168 if (m_pstream)
1169 {
1170 delete m_pstream;
1171 m_pstream = (ofstream *) NULL;
1172 }
1173
1174 char *header_file = wxGetTempFileName("ps");
1175
1176 m_pstream = new ofstream( header_file );
1177
1178 *m_pstream << "%!PS-Adobe-2.0\n"; /* PostScript magic strings */
1179 *m_pstream << "%%Title: " << (const char *) m_title << "\n";
1180 *m_pstream << "%%Creator: " << wxTheApp->argv[0] << "\n";
1181 *m_pstream << "%%CreationDate: " << wxNow() << "\n";
1182
1183 char userID[256];
1184 if ( wxGetEmailAddress(userID, sizeof(userID)) )
1185 {
1186 *m_pstream << "%%For: " << (char *)userID;
1187 char userName[245];
1188 if (wxGetUserName(userName, sizeof(userName)))
1189 *m_pstream << " (" << (char *)userName << ")";
1190 *m_pstream << "\n";
1191 }
1192 else if ( wxGetUserName(userID, sizeof(userID)) )
1193 {
1194 *m_pstream << "%%For: " << (char *)userID << "\n";
1195 }
1196
1197 // THE FOLLOWING HAS BEEN CONTRIBUTED BY Andy Fyfe <andy@hyperparallel.com>
1198
1199 long wx_printer_translate_x, wx_printer_translate_y;
1200 double wx_printer_scale_x, wx_printer_scale_y;
1201 wxThePrintSetupData->GetPrinterTranslation(&wx_printer_translate_x, &wx_printer_translate_y);
1202 wxThePrintSetupData->GetPrinterScaling(&wx_printer_scale_x, &wx_printer_scale_y);
1203
1204 if (wxThePrintSetupData->GetPrinterOrientation() == PS_LANDSCAPE)
1205 {
1206 *m_pstream << "%%Orientation: Landscape\n";
1207 }
1208 else
1209 {
1210 *m_pstream << "%%Orientation: Portrait\n";
1211 }
1212
1213 // Compute the bounding box. Note that it is in the default user
1214 // coordinate system, thus we have to convert the values.
1215 long llx = (long) ((XLOG2DEV(m_minX)+wx_printer_translate_x)*wx_printer_scale_x);
1216 long lly = (long) ((YLOG2DEV(m_minY)+wx_printer_translate_y)*wx_printer_scale_y);
1217 long urx = (long) ((XLOG2DEV(m_maxX)+wx_printer_translate_x)*wx_printer_scale_x);
1218 long ury = (long) ((YLOG2DEV(m_maxY)+wx_printer_translate_y)*wx_printer_scale_y);
1219
1220 // If we're landscape, our sense of "x" and "y" is reversed.
1221 if (wxThePrintSetupData->GetPrinterOrientation() == PS_LANDSCAPE)
1222 {
1223 long tmp;
1224 tmp = llx; llx = lly; lly = tmp;
1225 tmp = urx; urx = ury; ury = tmp;
1226
1227 // We need either the two lines that follow, or we need to subtract
1228 // min_x from real_translate_y, which is commented out below.
1229 llx = llx - (long)(m_minX*wx_printer_scale_y);
1230 urx = urx - (long)(m_minX*wx_printer_scale_y);
1231 }
1232
1233 // The Adobe specifications call for integers; we round as to make
1234 // the bounding larger.
1235 *m_pstream << "%%BoundingBox: "
1236 << floor((double)llx) << " " << floor((double)lly) << " "
1237 << ceil((double)urx) << " " << ceil((double)ury) << "\n";
1238 *m_pstream << "%%Pages: " << (wxPageNumber - 1) << "\n";
1239 *m_pstream << "%%EndComments\n\n";
1240
1241 // To check the correctness of the bounding box, postscript commands
1242 // to draw a box corresponding to the bounding box are generated below.
1243 // But since we typically don't want to print such a box, the postscript
1244 // commands are generated within comments. These lines appear before any
1245 // adjustment of scale, rotation, or translation, and hence are in the
1246 // default user coordinates.
1247 *m_pstream << "% newpath\n";
1248 *m_pstream << "% " << llx << " " << lly << " moveto\n";
1249 *m_pstream << "% " << urx << " " << lly << " lineto\n";
1250 *m_pstream << "% " << urx << " " << ury << " lineto\n";
1251 *m_pstream << "% " << llx << " " << ury << " lineto closepath stroke\n";
1252
1253 *m_pstream << "%%BeginProlog\n";
1254 *m_pstream << wxPostScriptHeaderEllipse;
1255 *m_pstream << wxPostScriptHeaderEllipticArc;
1256 *m_pstream << wxPostScriptHeaderColourImage;
1257 *m_pstream << wxPostScriptHeaderReencodeISO1;
1258 *m_pstream << wxPostScriptHeaderReencodeISO2;
1259
1260 if (wxPostScriptHeaderSpline)
1261 *m_pstream << wxPostScriptHeaderSpline;
1262 *m_pstream << "%%EndProlog\n";
1263
1264 delete m_pstream;
1265 m_pstream = (ofstream *) NULL;
1266
1267 char *tmp_file = wxGetTempFileName("ps");
1268
1269 // Paste header Before wx_printer_file
1270 wxConcatFiles (header_file, wxThePrintSetupData->GetPrinterFile(), tmp_file);
1271 wxRemoveFile (header_file);
1272 wxRemoveFile (wxThePrintSetupData->GetPrinterFile());
1273 wxRenameFile(tmp_file, wxThePrintSetupData->GetPrinterFile());
1274
1275 #if defined(__X__) || defined(__WXGTK__)
1276 if (m_ok)
1277 {
1278 switch (wxThePrintSetupData->GetPrinterMode()) {
1279 case PS_PREVIEW:
1280 {
1281 char *argv[3];
1282 argv[0] = wxThePrintSetupData->GetPrintPreviewCommand();
1283 argv[1] = wxThePrintSetupData->GetPrinterFile();
1284 argv[2] = (char *) NULL;
1285 wxExecute (argv, TRUE);
1286 wxRemoveFile(wxThePrintSetupData->GetPrinterFile());
1287 }
1288 break;
1289
1290 case PS_PRINTER:
1291 {
1292 char *argv[4];
1293 int argc = 0;
1294 argv[argc++] = wxThePrintSetupData->GetPrinterCommand();
1295
1296 // !SM! If we simply assign to argv[1] here, if printer options
1297 // are blank, we get an annoying and confusing message from lpr.
1298 char * opts = wxThePrintSetupData->GetPrinterOptions();
1299 if (opts && *opts)
1300 argv[argc++] = opts;
1301
1302 argv[argc++] = wxThePrintSetupData->GetPrinterFile();
1303 argv[argc++] = (char *) NULL;
1304 wxExecute (argv, TRUE);
1305 wxRemoveFile(wxThePrintSetupData->GetPrinterFile());
1306 }
1307 break;
1308
1309 case PS_FILE:
1310 break;
1311 }
1312 }
1313 #endif
1314 }
1315
1316 void wxPostScriptDC::StartPage ()
1317 {
1318 wxCHECK_RET( m_ok && m_pstream, "invalid postscript dc" );
1319
1320 *m_pstream << "%%Page: " << (wxPageNumber++) << "\n";
1321
1322 // *m_pstream << "matrix currentmatrix\n";
1323
1324 // Added by Chris Breeze
1325
1326 // Each page starts with an "initgraphics" which resets the
1327 // transformation and so we need to reset the origin
1328 // (and rotate the page for landscape printing)
1329
1330 /*
1331 m_scaleFactor = 1.0;
1332 m_logicalOriginX = 0;
1333 m_logicalOriginY = 0;
1334 */
1335
1336 // Output scaling
1337 long translate_x, translate_y;
1338 double scale_x, scale_y;
1339 wxThePrintSetupData->GetPrinterTranslation(&translate_x, &translate_y);
1340 wxThePrintSetupData->GetPrinterScaling(&scale_x, &scale_y);
1341
1342 if (wxThePrintSetupData->GetPrinterOrientation() == PS_LANDSCAPE)
1343 {
1344 translate_y -= m_maxY;
1345 *m_pstream << "90 rotate\n";
1346 }
1347
1348 *m_pstream << scale_x << " " << scale_y << " scale\n";
1349 *m_pstream << translate_x << " " << translate_y << " translate\n";
1350 }
1351
1352 void wxPostScriptDC::EndPage ()
1353 {
1354 wxCHECK_RET( m_ok && m_pstream, "invalid postscript dc" );
1355
1356 *m_pstream << "showpage\n";
1357 }
1358
1359 bool wxPostScriptDC::Blit( long WXUNUSED(xdest), long WXUNUSED(ydest),
1360 long WXUNUSED(fwidth), long WXUNUSED(fheight),
1361 wxDC *WXUNUSED(source),
1362 long WXUNUSED(xsrc), long WXUNUSED(ysrc),
1363 int WXUNUSED(rop), bool WXUNUSED(useMask) )
1364 {
1365 wxCHECK_MSG( m_ok && m_pstream, FALSE, "invalid postscript dc" );
1366
1367 wxFAIL_MSG( "wxPostScriptDC::Blit no yet implemented." );
1368
1369 return TRUE;
1370 }
1371
1372 long wxPostScriptDC::GetCharHeight ()
1373 {
1374 if (m_font.Ok())
1375 return m_font.GetPointSize();
1376 else
1377 return 12;
1378 }
1379
1380 void wxPostScriptDC::GetTextExtent (const wxString& string, long *x, long *y,
1381 long *descent, long *externalLeading, wxFont *theFont,
1382 bool WXUNUSED(use16))
1383 {
1384 // if (!m_pstream) return;
1385
1386 wxFont *fontToUse = theFont;
1387
1388 if (!fontToUse) fontToUse = (wxFont*) &m_font;
1389
1390 #if !USE_AFM_FOR_POSTSCRIPT
1391 // Provide a VERY rough estimate (avoid using it)
1392 // Chris Breeze 5/11/97: produces accurate results for mono-spaced
1393 // font such as Courier (aka wxMODERN)
1394 int height = 12;
1395 if (fontToUse)
1396 {
1397 height = fontToUse->GetPointSize();
1398 }
1399 *x = strlen (string) * height * 72 / 120;
1400 *y = (long) (height * 1.32); // allow for descender
1401
1402 if (descent)
1403 *descent = 0;
1404 if (externalLeading)
1405 *externalLeading = 0;
1406 #else
1407 // +++++ start of contributed code +++++
1408
1409 // ************************************************************
1410 // method for calculating string widths in postscript:
1411 // read in the AFM (adobe font metrics) file for the
1412 // actual font, parse it and extract the character widths
1413 // and also the descender. this may be improved, but for now
1414 // it works well. the AFM file is only read in if the
1415 // font is changed. this may be chached in the future.
1416 // calls to GetTextExtent with the font unchanged are rather
1417 // efficient!!!
1418 //
1419 // for each font and style used there is an AFM file necessary.
1420 // currently i have only files for the roman font family.
1421 // i try to get files for the other ones!
1422 //
1423 // CAVE: the size of the string is currently always calculated
1424 // in 'points' (1/72 of an inch). this should later on be
1425 // changed to depend on the mapping mode.
1426 // CAVE: the path to the AFM files must be set before calling this
1427 // function. this is usually done by a call like the following:
1428 // wxSetAFMPath("d:\\wxw161\\afm\\");
1429 //
1430 // example:
1431 //
1432 // wxPostScriptDC dc(NULL, TRUE);
1433 // if (dc.Ok()){
1434 // wxSetAFMPath("d:\\wxw161\\afm\\");
1435 // dc.StartDoc("Test");
1436 // dc.StartPage();
1437 // long w,h;
1438 // dc.SetFont(new wxFont(10, wxROMAN, wxNORMAL, wxNORMAL));
1439 // dc.GetTextExtent("Hallo",&w,&h);
1440 // dc.EndPage();
1441 // dc.EndDoc();
1442 // }
1443 //
1444 // by steve (stefan.hammes@urz.uni-heidelberg.de)
1445 // created: 10.09.94
1446 // updated: 14.05.95
1447
1448 assert(fontToUse && "void wxPostScriptDC::GetTextExtent: no font defined");
1449 assert(x && "void wxPostScriptDC::GetTextExtent: x == NULL");
1450 assert(y && "void wxPostScriptDC::GetTextExtent: y == NULL");
1451
1452 // these static vars are for storing the state between calls
1453 static int lastFamily= INT_MIN;
1454 static int lastSize= INT_MIN;
1455 static int lastStyle= INT_MIN;
1456 static int lastWeight= INT_MIN;
1457 static int lastDescender = INT_MIN;
1458 static int lastWidths[256]; // widths of the characters
1459
1460 // get actual parameters
1461 const int Family = fontToUse->GetFamily();
1462 const int Size = fontToUse->GetPointSize();
1463 const int Style = fontToUse->GetStyle();
1464 const int Weight = fontToUse->GetWeight();
1465
1466 // if we have another font, read the font-metrics
1467 if(Family!=lastFamily||Size!=lastSize||Style!=lastStyle||Weight!=lastWeight){
1468 // store actual values
1469 lastFamily = Family;
1470 lastSize = Size;
1471 lastStyle = Style;
1472 lastWeight = Weight;
1473
1474 // read in new font metrics **************************************
1475
1476 // 1. construct filename ******************************************
1477 /* MATTHEW: [2] Use wxTheFontNameDirectory */
1478 const char *name;
1479
1480 // Julian - we'll need to do this a different way now we've removed the
1481 // font directory system. Must find Stefan's original code.
1482
1483 name = wxTheFontNameDirectory->GetAFMName(Family, Weight, Style);
1484 if (!name)
1485 name = "unknown";
1486
1487 // get the directory of the AFM files
1488 char afmName[256];
1489 afmName[0] = 0;
1490 if (wxGetAFMPath())
1491 strcpy(afmName,wxGetAFMPath());
1492
1493 // 2. open and process the file **********************************
1494
1495 // a short explanation of the AFM format:
1496 // we have for each character a line, which gives its size
1497 // e.g.:
1498 //
1499 // C 63 ; WX 444 ; N question ; B 49 -14 395 676 ;
1500 //
1501 // that means, we have a character with ascii code 63, and width
1502 // (444/1000 * fontSize) points.
1503 // the other data is ignored for now!
1504 //
1505 // when the font has changed, we read in the right AFM file and store the
1506 // character widths in an array, which is processed below (see point 3.).
1507
1508 // new elements JC Sun Aug 25 23:21:44 MET DST 1996
1509
1510
1511 strcat(afmName,name);
1512 strcat(afmName,".afm");
1513 FILE *afmFile = fopen(afmName,"r");
1514 if(afmFile==NULL){
1515 wxLogDebug("GetTextExtent: can't open AFM file '%s'\n",afmName);
1516 wxLogDebug(" using approximate values\n");
1517 int i;
1518 for (i=0; i<256; i++) lastWidths[i] = 500; // an approximate value
1519 lastDescender = -150; // dito.
1520 }else{
1521 int i;
1522 // init the widths array
1523 for(i=0; i<256; i++) lastWidths[i]= INT_MIN;
1524 // some variables for holding parts of a line
1525 char cString[10],semiString[10],WXString[10],descString[20];
1526 char upString[30], utString[30], encString[50];
1527 char line[256];
1528 int ascii,cWidth;
1529 // read in the file and parse it
1530 while(fgets(line,sizeof(line),afmFile)!=NULL){
1531 // A.) check for descender definition
1532 if(strncmp(line,"Descender",9)==0){
1533 if((sscanf(line,"%s%d",descString,&lastDescender)!=2)
1534 || (strcmp(descString,"Descender")!=0)) {
1535 wxLogDebug("AFM-file '%s': line '%s' has error (bad descender)\n",
1536 afmName,line);
1537 }
1538 }
1539 // JC 1.) check for UnderlinePosition
1540 else if(strncmp(line,"UnderlinePosition",17)==0){
1541 if((sscanf(line,"%s%lf",upString,&UnderlinePosition)!=2)
1542 || (strcmp(upString,"UnderlinePosition")!=0)) {
1543 wxLogDebug("AFM-file '%s': line '%s' has error (bad UnderlinePosition)\n",
1544 afmName,line);
1545 }
1546 }
1547 // JC 2.) check for UnderlineThickness
1548 else if(strncmp(line,"UnderlineThickness",18)==0){
1549 if((sscanf(line,"%s%lf",utString,&UnderlineThickness)!=2)
1550 || (strcmp(utString,"UnderlineThickness")!=0)) {
1551 wxLogDebug("AFM-file '%s': line '%s' has error (bad UnderlineThickness)\n",
1552 afmName,line);
1553 }
1554 }
1555 // JC 3.) check for EncodingScheme
1556 else if(strncmp(line,"EncodingScheme",14)==0){
1557 if((sscanf(line,"%s%s",utString,encString)!=2)
1558 || (strcmp(utString,"EncodingScheme")!=0)) {
1559 wxLogDebug("AFM-file '%s': line '%s' has error (bad EncodingScheme)\n",
1560 afmName,line);
1561 }
1562 else if (strncmp(encString, "AdobeStandardEncoding", 21))
1563 {
1564 wxLogDebug("AFM-file '%s': line '%s' has error (unsupported EncodingScheme %s)\n",
1565 afmName,line, encString);
1566 }
1567 }
1568 // B.) check for char-width
1569 else if(strncmp(line,"C ",2)==0){
1570 if(sscanf(line,"%s%d%s%s%d",
1571 cString,&ascii,semiString,WXString,&cWidth)!=5){
1572 wxLogDebug("AFM-file '%s': line '%s' has an error (bad character width)\n",afmName,line);
1573 }
1574 if(strcmp(cString,"C")!=0 || strcmp(semiString,";")!=0 ||
1575 strcmp(WXString,"WX")!=0){
1576 wxLogDebug("AFM-file '%s': line '%s' has a format error\n",afmName,line);
1577 }
1578 //printf(" char '%c'=%d has width '%d'\n",ascii,ascii,cWidth);
1579 if(ascii>=0 && ascii<256){
1580 lastWidths[ascii] = cWidth; // store width
1581 }else{
1582 /* MATTHEW: this happens a lot; don't print an error */
1583 // wxLogDebug("AFM-file '%s': ASCII value %d out of range\n",afmName,ascii);
1584 }
1585 }
1586 // C.) ignore other entries.
1587 }
1588 fclose(afmFile);
1589 }
1590 // hack to compute correct values for german 'Umlaute'
1591 // the correct way would be to map the character names
1592 // like 'adieresis' to corresp. positions of ISOEnc and read
1593 // these values from AFM files, too. Maybe later ...
1594 lastWidths[196] = lastWidths['A']; // Ä
1595 lastWidths[228] = lastWidths['a']; // ä
1596 lastWidths[214] = lastWidths['O']; // Ö
1597 lastWidths[246] = lastWidths['o']; // ö
1598 lastWidths[220] = lastWidths['U']; // Ü
1599 lastWidths[252] = lastWidths['u']; // ü
1600 lastWidths[223] = lastWidths[251]; // ß
1601 }
1602
1603 // JC: calculate UnderlineThickness/UnderlinePosition
1604 m_underlinePosition = m_underlinePosition * fontToUse->GetPointSize() / 1000.0f;
1605 m_underlineThickness = m_underlineThickness * fontToUse->GetPointSize() / 1000.0f * m_scaleFactor;
1606
1607 // 3. now the font metrics are read in, calc size *******************
1608 // this is done by adding the widths of the characters in the
1609 // string. they are given in 1/1000 of the size!
1610
1611 long widthSum=0;
1612 long height=Size; // by default
1613 unsigned char *p;
1614 for(p=(unsigned char *)(const char *)string; *p; p++){
1615 if(lastWidths[*p]== INT_MIN){
1616 wxLogDebug("GetTextExtent: undefined width for character '%c' (%d)\n",
1617 *p,*p);
1618 widthSum += (long)(lastWidths[' ']/1000.0F * Size); // assume space
1619 }else{
1620 widthSum += (long)((lastWidths[*p]/1000.0F)*Size);
1621 }
1622 }
1623 // add descender to height (it is usually a negative value)
1624 if(lastDescender!=INT_MIN){
1625 height += (long)(((-lastDescender)/1000.0F) * Size); /* MATTHEW: forgot scale */
1626 }
1627
1628 // return size values
1629 *x = widthSum;
1630 *y = height;
1631
1632 // return other parameters
1633 if (descent){
1634 if(lastDescender!=INT_MIN){
1635 *descent = (long)(((-lastDescender)/1000.0F) * Size); /* MATTHEW: forgot scale */
1636 }else{
1637 *descent = 0;
1638 }
1639 }
1640
1641 // currently no idea how to calculate this!
1642 // if (externalLeading) *externalLeading = 0;
1643 if (externalLeading)
1644 *externalLeading = 0;
1645
1646 // ----- end of contributed code -----
1647 #endif
1648 }
1649
1650 void wxPostScriptDC::GetSizeMM(long *width, long *height) const
1651 {
1652 const char *paperType = wxThePrintSetupData->GetPaperName();
1653
1654 if (!paperType) paperType = _("A4 210 x 297 mm");
1655
1656 wxPrintPaperType *paper = wxThePrintPaperDatabase->FindPaperType(paperType);
1657
1658 if (!paper) paper = wxThePrintPaperDatabase->FindPaperType(_("A4 210 x 297 mm"));
1659
1660 if (paper)
1661 {
1662 if (width) *width = paper->widthMM;
1663 if (height) *height = paper->heightMM;
1664 }
1665 else
1666 {
1667 if (width) *width = 210;
1668 if (height) *height = 297;
1669 }
1670 }
1671
1672 #endif
1673 // wxUSE_POSTSCRIPT