Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
// $Header: /cvsroot/html2ps/output.png.class.php,v 1.7 2007/05/07 13:12:07 Konstantin Exp $
3
 
4
require_once(HTML2PS_DIR.'ot.class.php');
5
require_once(HTML2PS_DIR.'path.php');
6
require_once(HTML2PS_DIR.'font_factory.class.php');
7
 
8
/**
9
 * TODO: of course, it is not 'real' affine transformation;
10
 * it is just a compatibility hack
11
 */
12
class AffineTransform {
13
  var $_y_offset;
14
  var $_x_scale;
15
  var $_y_scale;
16
 
17
  function AffineTransform($y_offset, $x_scale, $y_scale) {
18
    $this->_y_offset = $y_offset;
19
    $this->_x_scale = $x_scale;
20
    $this->_y_scale = $y_scale;
21
  }
22
 
23
  function apply(&$x, &$y) {
24
    $x = floor($x * $this->_x_scale);
25
    $y = floor($this->_y_offset - $y * $this->_y_scale);
26
  }
27
}
28
 
29
class OutputDriverPNG extends OutputDriverGeneric {
30
  var $_image;
31
 
32
  var $_clipping;
33
 
34
  var $_media;
35
  var $_heightPixels;
36
  var $_widthPixels;
37
  var $_color;
38
  var $_font;
39
  var $_path;
40
 
41
  /**
42
   * This variable  contains an  array of clipping  contexts. Clipping
43
   * context describes the "active area" and "base" image (image which
44
   * will take the changes drawn in clipped area).
45
   *
46
   * As GD does not support  clipping natively, when new clipping area
47
   * is  defined,  we  create  new  image. When  clipping  context  is
48
   * terminated (i.e. by establishing new clipping context, by call to
49
   * 'restore' or by finishing the image output), only area bounded by
50
   * clipping region  is copied  to the "base"  image. Note  that This
51
   * will  increase the  memory  consumption, as  we'll  need to  keep
52
   * several active images at once.
53
   */
54
  var $_clip;
55
 
56
  function _restoreColor() {
57
    imagecolordeallocate($this->_image, $this->_color[0]);
58
    array_shift($this->_color);
59
  }
60
 
61
  function _restoreClip() {
62
    /**
63
     * As clipping context images have the same size/scale, we may use
64
     * the simplest/fastest image copying function
65
     */
66
    $clip = $this->_clipping[0];
67
    imagecopy($clip['image'],
68
              $this->_image,
69
              $clip['box']->ll->x,
70
              $clip['box']->ll->y,
71
              $clip['box']->ll->x,
72
              $clip['box']->ll->y,
73
              $clip['box']->getWidth(),
74
              $clip['box']->getHeight());
75
 
76
    /**
77
     * Now we should free image allocated for the clipping context to avoid memory leaks
78
     */
79
    imagedestroy($this->_image);
80
    $this->_image = $clip['image'];
81
 
82
    /**
83
     * Remove clipping context from the stack
84
     */
85
    array_shift($this->_clipping);
86
  }
87
 
88
  function _saveColor($rgb) {
89
    $color = imagecolorallocate($this->_image, $rgb[0], $rgb[1], $rgb[2]);
90
    array_unshift($this->_color, array('rgb'    => $rgb,
91
                                       'object' => $color));
92
  }
93
 
94
  function _saveClip($box) {
95
    /**
96
     * Initialize clipping  context record and add it  to the clipping
97
     * stack
98
     */
99
    $clip = array('image' => $this->_image,
100
                  'box'   => $box);
101
    array_unshift($this->_clipping, $clip);
102
 
103
    /**
104
     * Create a copy of current image for the clipping context
105
     */
106
    $width  = imagesx($clip['image']);
107
    $height = imagesy($clip['image']);
108
    $this->_image = imagecreatetruecolor($width,
109
                                         $height);
110
    imagecopy($this->_image,
111
              $clip['image'],
112
              0,0,
113
              0,0,
114
              $width, $height);
115
  }
116
 
117
  function _getCurrentColor() {
118
    return $this->_color[0]['object'];
119
  }
120
 
121
  function _setColor($color) {
122
    imagecolordeallocate($this->_image, $this->_color[0]['object']);
123
    $this->_color[0] = $color;
124
  }
125
 
126
  function _setFont($typeface, $encoding, $size) {
127
    global $g_font_resolver_pdf;
128
    $fontfile = $g_font_resolver_pdf->ttf_mappings[$typeface];
129
 
130
    $font = $this->_font_factory->getTrueType($typeface, $encoding);
131
    $ascender = $font->ascender() / 1000;
132
 
133
    $this->_font[0] = array('font'     => $typeface,
134
                            'encoding' => $encoding,
135
                            'size'     => $size,
136
                            'ascender' => $ascender);
137
  }
138
 
139
  function _getFont() {
140
    return $this->_font[0];
141
  }
142
 
143
  function _drawLine($x1, $y1, $x2, $y2) {
144
    imageline($this->_image, $x1, $y1, $x2, $y2, $this->_color[0]['object']);
145
  }
146
 
147
  /**
148
   * Note that "paper space" have Y coordinate axis directed to the bottom,
149
   * while images have Y coordinate axis directory to the top
150
   */
151
  function _fixCoords(&$x, &$y) {
152
    $x = $this->_fixCoordX($x);
153
    $y = $this->_fixCoordY($y);
154
  }
155
 
156
  function _fixCoordX($source_x) {
157
    $x = $source_x;
158
    $dummy = 0;
159
    $this->_transform->apply($x, $dummy);
160
    return $x;
161
  }
162
 
163
  function _fixCoordY($source_y) {
164
    $y = $source_y;
165
    $dummy = 0;
166
    $this->_transform->apply($dummy, $y);
167
    return $y;
168
  }
169
 
170
  function _fixSizes(&$x, &$y) {
171
    $x = $this->_fixSizeX($x);
172
    $y = $this->_fixSizeY($y);
173
  }
174
 
175
  function _fixSizeX($x) {
176
    static $scale = null;
177
    if (is_null($scale)) { $scale = $this->_widthPixels / mm2pt($this->media->width()); };
178
    return ceil($x * $scale);
179
  }
180
 
181
  function _fixSizeY($y) {
182
    static $scale = null;
183
    if (is_null($scale)) { $scale = $this->_heightPixels / mm2pt($this->media->height()); };
184
    return ceil($y * $scale);
185
  }
186
 
187
  function OutputDriverPNG() {
188
    $this->OutputDriverGeneric();
189
 
190
    $this->_color    = array();
191
    $this->_font     = array();
192
    $this->_path     = new Path;
193
    $this->_clipping = array();
194
 
195
    $this->_font_factory = new FontFactory();
196
  }
197
 
198
  function reset(&$media) {
199
    parent::reset($media);
200
 
201
    $this->update_media($media);
202
  }
203
 
204
  function update_media($media) {
205
    parent::update_media($media);
206
 
207
    /**
208
     * Here we use a small hack; media height and width (in millimetres) match
209
     * the size of screenshot (in pixels), so we take them as-is
210
     */
211
    $this->_heightPixels = $media->height();
212
    $this->_widthPixels  = $media->width();
213
 
214
    $this->_image = imagecreatetruecolor($this->_widthPixels,
215
                                         $this->_heightPixels);
216
    /**
217
     * Render white background
218
     */
219
    $white = imagecolorallocate($this->_image, 255,255,255);
220
    imagefill($this->_image, 0,0,$white);
221
    imagecolordeallocate($this->_image, $white);
222
 
223
    $this->_color[0] = array('rgb'    => array(0,0,0),
224
                             'object' => imagecolorallocate($this->_image, 0,0,0));
225
 
226
    /**
227
     * Setup initial clipping region
228
     */
229
    $this->_clipping = array();
230
    $this->_saveClip(new Rectangle(new Point(0,
231
                                             0),
232
                                   new Point($this->_widthPixels-1,
233
                                             $this->_heightPixels-1)));
234
 
235
    $this->_transform = new AffineTransform($this->_heightPixels,
236
                                            $this->_widthPixels / mm2pt($this->media->width()),
237
                                            $this->_heightPixels / mm2pt($this->media->height()));
238
  }
239
 
240
  function add_link($x, $y, $w, $h, $target) { /* N/A */ }
241
  function add_local_link($left, $top, $width, $height, $anchor) { /* N/A */ }
242
 
243
  function circle($x, $y, $r) {
244
    $this->_path = new PathCircle();
245
    $this->_path->set_r($r);
246
    $this->_path->set_x($x);
247
    $this->_path->set_y($y);
248
  }
249
 
250
  function clip() {
251
    /**
252
     * Only  rectangular  clipping  areas  are  supported;  we'll  use
253
     * bounding box of  current path for clipping. If  current path is
254
     * an rectangle, bounding box will match the path itself.
255
     */
256
    $box = $this->_path->getBbox();
257
 
258
    /**
259
     * Convert bounding from media coordinates
260
     * to output device coordinates
261
     */
262
    $this->_fixCoords($box->ll->x, $box->ll->y);
263
    $this->_fixCoords($box->ur->x, $box->ur->y);
264
    $box->normalize();
265
 
266
    /**
267
     * Add a clipping context information
268
     */
269
    $this->_restoreClip();
270
    $this->_saveClip($box);
271
 
272
    /**
273
     * Reset path after clipping have been applied
274
     */
275
    $this->_path = new Path;
276
  }
277
 
278
  function close() {
279
    /**
280
     * A small hack; as clipping  context is save every time 'save' is
281
     * called, we may deterine the number of graphic contexts saved by
282
     * the size of clipping context stack
283
     */
284
    while (count($this->_clipping) > 0) {
285
      $this->restore();
286
    };
287
 
288
    imagepng($this->_image, $this->get_filename());
289
    imagedestroy($this->_image);
290
  }
291
 
292
  function closepath() {
293
    $this->_path->close();
294
  }
295
 
296
  function content_type() {
297
    return ContentType::png();
298
  }
299
 
300
  function dash($x, $y) { }
301
  function decoration($underline, $overline, $strikeout) { }
302
 
303
  function error_message() {
304
    return "OutputDriverPNG: generic error";
305
  }
306
 
307
  function field_multiline_text($x, $y, $w, $h, $value, $field_name) { /* N/A */ }
308
  function field_text($x, $y, $w, $h, $value, $field_name) { /* N/A */ }
309
  function field_password($x, $y, $w, $h, $value, $field_name) { /* N/A */ }
310
  function field_pushbutton($x, $y, $w, $h) { /* N/A */ }
311
  function field_pushbuttonimage($x, $y, $w, $h, $field_name, $value, $actionURL) { /* N/A */ }
312
  function field_pushbuttonreset($x, $y, $w, $h) { /* N/A */ }
313
  function field_pushbuttonsubmit($x, $y, $w, $h, $field_name, $value, $actionURL) { /* N/A */ }
314
  function field_checkbox($x, $y, $w, $h, $name, $value) { /* N/A */ }
315
  function field_radio($x, $y, $w, $h, $groupname, $value, $checked) { /* N/A */ }
316
  function field_select($x, $y, $w, $h, $name, $value, $options) { /* N/A */ }
317
 
318
  function fill() {
319
    $this->_path->fill($this->_transform, $this->_image, $this->_getCurrentColor());
320
    $this->_path = new Path;
321
  }
322
 
323
  function font_ascender($name, $encoding) {
324
    $font = $this->_font_factory->getTrueType($name, $encoding);
325
    return $font->ascender() / 1000;
326
  }
327
 
328
  function font_descender($name, $encoding) {
329
    $font = $this->_font_factory->getTrueType($name, $encoding);
330
    return -$font->descender() / 1000;
331
  }
332
 
333
  function get_bottom() {}
334
 
335
  /**
336
   * Image output always contains only one page
337
   */
338
  function get_expected_pages() {
339
    return 1;
340
  }
341
 
342
  function image($image, $x, $y, $scale) {
343
    $this->image_scaled($image, $x, $y, $scale, $scale);
344
  }
345
 
346
  function image_scaled($image, $x, $y, $scale_x, $scale_y) {
347
    $this->_fixCoords($x, $y);
348
 
349
    $sx = $image->sx();
350
    $sy = $image->sy();
351
 
352
    /**
353
     * Get image size in device coordinates
354
     */
355
    $dx = $sx*$scale_x;
356
    $dy = $sy*$scale_y;
357
    $this->_fixSizes($dx, $dy);
358
 
359
    imagecopyresampled($this->_image, $image->get_handle(),
360
                       $x, $y-$dy,
361
                       0, 0,
362
                       $dx, $dy,
363
                       $sx, $sy);
364
  }
365
 
366
  function image_ry($image, $x, $y, $height, $bottom, $ox, $oy, $scale) {
367
    $base_y = floor($this->_fixCoordY($bottom));
368
    $this->_fixCoords($x, $y);
369
    $dest_height = floor($this->_fixSizeY($height));
370
    $start_y = $y - $dest_height;
371
 
372
    $sx = $image->sx();
373
    $sy = $image->sy();
374
    $dx = $this->_fixSizeX($sx * $scale);
375
    $dy = $this->_fixSizeY($sy * $scale);
376
 
377
    $cx = $x;
378
    $cy = $start_y - ceil($this->_fixSizeY($oy) / $dest_height) * $dest_height;
379
    while ($cy < $base_y) {
380
      imagecopyresampled($this->_image, $image->get_handle(),
381
                         $cx, $cy,
382
                         0, 0,
383
                         $dx, $dy,
384
                         $sx, $sy);
385
      $cy += $dest_height;
386
    };
387
  }
388
 
389
  function image_rx($image, $x, $y, $width, $right, $ox, $oy, $scale) {
390
    $base_x = floor($this->_fixCoordX($right));
391
    $this->_fixCoords($x, $y);
392
    $dest_width = floor($this->_fixSizeX($width));
393
    $start_x = $x - $dest_width;
394
 
395
    $sx = $image->sx();
396
    $sy = $image->sy();
397
    $dx = $this->_fixSizeX($sx * $scale);
398
    $dy = $this->_fixSizeY($sy * $scale);
399
 
400
    $cx = $start_x - ceil($this->_fixSizeX($oy) / $dest_width) * $dest_width;
401
 
402
    $cy = $y - $dy;
403
 
404
    while ($cx < $base_x) {
405
      imagecopyresampled($this->_image, $image->get_handle(),
406
                         $cx, $cy,
407
                         0, 0,
408
                         $dx, $dy,
409
                         $sx, $sy);
410
      $cx += $dest_width;
411
    };
412
  }
413
 
414
  function image_rx_ry($image, $x, $y, $width, $height, $right, $bottom, $ox, $oy, $scale) {
415
    $base_x = floor($this->_fixCoordX($right));
416
    $base_y = floor($this->_fixCoordY($bottom));
417
    $this->_fixCoords($x, $y);
418
    $dest_width  = floor($this->_fixSizeX($width));
419
    $dest_height = floor($this->_fixSizeY($height));
420
    $start_x = $x - $dest_width;
421
    $start_y = $y - $dest_height;
422
 
423
    $sx = $image->sx();
424
    $sy = $image->sy();
425
    $dx = $this->_fixSizeX($sx * $scale);
426
    $dy = $this->_fixSizeY($sy * $scale);
427
 
428
    $cx = $start_x - ceil($this->_fixSizeX($ox) / $dest_width)  * $dest_width;
429
    $cy = $start_y - ceil($this->_fixSizeY($oy) / $dest_height) * $dest_height;
430
 
431
    while ($cy < $base_y) {
432
      while ($cx < $base_x) {
433
        imagecopyresampled($this->_image,
434
                           $image->get_handle(),
435
                           $cx, $cy,
436
                           0, 0,
437
                           $dx, $dy,
438
                           $sx, $sy);
439
        $cx += $dest_width;
440
      };
441
      $cx = $start_x - ceil($this->_fixSizeX($ox) / $dest_width)  * $dest_width;
442
      $cy += $dest_height;
443
    };
444
  }
445
 
446
  function lineto($x, $y) {
447
    $this->_path->addPoint(new Point($x, $y));
448
  }
449
 
450
  function moveto($x, $y) {
451
    $this->_path->clear();
452
    $this->_path->addPoint(new Point($x, $y));
453
  }
454
 
455
  function new_form($name) { /* N/A */ }
456
  function next_page() { /* N/A */ }
457
  function release() { }
458
 
459
  /**
460
   * Note: _restoreClip  will change current image object,  so we must
461
   * release all  image-dependent objects before  call to _restoreClip
462
   * to ensure resources are released correctly
463
   */
464
  function restore() {
465
    $this->_restoreColor();
466
    $this->_restoreClip();
467
  }
468
 
469
  /**
470
   * Note:  _saveClip will  change current  image object,  so  we must
471
   * create  all image-dependent  objects after  call to  _saveClip to
472
   * ensure resources are created correctly
473
   */
474
  function save() {
475
    $this->_saveClip($this->_clipping[0]['box']);
476
    $this->_saveColor($this->_color[0]['rgb']);
477
  }
478
 
479
  function setfont($name, $encoding, $size) {
480
    $this->_setFont($name, $encoding, $size);
481
    return true;
482
  }
483
 
484
  function setlinewidth($x) {
485
    $dummy = 0;
486
    $this->_fixSizes($x, $dummy);
487
    imagesetthickness($this->_image, $x);
488
  }
489
 
490
  function setrgbcolor($r, $g, $b)  {
491
    $color = array('rgb'    => array($r, $g, $b),
492
                   'object' => imagecolorallocate($this->_image, $r*255, $g*255, $b*255));
493
    $this->_setColor($color);
494
  }
495
 
496
  function set_watermark($text) { }
497
 
498
  function show_xy($text, $x, $y) {
499
    $this->_fixCoords($x, $y);
500
 
501
    $font = $this->_getFont();
502
    $converter = Converter::create();
503
 
504
    global $g_font_resolver_pdf;
505
    $fontFile = $g_font_resolver_pdf->ttf_mappings[$font['font']];
506
 
507
    $fontSize = $font['size'];
508
 
509
    $dummy = 0;
510
    $this->_fixSizes($dummy, $fontSize);
511
 
512
    $utf8_string = $converter->to_utf8($text, $font['encoding']);
513
 
514
    imagefttext($this->_image,
515
                $fontSize * $font['ascender'],
516
                0,
517
                $x,
518
                $y,
519
                $this->_getCurrentColor(),
520
                TTF_FONTS_REPOSITORY.$fontFile,
521
                $utf8_string);
522
  }
523
 
524
  /**
525
   * Note: the koefficient is just a magic number; I'll need to examine the
526
   * imagefttext behavior more closely
527
   */
528
  function stringwidth($string, $name, $encoding, $size) {
529
    $font = $this->_font_factory->getTrueType($name, $encoding);
530
    return Font::points($size, $font->stringwidth($string))*1.25;
531
  }
532
 
533
  function stroke() {
534
    $this->_path->stroke($this->_transform, $this->_image, $this->_getCurrentColor());
535
    $this->_path = new Path;
536
  }
537
}
538
?>