Blame | Letzte Änderung | Log anzeigen | RSS feed
<?php// $Header: /cvsroot/html2ps/output.png.class.php,v 1.7 2007/05/07 13:12:07 Konstantin Exp $require_once(HTML2PS_DIR.'ot.class.php');require_once(HTML2PS_DIR.'path.php');require_once(HTML2PS_DIR.'font_factory.class.php');/*** TODO: of course, it is not 'real' affine transformation;* it is just a compatibility hack*/class AffineTransform {var $_y_offset;var $_x_scale;var $_y_scale;function AffineTransform($y_offset, $x_scale, $y_scale) {$this->_y_offset = $y_offset;$this->_x_scale = $x_scale;$this->_y_scale = $y_scale;}function apply(&$x, &$y) {$x = floor($x * $this->_x_scale);$y = floor($this->_y_offset - $y * $this->_y_scale);}}class OutputDriverPNG extends OutputDriverGeneric {var $_image;var $_clipping;var $_media;var $_heightPixels;var $_widthPixels;var $_color;var $_font;var $_path;/*** This variable contains an array of clipping contexts. Clipping* context describes the "active area" and "base" image (image which* will take the changes drawn in clipped area).** As GD does not support clipping natively, when new clipping area* is defined, we create new image. When clipping context is* terminated (i.e. by establishing new clipping context, by call to* 'restore' or by finishing the image output), only area bounded by* clipping region is copied to the "base" image. Note that This* will increase the memory consumption, as we'll need to keep* several active images at once.*/var $_clip;function _restoreColor() {imagecolordeallocate($this->_image, $this->_color[0]);array_shift($this->_color);}function _restoreClip() {/*** As clipping context images have the same size/scale, we may use* the simplest/fastest image copying function*/$clip = $this->_clipping[0];imagecopy($clip['image'],$this->_image,$clip['box']->ll->x,$clip['box']->ll->y,$clip['box']->ll->x,$clip['box']->ll->y,$clip['box']->getWidth(),$clip['box']->getHeight());/*** Now we should free image allocated for the clipping context to avoid memory leaks*/imagedestroy($this->_image);$this->_image = $clip['image'];/*** Remove clipping context from the stack*/array_shift($this->_clipping);}function _saveColor($rgb) {$color = imagecolorallocate($this->_image, $rgb[0], $rgb[1], $rgb[2]);array_unshift($this->_color, array('rgb' => $rgb,'object' => $color));}function _saveClip($box) {/*** Initialize clipping context record and add it to the clipping* stack*/$clip = array('image' => $this->_image,'box' => $box);array_unshift($this->_clipping, $clip);/*** Create a copy of current image for the clipping context*/$width = imagesx($clip['image']);$height = imagesy($clip['image']);$this->_image = imagecreatetruecolor($width,$height);imagecopy($this->_image,$clip['image'],0,0,0,0,$width, $height);}function _getCurrentColor() {return $this->_color[0]['object'];}function _setColor($color) {imagecolordeallocate($this->_image, $this->_color[0]['object']);$this->_color[0] = $color;}function _setFont($typeface, $encoding, $size) {global $g_font_resolver_pdf;$fontfile = $g_font_resolver_pdf->ttf_mappings[$typeface];$font = $this->_font_factory->getTrueType($typeface, $encoding);$ascender = $font->ascender() / 1000;$this->_font[0] = array('font' => $typeface,'encoding' => $encoding,'size' => $size,'ascender' => $ascender);}function _getFont() {return $this->_font[0];}function _drawLine($x1, $y1, $x2, $y2) {imageline($this->_image, $x1, $y1, $x2, $y2, $this->_color[0]['object']);}/*** Note that "paper space" have Y coordinate axis directed to the bottom,* while images have Y coordinate axis directory to the top*/function _fixCoords(&$x, &$y) {$x = $this->_fixCoordX($x);$y = $this->_fixCoordY($y);}function _fixCoordX($source_x) {$x = $source_x;$dummy = 0;$this->_transform->apply($x, $dummy);return $x;}function _fixCoordY($source_y) {$y = $source_y;$dummy = 0;$this->_transform->apply($dummy, $y);return $y;}function _fixSizes(&$x, &$y) {$x = $this->_fixSizeX($x);$y = $this->_fixSizeY($y);}function _fixSizeX($x) {static $scale = null;if (is_null($scale)) { $scale = $this->_widthPixels / mm2pt($this->media->width()); };return ceil($x * $scale);}function _fixSizeY($y) {static $scale = null;if (is_null($scale)) { $scale = $this->_heightPixels / mm2pt($this->media->height()); };return ceil($y * $scale);}function OutputDriverPNG() {$this->OutputDriverGeneric();$this->_color = array();$this->_font = array();$this->_path = new Path;$this->_clipping = array();$this->_font_factory = new FontFactory();}function reset(&$media) {parent::reset($media);$this->update_media($media);}function update_media($media) {parent::update_media($media);/*** Here we use a small hack; media height and width (in millimetres) match* the size of screenshot (in pixels), so we take them as-is*/$this->_heightPixels = $media->height();$this->_widthPixels = $media->width();$this->_image = imagecreatetruecolor($this->_widthPixels,$this->_heightPixels);/*** Render white background*/$white = imagecolorallocate($this->_image, 255,255,255);imagefill($this->_image, 0,0,$white);imagecolordeallocate($this->_image, $white);$this->_color[0] = array('rgb' => array(0,0,0),'object' => imagecolorallocate($this->_image, 0,0,0));/*** Setup initial clipping region*/$this->_clipping = array();$this->_saveClip(new Rectangle(new Point(0,0),new Point($this->_widthPixels-1,$this->_heightPixels-1)));$this->_transform = new AffineTransform($this->_heightPixels,$this->_widthPixels / mm2pt($this->media->width()),$this->_heightPixels / mm2pt($this->media->height()));}function add_link($x, $y, $w, $h, $target) { /* N/A */ }function add_local_link($left, $top, $width, $height, $anchor) { /* N/A */ }function circle($x, $y, $r) {$this->_path = new PathCircle();$this->_path->set_r($r);$this->_path->set_x($x);$this->_path->set_y($y);}function clip() {/*** Only rectangular clipping areas are supported; we'll use* bounding box of current path for clipping. If current path is* an rectangle, bounding box will match the path itself.*/$box = $this->_path->getBbox();/*** Convert bounding from media coordinates* to output device coordinates*/$this->_fixCoords($box->ll->x, $box->ll->y);$this->_fixCoords($box->ur->x, $box->ur->y);$box->normalize();/*** Add a clipping context information*/$this->_restoreClip();$this->_saveClip($box);/*** Reset path after clipping have been applied*/$this->_path = new Path;}function close() {/*** A small hack; as clipping context is save every time 'save' is* called, we may deterine the number of graphic contexts saved by* the size of clipping context stack*/while (count($this->_clipping) > 0) {$this->restore();};imagepng($this->_image, $this->get_filename());imagedestroy($this->_image);}function closepath() {$this->_path->close();}function content_type() {return ContentType::png();}function dash($x, $y) { }function decoration($underline, $overline, $strikeout) { }function error_message() {return "OutputDriverPNG: generic error";}function field_multiline_text($x, $y, $w, $h, $value, $field_name) { /* N/A */ }function field_text($x, $y, $w, $h, $value, $field_name) { /* N/A */ }function field_password($x, $y, $w, $h, $value, $field_name) { /* N/A */ }function field_pushbutton($x, $y, $w, $h) { /* N/A */ }function field_pushbuttonimage($x, $y, $w, $h, $field_name, $value, $actionURL) { /* N/A */ }function field_pushbuttonreset($x, $y, $w, $h) { /* N/A */ }function field_pushbuttonsubmit($x, $y, $w, $h, $field_name, $value, $actionURL) { /* N/A */ }function field_checkbox($x, $y, $w, $h, $name, $value) { /* N/A */ }function field_radio($x, $y, $w, $h, $groupname, $value, $checked) { /* N/A */ }function field_select($x, $y, $w, $h, $name, $value, $options) { /* N/A */ }function fill() {$this->_path->fill($this->_transform, $this->_image, $this->_getCurrentColor());$this->_path = new Path;}function font_ascender($name, $encoding) {$font = $this->_font_factory->getTrueType($name, $encoding);return $font->ascender() / 1000;}function font_descender($name, $encoding) {$font = $this->_font_factory->getTrueType($name, $encoding);return -$font->descender() / 1000;}function get_bottom() {}/*** Image output always contains only one page*/function get_expected_pages() {return 1;}function image($image, $x, $y, $scale) {$this->image_scaled($image, $x, $y, $scale, $scale);}function image_scaled($image, $x, $y, $scale_x, $scale_y) {$this->_fixCoords($x, $y);$sx = $image->sx();$sy = $image->sy();/*** Get image size in device coordinates*/$dx = $sx*$scale_x;$dy = $sy*$scale_y;$this->_fixSizes($dx, $dy);imagecopyresampled($this->_image, $image->get_handle(),$x, $y-$dy,0, 0,$dx, $dy,$sx, $sy);}function image_ry($image, $x, $y, $height, $bottom, $ox, $oy, $scale) {$base_y = floor($this->_fixCoordY($bottom));$this->_fixCoords($x, $y);$dest_height = floor($this->_fixSizeY($height));$start_y = $y - $dest_height;$sx = $image->sx();$sy = $image->sy();$dx = $this->_fixSizeX($sx * $scale);$dy = $this->_fixSizeY($sy * $scale);$cx = $x;$cy = $start_y - ceil($this->_fixSizeY($oy) / $dest_height) * $dest_height;while ($cy < $base_y) {imagecopyresampled($this->_image, $image->get_handle(),$cx, $cy,0, 0,$dx, $dy,$sx, $sy);$cy += $dest_height;};}function image_rx($image, $x, $y, $width, $right, $ox, $oy, $scale) {$base_x = floor($this->_fixCoordX($right));$this->_fixCoords($x, $y);$dest_width = floor($this->_fixSizeX($width));$start_x = $x - $dest_width;$sx = $image->sx();$sy = $image->sy();$dx = $this->_fixSizeX($sx * $scale);$dy = $this->_fixSizeY($sy * $scale);$cx = $start_x - ceil($this->_fixSizeX($oy) / $dest_width) * $dest_width;$cy = $y - $dy;while ($cx < $base_x) {imagecopyresampled($this->_image, $image->get_handle(),$cx, $cy,0, 0,$dx, $dy,$sx, $sy);$cx += $dest_width;};}function image_rx_ry($image, $x, $y, $width, $height, $right, $bottom, $ox, $oy, $scale) {$base_x = floor($this->_fixCoordX($right));$base_y = floor($this->_fixCoordY($bottom));$this->_fixCoords($x, $y);$dest_width = floor($this->_fixSizeX($width));$dest_height = floor($this->_fixSizeY($height));$start_x = $x - $dest_width;$start_y = $y - $dest_height;$sx = $image->sx();$sy = $image->sy();$dx = $this->_fixSizeX($sx * $scale);$dy = $this->_fixSizeY($sy * $scale);$cx = $start_x - ceil($this->_fixSizeX($ox) / $dest_width) * $dest_width;$cy = $start_y - ceil($this->_fixSizeY($oy) / $dest_height) * $dest_height;while ($cy < $base_y) {while ($cx < $base_x) {imagecopyresampled($this->_image,$image->get_handle(),$cx, $cy,0, 0,$dx, $dy,$sx, $sy);$cx += $dest_width;};$cx = $start_x - ceil($this->_fixSizeX($ox) / $dest_width) * $dest_width;$cy += $dest_height;};}function lineto($x, $y) {$this->_path->addPoint(new Point($x, $y));}function moveto($x, $y) {$this->_path->clear();$this->_path->addPoint(new Point($x, $y));}function new_form($name) { /* N/A */ }function next_page() { /* N/A */ }function release() { }/*** Note: _restoreClip will change current image object, so we must* release all image-dependent objects before call to _restoreClip* to ensure resources are released correctly*/function restore() {$this->_restoreColor();$this->_restoreClip();}/*** Note: _saveClip will change current image object, so we must* create all image-dependent objects after call to _saveClip to* ensure resources are created correctly*/function save() {$this->_saveClip($this->_clipping[0]['box']);$this->_saveColor($this->_color[0]['rgb']);}function setfont($name, $encoding, $size) {$this->_setFont($name, $encoding, $size);return true;}function setlinewidth($x) {$dummy = 0;$this->_fixSizes($x, $dummy);imagesetthickness($this->_image, $x);}function setrgbcolor($r, $g, $b) {$color = array('rgb' => array($r, $g, $b),'object' => imagecolorallocate($this->_image, $r*255, $g*255, $b*255));$this->_setColor($color);}function set_watermark($text) { }function show_xy($text, $x, $y) {$this->_fixCoords($x, $y);$font = $this->_getFont();$converter = Converter::create();global $g_font_resolver_pdf;$fontFile = $g_font_resolver_pdf->ttf_mappings[$font['font']];$fontSize = $font['size'];$dummy = 0;$this->_fixSizes($dummy, $fontSize);$utf8_string = $converter->to_utf8($text, $font['encoding']);imagefttext($this->_image,$fontSize * $font['ascender'],0,$x,$y,$this->_getCurrentColor(),TTF_FONTS_REPOSITORY.$fontFile,$utf8_string);}/*** Note: the koefficient is just a magic number; I'll need to examine the* imagefttext behavior more closely*/function stringwidth($string, $name, $encoding, $size) {$font = $this->_font_factory->getTrueType($name, $encoding);return Font::points($size, $font->stringwidth($string))*1.25;}function stroke() {$this->_path->stroke($this->_transform, $this->_image, $this->_getCurrentColor());$this->_path = new Path;}}?>