1<?php
2// $Header: /cvsroot/html2ps/output.fastps.class.php,v 1.18 2007/05/17 13:55:13 Konstantin Exp $
3
4define('FASTPS_STATUS_DOCUMENT_INITIALIZED',0);
5define('FASTPS_STATUS_OUTPUT_STARTED',1);
6define('FASTPS_STATUS_OUTPUT_TERMINATED',2);
7
8class OutputDriverFastPS extends OutputDriverGenericPS {
9  var $found_fonts;
10  var $used_encodings;
11  var $font_factory;
12  var $status;
13
14  var $overline;
15  var $underline;
16  var $linethrough;
17
18  function OutputDriverFastPS(&$image_encoder) {
19    $this->OutputDriverGenericPS($image_encoder);
20  }
21
22  function add_link($x, $y, $w, $h, $target) {
23    $this->write(sprintf("[ /Rect [ %.2f %.2f %.2f %.2f ] /Action << /Subtype /URI /URI (%s) >> /Border [0 0 0] /Subtype /Link /ANN pdfmark\n",
24                         $x, $y, $x+$w, $y-$h, $this->_string($target)));
25  }
26
27  function add_local_link($left, $top, $width, $height, $anchor) {
28    $this->write(sprintf("[ /Rect [ %.2f %.2f %.2f %.2f ] /Page %d /View [ /XYZ null %.2f null ] /Border [0 0 0] /Subtype /Link /ANN pdfmark\n",
29                         $left, $top, $left + $width, $top - $height, $anchor->page, $anchor->y));
30  }
31
32  function circle($x, $y, $r) {
33    $this->moveto($x, $y);
34    $this->write(sprintf("%.2f %.2f %.2f 0 360 arc\n", $x, $y, $r));
35  }
36
37  function clip() {
38    $this->write("clip newpath\n");
39  }
40
41  function close() {
42    $this->_terminate_output();
43    fclose($this->data);
44  }
45
46  function closepath() {
47    $this->write("closepath\n");
48  }
49
50  function dash($x, $y) {
51    $this->write(sprintf("[%.2f %.2f] 0 setdash\n", $x, $y));
52  }
53
54  function decoration($underline, $overline, $linethrough) {
55    $this->underline   = $underline;
56    $this->overline    = $overline;
57    $this->linethrough = $linethrough;
58  }
59
60  function fill() {
61    $this->write("fill\n");
62  }
63
64  function _findfont($name, $encoding) {
65    $font =& $this->font_factory->get_type1($name, $encoding);
66    if (is_null($font)) {
67      $this->error_message .= $this->font_factory->error_message();
68      $dummy = null;
69      return $dummy;
70    };
71
72    if (!isset($this->used_encodings[$encoding])) {
73      $this->used_encodings[$encoding] = true;
74
75      $manager = ManagerEncoding::get();
76      $this->_write_document_prolog($manager->get_ps_encoding_vector($encoding));
77      $this->_write_document_prolog("\n");
78    };
79
80    $fontname = $font->name();
81    if (!isset($this->found_fonts[$fontname])) {
82      $this->found_fonts[$fontname] = true;
83
84      $this->_write_document_prolog("/$fontname /$name $encoding findfont-enc def\n");
85    };
86
87    return $font;
88  }
89
90  // @return 'null' in case of error or ascender fraction of font-size
91  //
92  function font_ascender($name, $encoding) {
93    $font = $this->_findfont($name, $encoding);
94    if (is_null($font)) { return null; };
95
96    return $font->ascender()/1000;
97  }
98
99  // @return 'null' in case of error or ascender fraction of font-size
100  //
101  function font_descender($name, $encoding) {
102    $font = $this->_findfont($name, $encoding);
103    if (is_null($font)) { return null; };
104
105    return -$font->descender()/1000;
106  }
107
108  function get_bottom() {
109    return $this->bottom + $this->offset;
110  }
111
112  function &get_font_resolver() {
113    global $g_font_resolver;
114    return $g_font_resolver;
115  }
116
117  function image($image, $x, $y, $scale) {
118    $image_encoder = $this->get_image_encoder();
119    $id = $image_encoder->auto($this, $image, $size_x, $size_y, $tcolor, $image, $mask);
120    $init = "image-".$id."-init";
121
122    $this->moveto($x, $y);
123    $this->write(sprintf("%.2f %.2f %s %s {%s} %d %d image-create image-show\n",
124                         $size_x * $scale,
125                         $size_y * $scale,
126                         ($mask !== "" ? $mask : "/null"),
127                         $image,
128                         $init,
129                         $size_y,
130                         $size_x));
131  }
132
133  function image_scaled($image, $x, $y, $scale_x, $scale_y) {
134    $image_encoder = $this->get_image_encoder();
135    $id = $image_encoder->auto($this, $image, $size_x, $size_y, $tcolor, $image, $mask);
136    $init = "image-".$id."-init";
137
138    $this->moveto($x, $y);
139    $this->write(sprintf("%.2f %.2f %s %s {%s} %d %d image-create image-show\n",
140                         $size_x * $scale_x ,
141                         $size_y * $scale_y,
142                         ($mask !== "" ? $mask : "/null"),
143                         $image,
144                         $init,
145                         $size_y,
146                         $size_x));
147  }
148
149  function image_ry($image, $x, $y, $height, $bottom, $ox, $oy, $scale) {
150    $image_encoder = $this->get_image_encoder();
151    $id = $image_encoder->auto($this, $image, $size_x, $size_y, $tcolor, $image, $mask);
152    $init = "image-".$id."-init";
153
154    $this->write(sprintf("%.2f %.2f %.2f %.2f %.2f %.2f %.2f %s %s {%s} %d %d image-create image-show-repeat-y\n",
155                         $scale, $oy, $ox, $bottom, $height, $y, $x,
156                         ($mask !== "" ? $mask : "/null"),
157                         $image,
158                         $init,
159                         $size_y,
160                         $size_x));
161  }
162
163  function image_rx($image, $x, $y, $width, $right, $ox, $oy, $scale) {
164    $image_encoder = $this->get_image_encoder();
165    $id = $image_encoder->auto($this, $image, $size_x, $size_y, $tcolor, $image, $mask);
166    $init = "image-".$id."-init";
167
168    $this->write(sprintf("%.2f %.2f %.2f %.2f %.2f %.2f %.2f %s %s {%s} %d %d image-create image-show-repeat-x\n",
169                         $scale, $oy, $ox, $right, $width, $y, $x,
170                         ($mask !== "" ? $mask : "/null"),
171                         $image,
172                         $init,
173                         $size_y,
174                         $size_x));
175  }
176
177  function image_rx_ry($image, $x, $y, $width, $height, $right, $bottom, $ox, $oy, $scale) {
178    $image_encoder = $this->get_image_encoder();
179    $id = $image_encoder->auto($this, $image, $size_x, $size_y, $tcolor, $image, $mask);
180    $init = "image-".$id."-init";
181
182    $this->write(sprintf("%.2f %.2f %.2f %.2f %.2f %.2f %.2f  %.2f %.2f %s %s {%s} %d %d image-create image-show-repeat-xy\n",
183                         $scale, $oy, $ox, $bottom, $right, $height, $width, $y, $x,
184                         ($mask !== "" ? $mask : "/null"),
185                         $image,
186                         $init,
187                         $size_y,
188                         $size_x));
189  }
190
191  function lineto($x, $y) {
192    $data = sprintf("%.2f %.2f lineto\n", $x, $y);
193    $this->write($data);
194  }
195
196  function moveto($x, $y) {
197    $data = sprintf("%.2f %.2f moveto\n", $x, $y);
198    $this->write($data);
199  }
200
201  function next_page($height) {
202    if ($this->current_page > 0) {
203      $this->write("showpage\n");
204    };
205
206    $this->offset -= $height - $this->offset_delta;
207
208    // Reset the "correction" offset to it normal value
209    // Note: "correction" offset is an offset value required to avoid page breaking
210    // in the middle of text boxes
211    $this->offset_delta = 0;
212
213    $this->write(sprintf("%%%%Page: %d %d\n", $this->current_page + 1, $this->current_page + 1));
214    $this->write("%%BeginPageSetup\n");
215    $this->write(sprintf("initpage\n"));
216    $this->write(sprintf("0 %.2f translate\n", -$this->offset));
217    $this->write("0 0 0 setrgbcolor\n");
218    $this->write("%%EndPageSetup\n");
219
220    parent::next_page($height);
221  }
222
223  function reset(&$media) {
224    OutputDriverGenericPS::reset($media);
225
226    $this->media =& $media;
227    $this->data = fopen($this->get_filename(), "wb");
228
229    // List of fonts names which already had generated findfond PS code
230    $this->found_fonts = array();
231
232    $this->used_encodings = array();
233
234    $this->overline = false;
235    $this->underline = false;
236    $this->linethrough = false;
237
238    // A font class factory
239    $this->font_factory =& new FontFactory;
240
241    $this->_document_body = '';
242    $this->_document_prolog = '';
243
244    $this->status = FASTPS_STATUS_DOCUMENT_INITIALIZED;
245  }
246
247  function restore() {
248    $this->write("grestore\n");
249  }
250
251  function save() {
252    $this->write("gsave\n");
253  }
254
255  // @return true normally or null in case of error
256  //
257  function setfont($name, $encoding, $size) {
258    $this->fontsize    = $size;
259    $this->currentfont = $this->_findfont($name, $encoding);
260
261    if (is_null($this->currentfont)) { return null; };
262
263    $this->write(sprintf("%s %.2f scalefont setfont\n", $this->currentfont->name(), $size));
264
265    return true;
266  }
267
268  function setlinewidth($x) {
269    $data = sprintf("%.2f setlinewidth\n", $x);
270    $this->write($data);
271  }
272
273  function setrgbcolor($r, $g, $b)  {
274    $data = sprintf("%.2f %.2f %.2f setrgbcolor\n", $r, $g, $b);
275    $this->write($data);
276  }
277
278  function show_xy($text, $x, $y) {
279    if (trim($text) !== '') {
280      $this->moveto($x, $y);
281      $this->write("(".$this->_string($text).") show\n");
282    };
283
284    $width = Font::points($this->fontsize, $this->currentfont->stringwidth($text));
285    if ($this->overline)    { $this->_show_overline($x, $y, $width, $this->fontsize);  };
286    if ($this->underline)   { $this->_show_underline($x, $y, $width, $this->fontsize); };
287    if ($this->linethrough) { $this->_show_linethrough($x, $y, $width, $this->fontsize); };
288  }
289
290  function stringwidth($string, $name, $encoding, $size) {
291    $font =& $this->font_factory->get_type1($name, $encoding);
292
293    if (is_null($font)) {
294      $this->error_message .= $this->font_factory->error_message();
295      $dummy = null;
296      return $dummy;
297    };
298
299    return Font::points($size, $font->stringwidth($string));
300  }
301
302  function stroke() {
303    $this->write("stroke\n");
304  }
305
306  function write($string) {
307    if ($this->status == FASTPS_STATUS_DOCUMENT_INITIALIZED) {
308      $this->_start_output();
309    };
310
311    $this->_document_body .= $string;
312  }
313
314  function _write_document_prolog($string) {
315    $this->_document_prolog .= $string;
316  }
317
318  function _show_line($x, $y, $width, $height, $up, $ut) {
319    $this->setlinewidth($ut);
320    $this->moveto($x, $y + $up);
321    $this->lineto($x+$width, $y + $up);
322    $this->stroke();
323  }
324
325  function _show_underline($x, $y, $width, $height) {
326    $up = Font::points($this->fontsize, $this->currentfont->underline_position());
327    $ut = Font::points($this->fontsize, $this->currentfont->underline_thickness());
328
329    $this->_show_line($x, $y, $width, $height, $up, $ut);
330  }
331
332  function _show_overline($x, $y, $width, $height) {
333    $up = Font::points($this->fontsize, $this->currentfont->overline_position());
334    $ut = Font::points($this->fontsize, $this->currentfont->underline_thickness());
335
336    $this->_show_line($x, $y, $width, $height, $up, $ut);
337  }
338
339  function _show_linethrough($x, $y, $width, $height) {
340    $up = Font::points($this->fontsize, $this->currentfont->linethrough_position());
341    $ut = Font::points($this->fontsize, $this->currentfont->underline_thickness());
342
343    $this->_show_line($x, $y, $width, $height, $up, $ut);
344  }
345
346  function _start_output() {
347    $this->status = FASTPS_STATUS_OUTPUT_STARTED;
348  }
349
350  function _terminate_output() {
351    /**
352     * Prepare the PS file header
353     * Note that %PS-Adobe-3.0 refers to DSC version, NOT language level
354     */
355    $header = file_get_contents(HTML2PS_DIR.'postscript/fastps.header.ps');
356
357    global $g_config;
358    $header = preg_replace("/##PS2PDF##/",
359                           ($g_config['ps2pdf'] && $g_config['transparency_workaround']) ? "/ps2pdf-transparency-hack true def" : "/ps2pdf-transparency-hack false def",$header);
360    $header = preg_replace("/##TRANSPARENCY##/",($g_config['transparency_workaround']) ? "/no-transparency-output true def" : "/no-transparency-output false def",$header);
361    $header = preg_replace("/##PAGES##/", $this->expected_pages, $header);
362
363    $header = preg_replace("/##BBOX##/", $this->media->to_bbox(), $header);
364    $header = preg_replace("/##MEDIA##/", $this->media->to_ps(), $header);
365
366    $header = preg_replace("/##PROLOG##/", $this->_document_prolog, $header);
367
368    fwrite($this->data, $header);
369    fwrite($this->data, "\n");
370    fwrite($this->data, $this->_document_body);
371
372    $footer = file_get_contents(HTML2PS_DIR.'postscript/fastps.footer.ps');
373    fwrite($this->data, $footer);
374  }
375
376  function _show_watermark() {
377  }
378
379  /**
380   * Protected output-specific methods
381   */
382
383  /**
384   * Escapes special Postscript symbols '(',')' and '%' inside a text string
385   */
386  function _string($str) {
387    $str = str_replace("\\", "\\\\", $str);
388    $str = str_replace(array("(",")","%"), array("\\(","\\)","\\%"), $str);
389
390    // Replace characters having 8-bit set with their octal representation
391    for ($i=0; $i<strlen($str); $i++) {
392      if (ord($str{$i}) > 127) {
393        $str = substr_replace($str, sprintf("\\%o", ord($str{$i})), $i, 1);
394        $i += 3;
395      };
396    };
397
398    return $str;
399  }
400}
401
402?>