Primer commit del sistema avantika sin cambios

This commit is contained in:
2026-01-06 19:42:24 -06:00
commit 3ae4be5957
7127 changed files with 440072 additions and 0 deletions

View File

@@ -0,0 +1,85 @@
<?php
/**
* DOMPDF - PHP5 HTML to PDF renderer
*
* File: $RCSfile: absolute_positioner.cls.php,v $
* Created on: 2004-06-08
*
* Copyright (c) 2004 - Benj Carson <benjcarson@digitaljunkies.ca>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library in the file LICENSE.LGPL; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*
* Alternatively, you may distribute this software under the terms of the
* PHP License, version 3.0 or later. A copy of this license should have
* been distributed with this file in the file LICENSE.PHP . If this is not
* the case, you can obtain a copy at http://www.php.net/license/3_0.txt.
*
* The latest version of DOMPDF might be available at:
* http://www.dompdf.com/
*
* @link http://www.dompdf.com/
* @copyright 2004 Benj Carson
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @package dompdf
*/
/* $Id */
/**
* Positions absolutly positioned frames
*/
class Absolute_Positioner extends Positioner {
function __construct(Frame_Decorator $frame) { parent::__construct($frame); }
function position() {
$frame = $this->_frame;
$style = $frame->get_style();
$cb = $frame->get_containing_block();
$top = $style->length_in_pt($style->top, $cb["h"]);
$right = $style->length_in_pt($style->right, $cb["w"]);
$bottom = $style->length_in_pt($style->bottom, $cb["h"]);
$left = $style->length_in_pt($style->left, $cb["w"]);
$p = $frame->find_positionned_parent();
if ( $p ) {
// Get the parent's padding box (see http://www.w3.org/TR/CSS21/visuren.html#propdef-top)
list($x, $y, $w, $h) = $p->get_padding_box();
} else {
$x = $cb["x"];
$y = $cb["y"];
}
if ( $top !== "auto" ) {
$y += $top;
} else if ( $bottom !== "auto" ) {
// FIXME: need to know this frame's height before we can do this correctly
}
if ( $left !== "auto" ) {
$x += $left;
} else if ( $right !== "auto" ) {
// FIXME: need to know this frame's width before we can do this correctly
}
$frame->set_position($x, $y);
}
}

View File

@@ -0,0 +1,890 @@
<?php
/**
* DOMPDF - PHP5 HTML to PDF renderer
*
* File: $RCSfile: abstract_renderer.cls.php,v $
* Created on: 2004-06-01
*
* Copyright (c) 2004 - Benj Carson <benjcarson@digitaljunkies.ca>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library in the file LICENSE.LGPL; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*
* Alternatively, you may distribute this software under the terms of the
* PHP License, version 3.0 or later. A copy of this license should have
* been distributed with this file in the file LICENSE.PHP . If this is not
* the case, you can obtain a copy at http://www.php.net/license/3_0.txt.
*
* The latest version of DOMPDF might be available at:
* http://www.dompdf.com/
*
* @link http://www.dompdf.com/
* @copyright 2004 Benj Carson
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @contributor Helmut Tischer <htischer@weihenstephan.org>
* @package dompdf
*
* Changes
* @contributor Helmut Tischer <htischer@weihenstephan.org>
* @version 0.5.1.htischer.20090507
* - On background image
* - Clip invisible areas from background images, then merge identical
* image/size/offset to a single image.
* - Fix rounding of background image size.
* - Fix background image position given as percent
* - Check if identical image is already cached by cpdf. Then do not create
* duplicates to save memory and CPU time
* - Fix skipping of image repetition if area is too small
* - Do not create temporary files, but pass gd object directly
*/
/* $Id: abstract_renderer.cls.php 361 2011-02-16 21:03:05Z fabien.menager $ */
/**
* Base renderer class
*
* @access private
* @package dompdf
*/
abstract class Abstract_Renderer {
/**
* Rendering backend
*
* @var Canvas
*/
protected $_canvas;
/**
* Current dompdf instance
*
* @var DOMPDF
*/
protected $_dompdf;
/**
* Class constructor
*
* @param DOMPDF $dompdf The current dompdf instance
*/
function __construct(DOMPDF $dompdf) {
$this->_dompdf = $dompdf;
$this->_canvas = $dompdf->get_canvas();
}
/**
* Render a frame.
*
* Specialized in child classes
*
* @param Frame $frame The frame to render
*/
abstract function render(Frame $frame);
//........................................................................
/**
* Render a background image over a rectangular area
*
* @param string $img The background image to load
* @param float $x The left edge of the rectangular area
* @param float $y The top edge of the rectangular area
* @param float $width The width of the rectangular area
* @param float $height The height of the rectangular area
* @param Style $style The associated Style object
*/
protected function _background_image($url, $x, $y, $width, $height, $style) {
$sheet = $style->get_stylesheet();
// Skip degenerate cases
if ( $width == 0 || $height == 0 )
return;
//debugpng
if (DEBUGPNG) print '[_background_image '.$url.']';
list($img, $ext) = Image_Cache::resolve_url($url,
$sheet->get_protocol(),
$sheet->get_host(),
$sheet->get_base_path());
// Bail if the image is no good
if ( $img === DOMPDF_LIB_DIR . "/res/broken_image.png" )
return;
//Try to optimize away reading and composing of same background multiple times
//Postponing read with imagecreatefrom ...()
//final composition paramters and name not known yet
//Therefore read dimension directly from file, instead of creating gd object first.
//$img_w = imagesx($src); $img_h = imagesy($src);
list($img_w, $img_h) = dompdf_getimagesize($img);
if (!isset($img_w) || $img_w == 0 || !isset($img_h) || $img_h == 0) {
return;
}
$repeat = $style->background_repeat;
$bg_color = $style->background_color;
//Increase background resolution and dependent box size according to image resolution to be placed in
//Then image can be copied in without resize
$bg_width = round((float)($width * DOMPDF_DPI) / 72);
$bg_height = round((float)($height * DOMPDF_DPI) / 72);
//Need %bg_x, $bg_y as background pos, where img starts, converted to pixel
list($bg_x, $bg_y) = $style->background_position;
if ( is_percent($bg_x) ) {
// The point $bg_x % from the left edge of the image is placed
// $bg_x % from the left edge of the background rectangle
$p = ((float)$bg_x)/100.0;
$x1 = $p * $img_w;
$x2 = $p * $bg_width;
$bg_x = round($x2 - $x1);
} else {
$bg_x = round((float)($style->length_in_pt($bg_x)*DOMPDF_DPI) / 72);
}
if ( is_percent($bg_y) ) {
// The point $bg_y % from the left edge of the image is placed
// $bg_y % from the left edge of the background rectangle
$p = ((float)$bg_y)/100.0;
$y1 = $p * $img_h;
$y2 = $p * $bg_height;
$bg_y = round($y2 - $y1);
} else {
$bg_y = round((float)($style->length_in_pt($bg_y)*DOMPDF_DPI) / 72);
}
//clip background to the image area on partial repeat. Nothing to do if img off area
//On repeat, normalize start position to the tile at immediate left/top or 0/0 of area
//On no repeat with positive offset: move size/start to have offset==0
//Handle x/y Dimensions separately
if ( $repeat !== "repeat" && $repeat !== "repeat-x" ) {
//No repeat x
if ($bg_x < 0) {
$bg_width = $img_w + $bg_x;
} else {
$x += ($bg_x * 72)/DOMPDF_DPI;
$bg_width = $bg_width - $bg_x;
if ($bg_width > $img_w) {
$bg_width = $img_w;
}
$bg_x = 0;
}
if ($bg_width <= 0) {
return;
}
$width = (float)($bg_width * 72)/DOMPDF_DPI;
} else {
//repeat x
if ($bg_x < 0) {
$bg_x = - ((-$bg_x) % $img_w);
} else {
$bg_x = $bg_x % $img_w;
if ($bg_x > 0) {
$bg_x -= $img_w;
}
}
}
if ( $repeat !== "repeat" && $repeat !== "repeat-y" ) {
//no repeat y
if ($bg_y < 0) {
$bg_height = $img_h + $bg_y;
} else {
$y += ($bg_y * 72)/DOMPDF_DPI;
$bg_height = $bg_height - $bg_y;
if ($bg_height > $img_h) {
$bg_height = $img_h;
}
$bg_y = 0;
}
if ($bg_height <= 0) {
return;
}
$height = (float)($bg_height * 72)/DOMPDF_DPI;
} else {
//repeat y
if ($bg_y < 0) {
$bg_y = - ((-$bg_y) % $img_h);
} else {
$bg_y = $bg_y % $img_h;
if ($bg_y > 0) {
$bg_y -= $img_h;
}
}
}
//Optimization, if repeat has no effect
if ( $repeat === "repeat" && $bg_y <= 0 && $img_h+$bg_y >= $bg_height ) {
$repeat = "repeat-x";
}
if ( $repeat === "repeat" && $bg_x <= 0 && $img_w+$bg_x >= $bg_width ) {
$repeat = "repeat-y";
}
if ( ($repeat === "repeat-x" && $bg_x <= 0 && $img_w+$bg_x >= $bg_width) ||
($repeat === "repeat-y" && $bg_y <= 0 && $img_h+$bg_y >= $bg_height) ) {
$repeat = "no-repeat";
}
//Use filename as indicator only
//different names for different variants to have different copies in the pdf
//This is not dependent of background color of box! .'_'.(is_array($bg_color) ? $bg_color["hex"] : $bg_color)
//Note: Here, bg_* are the start values, not end values after going through the tile loops!
$filedummy = $img;
/*
//Make shorter strings with limited characters for cache associative array index - needed?
//Strip common base path - server root, explicite temp, default temp; remove unwanted characters;
$filedummy = strtr($filedummy,"\\:","//");
$p = strtr($_SERVER["DOCUMENT_ROOT"],"\\:","//");
$l = strlen($p);
if ( substr($filedummy,0,$l) == $p) {
$filedummy = substr($filedummy,$l);
} else {
$p = strtr(DOMPDF_TEMP_DIR,"\\:","//");
$l = strlen($p);
if ( substr($filedummy,0,$l) == $p) {
$filedummy = substr($filedummy,$l);
} else {
$p = strtr(sys_get_temp_dir(),"\\:","//");
$l = strlen($p);
if ( substr($filedummy,0,$l) == $p) {
$filedummy = substr($filedummy,$l);
}
}
}
*/
$filedummy .= '_'.$bg_width.'_'.$bg_height.'_'.$bg_x.'_'.$bg_y.'_'.$repeat;
//debugpng
//if (DEBUGPNG) print '<pre>[_background_image name '.$filedummy.']</pre>';
//Optimization to avoid multiple times rendering the same image.
//If check functions are existing and identical image already cached,
//then skip creation of duplicate, because it is not needed by addImagePng
if ( method_exists( $this->_canvas, "get_cpdf" ) &&
method_exists( $this->_canvas->get_cpdf(), "addImagePng" ) &&
method_exists( $this->_canvas->get_cpdf(), "image_iscached" ) &&
$this->_canvas->get_cpdf()->image_iscached($filedummy) ) {
$bg = null;
//debugpng
//if (DEBUGPNG) print '[_background_image skip]';
}
else {
// Create a new image to fit over the background rectangle
$bg = imagecreatetruecolor($bg_width, $bg_height);
//anyway default
//imagealphablending($img, true);
switch (strtolower($ext)) {
case "png":
$src = imagecreatefrompng($img);
break;
case "jpg":
case "jpeg":
$src = imagecreatefromjpeg($img);
break;
case "gif":
$src = imagecreatefromgif($img);
break;
case "bmp":
$src = imagecreatefrombmp($img);
break;
default: return; // Unsupported image type
}
if ($src == null) {
return;
}
//Background color if box is not relevant here
//Non transparent image: box clipped to real size. Background non relevant.
//Transparent image: The image controls the transparency and lets shine through whatever background.
//However on transparent imaage preset the composed image with the transparency color,
//to keep the transparency when copying over the non transparent parts of the tiles.
$ti = imagecolortransparent($src);
if ($ti >= 0) {
$tc = imagecolorsforindex($src,$ti);
$ti = imagecolorallocate($bg,$tc['red'],$tc['green'],$tc['blue']);
imagefill($bg,0,0,$ti);
imagecolortransparent($bg, $ti);
}
//This has only an effect for the non repeatable dimension.
//compute start of src and dest coordinates of the single copy
if ( $bg_x < 0 ) {
$dst_x = 0;
$src_x = -$bg_x;
} else {
$src_x = 0;
$dst_x = $bg_x;
}
if ( $bg_y < 0 ) {
$dst_y = 0;
$src_y = -$bg_y;
} else {
$src_y = 0;
$dst_y = $bg_y;
}
//For historical reasons exchange meanings of variables:
//start_* will be the start values, while bg_* will be the temporary start values in the loops
$start_x = $bg_x;
$start_y = $bg_y;
// Copy regions from the source image to the background
if ( $repeat === "no-repeat" ) {
// Simply place the image on the background
imagecopy($bg, $src, $dst_x, $dst_y, $src_x, $src_y, $img_w, $img_h);
} else if ( $repeat === "repeat-x" ) {
for ( $bg_x = $start_x; $bg_x < $bg_width; $bg_x += $img_w ) {
if ( $bg_x < 0 ) {
$dst_x = 0;
$src_x = -$bg_x;
$w = $img_w + $bg_x;
} else {
$dst_x = $bg_x;
$src_x = 0;
$w = $img_w;
}
imagecopy($bg, $src, $dst_x, $dst_y, $src_x, $src_y, $w, $img_h);
}
} else if ( $repeat === "repeat-y" ) {
for ( $bg_y = $start_y; $bg_y < $bg_height; $bg_y += $img_h ) {
if ( $bg_y < 0 ) {
$dst_y = 0;
$src_y = -$bg_y;
$h = $img_h + $bg_y;
} else {
$dst_y = $bg_y;
$src_y = 0;
$h = $img_h;
}
imagecopy($bg, $src, $dst_x, $dst_y, $src_x, $src_y, $img_w, $h);
}
} else if ( $repeat === "repeat" ) {
for ( $bg_y = $start_y; $bg_y < $bg_height; $bg_y += $img_h ) {
for ( $bg_x = $start_x; $bg_x < $bg_width; $bg_x += $img_w ) {
if ( $bg_x < 0 ) {
$dst_x = 0;
$src_x = -$bg_x;
$w = $img_w + $bg_x;
} else {
$dst_x = $bg_x;
$src_x = 0;
$w = $img_w;
}
if ( $bg_y < 0 ) {
$dst_y = 0;
$src_y = -$bg_y;
$h = $img_h + $bg_y;
} else {
$dst_y = $bg_y;
$src_y = 0;
$h = $img_h;
}
imagecopy($bg, $src, $dst_x, $dst_y, $src_x, $src_y, $w, $h);
}
}
}
else {
print 'Unknown repeat!';
}
imagedestroy($src);
} /* End optimize away creation of duplicates */
//img: image url string
//img_w, img_h: original image size in px
//width, height: box size in pt
//bg_width, bg_height: box size in px
//x, y: left/top edge of box on page in pt
//start_x, start_y: placement of image relativ to pattern
//$repeat: repeat mode
//$bg: GD object of result image
//$src: GD object of original image
//When using cpdf and optimization to direct png creation from gd object is available,
//don't create temp file, but place gd object directly into the pdf
if ( method_exists( $this->_canvas, "get_cpdf" ) &&
method_exists( $this->_canvas->get_cpdf(), "addImagePng" ) ) {
// Note: CPDF_Adapter image converts y position
$this->_canvas->get_cpdf()->addImagePng($filedummy, $x, $this->_canvas->get_height() - $y - $height, $width, $height, $bg);
}
else {
$tmp_file = tempnam(DOMPDF_TEMP_DIR, "bg_dompdf_img_").'.png';
//debugpng
if (DEBUGPNG) print '[_background_image '.$tmp_file.']';
imagepng($bg, $tmp_file);
$this->_canvas->image($tmp_file, "png", $x, $y, $width, $height);
imagedestroy($bg);
//debugpng
if (DEBUGPNG) print '[_background_image unlink '.$tmp_file.']';
if (!DEBUGKEEPTEMP)
unlink($tmp_file);
}
}
protected function _get_dash_pattern($style, $width) {
$pattern = array();
switch ($style) {
default:
/*case "solid":
case "double":
case "groove":
case "inset":
case "outset":
case "ridge":*/
case "none": break;
case "dotted":
if ( $width < 2 )
$pattern = array($width, 2);
else
$pattern = array($width);
break;
case "dashed":
$pattern = array(3 * $width);
break;
}
return $pattern;
}
protected function _border_none($x, $y, $length, $color, $widths, $side, $corner_style = "bevel") {
return;
}
// Border rendering functions
protected function _border_dotted($x, $y, $length, $color, $widths, $side, $corner_style = "bevel") {
list($top, $right, $bottom, $left) = $widths;
$pattern = $this->_get_dash_pattern("dotted", $$side);
switch ($side) {
case "top":
$delta = $top / 2;
case "bottom":
$delta = isset($delta) ? $delta : -$bottom / 2;
$this->_canvas->line($x, $y + $delta, $x + $length, $y + $delta, $color, $$side, $pattern);
break;
case "left":
$delta = $left / 2;
case "right":
$delta = isset($delta) ? $delta : - $right / 2;
$this->_canvas->line($x + $delta, $y, $x + $delta, $y + $length, $color, $$side, $pattern);
break;
default:
return;
}
}
protected function _border_dashed($x, $y, $length, $color, $widths, $side, $corner_style = "bevel") {
list($top, $right, $bottom, $left) = $widths;
$pattern = $this->_get_dash_pattern("dashed", $$side);
switch ($side) {
case "top":
$delta = $top / 2;
case "bottom":
$delta = isset($delta) ? $delta : -$bottom / 2;
$this->_canvas->line($x, $y + $delta, $x + $length, $y + $delta, $color, $$side, $pattern);
break;
case "left":
$delta = $left / 2;
case "right":
$delta = isset($delta) ? $delta : - $right / 2;
$this->_canvas->line($x + $delta, $y, $x + $delta, $y + $length, $color, $$side, $pattern);
break;
default:
return;
}
}
protected function _border_solid($x, $y, $length, $color, $widths, $side, $corner_style = "bevel") {
list($top, $right, $bottom, $left) = $widths;
// All this polygon business is for beveled corners...
switch ($side) {
case "top":
if ( $corner_style === "bevel" ) {
$points = array($x, $y,
$x + $length, $y,
$x + $length - $right, $y + $top,
$x + $left, $y + $top);
$this->_canvas->polygon($points, $color, null, null, true);
} else
$this->_canvas->filled_rectangle($x, $y, $length, $top, $color);
break;
case "bottom":
if ( $corner_style === "bevel" ) {
$points = array($x, $y,
$x + $length, $y,
$x + $length - $right, $y - $bottom,
$x + $left, $y - $bottom);
$this->_canvas->polygon($points, $color, null, null, true);
} else
$this->_canvas->filled_rectangle($x, $y - $bottom, $length, $bottom, $color);
break;
case "left":
if ( $corner_style === "bevel" ) {
$points = array($x, $y,
$x, $y + $length,
$x + $left, $y + $length - $bottom,
$x + $left, $y + $top);
$this->_canvas->polygon($points, $color, null, null, true);
} else
$this->_canvas->filled_rectangle($x, $y, $left, $length, $color);
break;
case "right":
if ( $corner_style === "bevel" ) {
$points = array($x, $y,
$x, $y + $length,
$x - $right, $y + $length - $bottom,
$x - $right, $y + $top);
$this->_canvas->polygon($points, $color, null, null, true);
} else
$this->_canvas->filled_rectangle($x - $right, $y, $right, $length, $color);
break;
default:
return;
}
}
protected function _border_double($x, $y, $length, $color, $widths, $side, $corner_style = "bevel") {
list($top, $right, $bottom, $left) = $widths;
$line_width = $$side / 4;
// We draw the outermost edge first. Points are ordered: outer left,
// outer right, inner right, inner left, or outer top, outer bottom,
// inner bottom, inner top.
switch ($side) {
case "top":
if ( $corner_style === "bevel" ) {
$left_line_width = $left / 4;
$right_line_width = $right / 4;
$points = array($x, $y,
$x + $length, $y,
$x + $length - $right_line_width, $y + $line_width,
$x + $left_line_width, $y + $line_width,);
$this->_canvas->polygon($points, $color, null, null, true);
$points = array($x + $left - $left_line_width, $y + $top - $line_width,
$x + $length - $right + $right_line_width, $y + $top - $line_width,
$x + $length - $right, $y + $top,
$x + $left, $y + $top);
$this->_canvas->polygon($points, $color, null, null, true);
} else {
$this->_canvas->filled_rectangle($x, $y, $length, $line_width, $color);
$this->_canvas->filled_rectangle($x, $y + $top - $line_width, $length, $line_width, $color);
}
break;
case "bottom":
if ( $corner_style === "bevel" ) {
$left_line_width = $left / 4;
$right_line_width = $right / 4;
$points = array($x, $y,
$x + $length, $y,
$x + $length - $right_line_width, $y - $line_width,
$x + $left_line_width, $y - $line_width);
$this->_canvas->polygon($points, $color, null, null, true);
$points = array($x + $left - $left_line_width, $y - $bottom + $line_width,
$x + $length - $right + $right_line_width, $y - $bottom + $line_width,
$x + $length - $right, $y - $bottom,
$x + $left, $y - $bottom);
$this->_canvas->polygon($points, $color, null, null, true);
} else {
$this->_canvas->filled_rectangle($x, $y - $line_width, $length, $line_width, $color);
$this->_canvas->filled_rectangle($x, $y - $bottom, $length, $line_width, $color);
}
break;
case "left":
if ( $corner_style === "bevel" ) {
$top_line_width = $top / 4;
$bottom_line_width = $bottom / 4;
$points = array($x, $y,
$x, $y + $length,
$x + $line_width, $y + $length - $bottom_line_width,
$x + $line_width, $y + $top_line_width);
$this->_canvas->polygon($points, $color, null, null, true);
$points = array($x + $left - $line_width, $y + $top - $top_line_width,
$x + $left - $line_width, $y + $length - $bottom + $bottom_line_width,
$x + $left, $y + $length - $bottom,
$x + $left, $y + $top);
$this->_canvas->polygon($points, $color, null, null, true);
} else {
$this->_canvas->filled_rectangle($x, $y, $line_width, $length, $color);
$this->_canvas->filled_rectangle($x + $left - $line_width, $y, $line_width, $length, $color);
}
break;
case "right":
if ( $corner_style === "bevel" ) {
$top_line_width = $top / 4;
$bottom_line_width = $bottom / 4;
$points = array($x, $y,
$x, $y + $length,
$x - $line_width, $y + $length - $bottom_line_width,
$x - $line_width, $y + $top_line_width);
$this->_canvas->polygon($points, $color, null, null, true);
$points = array($x - $right + $line_width, $y + $top - $top_line_width,
$x - $right + $line_width, $y + $length - $bottom + $bottom_line_width,
$x - $right, $y + $length - $bottom,
$x - $right, $y + $top);
$this->_canvas->polygon($points, $color, null, null, true);
} else {
$this->_canvas->filled_rectangle($x - $line_width, $y, $line_width, $length, $color);
$this->_canvas->filled_rectangle($x - $right, $y, $line_width, $length, $color);
}
break;
default:
return;
}
}
protected function _border_groove($x, $y, $length, $color, $widths, $side, $corner_style = "bevel") {
list($top, $right, $bottom, $left) = $widths;
$half_widths = array($top / 2, $right / 2, $bottom / 2, $left / 2);
$this->_border_inset($x, $y, $length, $color, $half_widths, $side);
switch ($side) {
case "top":
$x += $left / 2;
$y += $top / 2;
$length -= $left / 2 + $right / 2;
break;
case "bottom":
$x += $left / 2;
$y -= $bottom / 2;
$length -= $left / 2 + $right / 2;
break;
case "left":
$x += $left / 2;
$y += $top / 2;
$length -= $top / 2 + $bottom / 2;
break;
case "right":
$x -= $right / 2;
$y += $top / 2;
$length -= $top / 2 + $bottom / 2;
break;
default:
return;
}
$this->_border_outset($x, $y, $length, $color, $half_widths, $side);
}
protected function _border_ridge($x, $y, $length, $color, $widths, $side, $corner_style = "bevel") {
list($top, $right, $bottom, $left) = $widths;
$half_widths = array($top / 2, $right / 2, $bottom / 2, $left / 2);
$this->_border_outset($x, $y, $length, $color, $half_widths, $side);
switch ($side) {
case "top":
$x += $left / 2;
$y += $top / 2;
$length -= $left / 2 + $right / 2;
break;
case "bottom":
$x += $left / 2;
$y -= $bottom / 2;
$length -= $left / 2 + $right / 2;
break;
case "left":
$x += $left / 2;
$y += $top / 2;
$length -= $top / 2 + $bottom / 2;
break;
case "right":
$x -= $right / 2;
$y += $top / 2;
$length -= $top / 2 + $bottom / 2;
break;
default:
return;
}
$this->_border_inset($x, $y, $length, $color, $half_widths, $side);
}
protected function _tint($c) {
if ( !is_numeric($c) )
return $c;
return min(1, $c + 0.16);
}
protected function _shade($c) {
if ( !is_numeric($c) )
return $c;
return max(0, $c - 0.33);
}
protected function _border_inset($x, $y, $length, $color, $widths, $side, $corner_style = "bevel") {
list($top, $right, $bottom, $left) = $widths;
switch ($side) {
case "top":
case "left":
$shade = array_map(array($this, "_shade"), $color);
$this->_border_solid($x, $y, $length, $shade, $widths, $side);
break;
case "bottom":
case "right":
$tint = array_map(array($this, "_tint"), $color);
$this->_border_solid($x, $y, $length, $tint, $widths, $side);
break;
default:
return;
}
}
protected function _border_outset($x, $y, $length, $color, $widths, $side, $corner_style = "bevel") {
list($top, $right, $bottom, $left) = $widths;
switch ($side) {
case "top":
case "left":
$tint = array_map(array($this, "_tint"), $color);
$this->_border_solid($x, $y, $length, $tint, $widths, $side);
break;
case "bottom":
case "right":
$shade = array_map(array($this, "_shade"), $color);
$this->_border_solid($x, $y, $length, $shade, $widths, $side);
break;
default:
return;
}
}
protected function _set_opacity($opacity) {
if ( is_numeric($opacity) && $opacity <= 1.0 && $opacity >= 0.0 ) {
$this->_canvas->set_opacity( $opacity );
}
}
protected function _debug_layout($box, $color = "red", $style = array()) {
$this->_canvas->rectangle($box[0], $box[1], $box[2], $box[3], CSS_Color::parse($color), 0.1, $style);
}
//........................................................................
}

View File

@@ -0,0 +1,481 @@
<?php
/**
* DOMPDF - PHP5 HTML to PDF renderer
*
* File: $RCSfile: attribute_translator.cls.php,v $
* Created on: 2004-09-13
*
* Copyright (c) 2004 - Benj Carson <benjcarson@digitaljunkies.ca>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library in the file LICENSE.LGPL; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*
* Alternatively, you may distribute this software under the terms of the
* PHP License, version 3.0 or later. A copy of this license should have
* been distributed with this file in the file LICENSE.PHP . If this is not
* the case, you can obtain a copy at http://www.php.net/license/3_0.txt.
*
* The latest version of DOMPDF might be available at:
* http://www.dompdf.com/
*
* @link http://www.dompdf.com/
* @copyright 2004 Benj Carson
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @package dompdf
*/
/* $Id: attribute_translator.cls.php 346 2011-01-09 13:23:22Z fabien.menager $ */
/**
* Translates HTML 4.0 attributes into CSS rules
*
* @access private
* @package dompdf
*/
class Attribute_Translator {
// Munged data originally from
// http://www.w3.org/TR/REC-html40/index/attributes.html
//
// thank you var_export() :D
static private $__ATTRIBUTE_LOOKUP = array(
//'caption' => array ( 'align' => '', ),
'img' => array(
'align' => array(
'bottom' => 'vertical-align: baseline;',
'middle' => 'vertical-align: middle;',
'top' => 'vertical-align: top;',
'left' => 'float: left;',
'right' => 'float: right;'
),
'border' => 'border-width: %0.2F px;',
'height' => 'height: %s px;',
'hspace' => 'padding-left: %1$0.2F px; padding-right: %1$0.2F px;',
'vspace' => 'padding-top: %1$0.2F px; padding-bottom: %1$0.2F px;',
'width' => 'width: %s px;',
),
'table' => array(
'align' => array(
'left' => 'margin-left: 0; margin-right: auto;',
'center' => 'margin-left: auto; margin-right: auto;',
'right' => 'margin-left: auto; margin-right: 0;'
),
'bgcolor' => 'background-color: %s;',
'border' => '!set_table_border',
'cellpadding' => '!set_table_cellpadding',
'cellspacing' => 'border-spacing: %0.2F; border-collapse: separate;',
'frame' => array(
'void' => 'border-style: none;',
'above' => 'border-top-style: solid;',
'below' => 'border-bottom-style: solid;',
'hsides' => 'border-left-style: solid; border-right-style: solid;',
'vsides' => 'border-top-style: solid; border-bottom-style: solid;',
'lhs' => 'border-left-style: solid;',
'rhs' => 'border-right-style: solid;',
'box' => 'border-style: solid;',
'border' => 'border-style: solid;'
),
'rules' => '!set_table_rules',
'width' => 'width: %s;',
),
'hr' => array(
'align' => '!set_hr_align', // Need to grab width to set 'left' & 'right' correctly
'noshade' => 'border-style: solid;',
'size' => 'border-width: %0.2F px;',
'width' => 'width: %s;',
),
'div' => array(
'align' => 'text-align: %s;',
),
'h1' => array(
'align' => 'text-align: %s;',
),
'h2' => array(
'align' => 'text-align: %s;',
),
'h3' => array(
'align' => 'text-align: %s;',
),
'h4' => array(
'align' => 'text-align: %s;',
),
'h5' => array(
'align' => 'text-align: %s;',
),
'h6' => array(
'align' => 'text-align: %s;',
),
'p' => array(
'align' => 'text-align: %s;',
),
// 'col' => array(
// 'align' => '',
// 'valign' => '',
// ),
// 'colgroup' => array(
// 'align' => '',
// 'valign' => '',
// ),
'tbody' => array(
'align' => '!set_table_row_align',
'valign' => '!set_table_row_valign',
),
'td' => array(
'align' => 'text-align: %s;',
'bgcolor' => 'background-color: %s;',
'height' => 'height: %s;',
'nowrap' => 'white-space: nowrap;',
'valign' => 'vertical-align: %s;',
'width' => 'width: %s;',
),
'tfoot' => array(
'align' => '!set_table_row_align',
'valign' => '!set_table_row_valign',
),
'th' => array(
'align' => 'text-align: %s;',
'bgcolor' => 'background-color: %s;',
'height' => 'height: %s;',
'nowrap' => 'white-space: nowrap;',
'valign' => 'vertical-align: %s;',
'width' => 'width: %s;',
),
'thead' => array(
'align' => '!set_table_row_align',
'valign' => '!set_table_row_valign',
),
'tr' => array(
'align' => '!set_table_row_align',
'bgcolor' => '!set_table_row_bgcolor',
'valign' => '!set_table_row_valign',
),
'body' => array(
'background' => 'background-image: url(%s);',
'bgcolor' => 'background-color: %s;',
'link' => '!set_body_link',
'text' => 'color: %s;',
),
'br' => array(
'clear' => 'clear: %s;',
),
'basefont' => array(
'color' => 'color: %s;',
'face' => 'font-family: %s;',
'size' => '!set_basefont_size',
),
'font' => array(
'color' => 'color: %s;',
'face' => 'font-family: %s;',
'size' => '!set_font_size',
),
'dir' => array(
'compact' => 'margin: 0.5em 0;',
),
'dl' => array(
'compact' => 'margin: 0.5em 0;',
),
'menu' => array(
'compact' => 'margin: 0.5em 0;',
),
'ol' => array(
'compact' => 'margin: 0.5em 0;',
'start' => 'counter-reset: -dompdf-default-counter %d;',
'type' => 'list-style-type: %s;',
),
'ul' => array(
'compact' => 'margin: 0.5em 0;',
'type' => 'list-style-type: %s;',
),
'li' => array(
'type' => 'list-style-type: %s;',
'value' => 'counter-reset: -dompdf-default-counter %d;',
),
'pre' => array(
'width' => 'width: %s;',
),
);
static protected $_last_basefont_size = 3;
static protected $_font_size_lookup = array(
// For basefont support
-3 => "4pt",
-2 => "5pt",
-1 => "6pt",
0 => "7pt",
1 => "8pt",
2 => "10pt",
3 => "12pt",
4 => "14pt",
5 => "18pt",
6 => "24pt",
7 => "34pt",
// For basefont support
8 => "48pt",
9 => "44pt",
10 => "52pt",
11 => "60pt",
);
static function translate_attributes($frame) {
$node = $frame->get_node();
$tag = $node->tagName;
if ( !isset(self::$__ATTRIBUTE_LOOKUP[$tag]) )
return;
$valid_attrs = self::$__ATTRIBUTE_LOOKUP[$tag];
$attrs = $node->attributes;
$style = rtrim($node->getAttribute("style"), "; ");
if ( $style != "" )
$style .= ";";
foreach ($attrs as $attr => $attr_node ) {
if ( !isset($valid_attrs[$attr]) )
continue;
$value = $attr_node->value;
$target = $valid_attrs[$attr];
// Look up $value in $target, if $target is an array:
if ( is_array($target) ) {
if ( isset($target[$value]) )
$style .= " " . self::_resolve_target($node, $target[$value], $value);
} else {
// otherwise use target directly
$style .= " " . self::_resolve_target($node, $target, $value);
}
}
if ( !is_null($style) ) {
$style = ltrim($style);
$node->setAttribute("style", $style);
}
}
static protected function _resolve_target($node, $target, $value) {
if ( $target[0] === "!" ) {
// Function call
$func = "_" . mb_substr($target, 1);
return self::$func($node, $value);
}
return $value ? sprintf($target, $value) : "";
}
//.....................................................................
static protected function _set_table_cellpadding($node, $value) {
$td_list = $node->getElementsByTagName("td");
foreach ($td_list as $td) {
$style = rtrim($td->getAttribute("style"), ";");
$style .= "; padding: $value" . "px;";
$style = ltrim($style, ";");
$td->setAttribute("style", $style);
}
return null;
}
static protected function _set_table_border($node, $value) {
$td_list = $node->getElementsByTagName("td");
foreach ($td_list as $td) {
$style = $td->getAttribute("style");
if ( strpos($style, "border") !== false )
continue;
$style = rtrim($style, ";");
$style .= "; border-width: " . ($value > 0 ? 1 : 0) . "pt; border-style: inset;";
$style = ltrim($style, ";");
$td->setAttribute("style", $style);
}
$th_list = $node->getElementsByTagName("th");
foreach ($th_list as $th) {
$style = $th->getAttribute("style");
if ( strpos($style, "border") !== false )
continue;
$style = rtrim($style, ";");
$style .= "; border-width: " . ($value > 0 ? 1 : 0) . "pt; border-style: inset;";
$style = ltrim($style, ";");
$th->setAttribute("style", $style);
}
$style = rtrim($node->getAttribute("style"),";");
$style .= "; border-width: $value" . "px; ";
return ltrim($style, "; ");
}
static protected function _set_table_cellspacing($node, $value) {
$style = rtrim($node->getAttribute($style), ";");
if ( $value == 0 )
$style .= "; border-collapse: collapse;";
else
$style = "; border-collapse: separate;";
return ltrim($style, ";");
}
static protected function _set_table_rules($node, $value) {
$new_style = "; border-collapse: collapse;";
switch ($value) {
case "none":
$new_style .= "border-style: none;";
break;
case "groups":
// FIXME: unsupported
return;
case "rows":
$new_style .= "border-style: solid none solid none; border-width: 1px; ";
break;
case "cols":
$new_style .= "border-style: none solid none solid; border-width: 1px; ";
break;
case "all":
$new_style .= "border-style: solid; border-width: 1px; ";
break;
default:
// Invalid value
return null;
}
$td_list = $node->getElementsByTagName("td");
foreach ($td_list as $td) {
$style = $td->getAttribute("style");
$style .= $new_style;
$td->setAttribute("style", $style);
}
return null;
}
static protected function _set_hr_align($node, $value) {
$style = rtrim($node->getAttribute("style"),";");
$width = $node->getAttribute("width");
if ( $width == "" )
$width = "100%";
$remainder = 100 - (double)rtrim($width, "% ");
switch ($value) {
case "left":
$style .= "; margin-right: $remainder %;";
break;
case "right":
$style .= "; margin-left: $remainder %;";
break;
case "center":
$style .= "; margin-left: auto; margin-right: auto;";
break;
default:
return null;
}
return ltrim($style, "; ");
}
static protected function _set_table_row_align($node, $value) {
$td_list = $node->getElementsByTagName("td");
foreach ($td_list as $td) {
$style = rtrim($td->getAttribute("style"), ";");
$style .= "; text-align: $value;";
$style = ltrim($style, "; ");
$td->setAttribute("style", $style);
}
return null;
}
static protected function _set_table_row_valign($node, $value) {
$td_list = $node->getElementsByTagName("td");
foreach ($td_list as $td) {
$style = rtrim($td->getAttribute("style"), ";");
$style .= "; vertical-align: $value;";
$style = ltrim($style, "; ");
$td->setAttribute("style", $style);
}
return null;
}
static protected function _set_table_row_bgcolor($node, $value) {
$td_list = $node->getElementsByTagName("td");
foreach ($td_list as $td) {
$style = rtrim($td->getAttribute("style"), ";");
$style .= "; background-color: $value;";
$style = ltrim($style, "; ");
$td->setAttribute("style", $style);
}
return null;
}
static protected function _set_body_link($node, $value) {
$a_list = $node->getElementsByTagName("a");
foreach ($a_list as $a) {
$style = rtrim($a->getAttribute("style"), ";");
$style .= "; color: $value;";
$style = ltrim($style, "; ");
$a->setAttribute("style", $style);
}
return null;
}
static protected function _set_basefont_size($node, $value) {
// FIXME: ? we don't actually set the font size of anything here, just
// the base size for later modification by <font> tags.
self::$_last_basefont_size = $value;
return null;
}
static protected function _set_font_size($node, $value) {
$style = $node->getAttribute("style");
if ( $value[0] === "-" || $value[0] === "+" )
$value = self::$_last_basefont_size + (int)$value;
if ( isset(self::$_font_size_lookup[$value]) )
$style .= "; font-size: " . self::$_font_size_lookup[$value] . ";";
else
$style .= "; font-size: $value;";
return ltrim($style, "; ");
}
}

View File

@@ -0,0 +1,354 @@
<?php
/**
* DOMPDF - PHP5 HTML to PDF renderer
*
* File: $RCSfile: block_frame_decorator.cls.php,v $
* Created on: 2004-06-02
*
* Copyright (c) 2004 - Benj Carson <benjcarson@digitaljunkies.ca>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library in the file LICENSE.LGPL; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*
* Alternatively, you may distribute this software under the terms of the
* PHP License, version 3.0 or later. A copy of this license should have
* been distributed with this file in the file LICENSE.PHP . If this is not
* the case, you can obtain a copy at http://www.php.net/license/3_0.txt.
*
* The latest version of DOMPDF might be available at:
* http://www.dompdf.com/
*
* @link http://www.dompdf.com/
* @copyright 2004 Benj Carson
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @package dompdf
*/
/* $Id: block_frame_decorator.cls.php 358 2011-01-30 22:22:47Z fabien.menager $ */
/**
* Decorates frames for block layout
*
* @access private
* @package dompdf
*/
class Block_Frame_Decorator extends Frame_Decorator {
const DEFAULT_COUNTER = "-dompdf-default-counter";
protected $_lines; // array( [num] => array([frames] => array(<frame list>),
// y, w, h) )
protected $_counters; // array([id] => counter_value) (for generated content)
protected $_cl; // current line index
static protected $_initial_line_state = array(
"frames" => array(),
"wc" => 0,
"y" => null,
"w" => 0,
"h" => 0,
"left" => 0,
"right" => 0,
"tallest_frame" => null,
"br" => false,
);
//........................................................................
function __construct(Frame $frame, DOMPDF $dompdf) {
parent::__construct($frame, $dompdf);
$this->_lines = array(self::$_initial_line_state);
$this->_counters = array(self::DEFAULT_COUNTER => 0);
$this->_cl = 0;
}
//........................................................................
function reset() {
parent::reset();
$this->_lines = array(self::$_initial_line_state);
$this->_counters = array(self::DEFAULT_COUNTER => 0);
$this->_cl = 0;
}
//........................................................................
// Accessor methods
function get_current_line($i = null) {
$cl = $this->_lines[$this->_cl];
if ( isset($i) )
return $cl[$i];
return $cl;
}
function get_current_line_number() {
return $this->_cl;
}
function get_lines() { return $this->_lines; }
//........................................................................
// Set methods
function set_current_line($y = null, $w = null, $h = null, $tallest_frame = null, $left = null, $right = null) {
$this->set_line($this->_cl, $y, $w, $h, $tallest_frame, $left, $right);
}
function clear_line($i) {
if ( isset($this->_lines[$i]) )
unset($this->_lines[$i]);
}
function set_line($lineno, $y = null, $w = null, $h = null, $tallest_frame = null, $left = null, $right = null) {
if ( is_array($y) )
extract($y);
if (is_numeric($y))
$this->_lines[$lineno]["y"] = $y;
if (is_numeric($w))
$this->_lines[$lineno]["w"] = $w;
if (is_numeric($h))
$this->_lines[$lineno]["h"] = $h;
if ($tallest_frame && $tallest_frame instanceof Frame)
$this->_lines[$lineno]["tallest_frame"] = $tallest_frame;
if (is_numeric($left))
$this->_lines[$lineno]["left"] = $left;
if (is_numeric($right))
$this->_lines[$lineno]["right"] = $right;
}
function add_frame_to_line(Frame $frame) {
$style = $frame->get_style();
if ( in_array($style->position, array("absolute", "fixed")) ||
(DOMPDF_ENABLE_CSS_FLOAT && $style->float !== "none") ) {
return;
}
$frame->set_containing_line($this->_lines[$this->_cl]);
/*
// Adds a new line after a block, only if certain conditions are met
if ((($frame instanceof Inline_Frame_Decorator && $frame->get_node()->nodeName !== "br") ||
$frame instanceof Text_Frame_Decorator && trim($frame->get_text())) &&
($frame->get_prev_sibling() && $frame->get_prev_sibling()->get_style()->display === "block" &&
$this->_lines[$this->_cl]["w"] > 0 )) {
$this->maximize_line_height( $style->length_in_pt($style->line_height), $frame );
$this->add_line();
// Add each child of the inline frame to the line individually
foreach ($frame->get_children() as $child)
$this->add_frame_to_line( $child );
}
else*/
// Handle inline frames (which are effectively wrappers)
if ( $frame instanceof Inline_Frame_Decorator ) {
// Handle line breaks
if ( $frame->get_node()->nodeName === "br" ) {
$this->maximize_line_height( $style->length_in_pt($style->line_height), $frame );
$this->add_line(true);
}
return;
}
// Trim leading text if this is an empty line. Kinda a hack to put it here,
// but what can you do...
if ( $this->_lines[$this->_cl]["w"] == 0 &&
$frame->get_node()->nodeName === "#text" &&
($style->white_space !== "pre" ||
$style->white_space !== "pre-wrap") ) {
$frame->set_text( ltrim($frame->get_text()) );
$frame->recalculate_width();
}
$w = $frame->get_margin_width();
if ( $w == 0 )
return;
// Debugging code:
/*
pre_r("\n<h3>Adding frame to line:</h3>");
// pre_r("Me: " . $this->get_node()->nodeName . " (" . spl_object_hash($this->get_node()) . ")");
// pre_r("Node: " . $frame->get_node()->nodeName . " (" . spl_object_hash($frame->get_node()) . ")");
if ( $frame->get_node()->nodeName === "#text" )
pre_r('"'.$frame->get_node()->nodeValue.'"');
pre_r("Line width: " . $this->_lines[$this->_cl]["w"]);
pre_r("Frame: " . get_class($frame));
pre_r("Frame width: " . $w);
pre_r("Frame height: " . $frame->get_margin_height());
pre_r("Containing block width: " . $this->get_containing_block("w"));
*/
// End debugging
$line = $this->_lines[$this->_cl];
if ( $line["left"] + $line["w"] + $line["right"] + $w > $this->get_containing_block("w"))
$this->add_line();
$frame->position();
$current_line = &$this->_lines[$this->_cl];
$current_line["frames"][] = $frame;
if ( $frame->get_node()->nodeName === "#text")
$current_line["wc"] += count(preg_split("/\s+/", trim($frame->get_text())));
$this->increase_line_width($w);
$this->maximize_line_height($frame->get_margin_height(), $frame);
}
function remove_frames_from_line(Frame $frame) {
// Search backwards through the lines for $frame
$i = $this->_cl;
while ($i >= 0) {
if ( ($j = in_array($frame, $this->_lines[$i]["frames"], true)) !== false )
break;
$i--;
}
if ( $j === false )
return;
// Remove $frame and all frames that follow
while ($j < count($this->_lines[$i]["frames"])) {
$f = $this->_lines[$i]["frames"][$j];
$this->_lines[$i]["frames"][$j] = null;
unset($this->_lines[$i]["frames"][$j]);
$j++;
$this->_lines[$i]["w"] -= $f->get_margin_width();
}
// Recalculate the height of the line
$h = 0;
foreach ($this->_lines[$i]["frames"] as $f)
$h = max( $h, $f->get_margin_height() );
$this->_lines[$i]["h"] = $h;
// Remove all lines that follow
while ($this->_cl > $i) {
$this->_lines[ $this->_cl ] = null;
unset($this->_lines[ $this->_cl ]);
$this->_cl--;
}
}
function increase_line_width($w) {
$this->_lines[ $this->_cl ]["w"] += $w;
}
function maximize_line_height($val, Frame $frame) {
if ( $val > $this->_lines[ $this->_cl ]["h"] ) {
$this->_lines[ $this->_cl ]["tallest_frame"] = $frame;
$this->_lines[ $this->_cl ]["h"] = $val;
}
}
function add_line($br = false) {
// if ( $this->_lines[$this->_cl]["h"] == 0 ) //count($this->_lines[$i]["frames"]) == 0 ||
// return;
$this->_lines[$this->_cl]["br"] = $br;
$y = $this->_lines[$this->_cl]["y"] + $this->_lines[$this->_cl]["h"];
$new_line = self::$_initial_line_state;
$new_line["y"] = $y;
$this->_lines[ ++$this->_cl ] = $new_line;
}
//........................................................................
function reset_counter($id = self::DEFAULT_COUNTER, $value = 0) {
$this->_counters[$id] = $value;
}
function increment_counter($id = self::DEFAULT_COUNTER, $increment = 1) {
if ( !isset($this->_counters[$id]) )
$this->_counters[$id] = $increment;
else
$this->_counters[$id] += $increment;
}
// TODO: What version is the best : this one or the one in List_Bullet_Renderer ?
function counter_value($id = self::DEFAULT_COUNTER, $type = "decimal") {
$type = mb_strtolower($type);
if ( $id === "page" ) {
$value = $this->get_dompdf()->get_canvas()->get_page_number();
}
elseif ( !isset($this->_counters[$id]) ) {
$this->_counters[$id] = 0;
$value = 0;
}
else {
$value = $this->_counters[$id];
}
switch ($type) {
default:
case "decimal":
return $value;
case "decimal-leading-zero":
return str_pad($value, 2, "0");
case "lower-roman":
return dec2roman($value);
case "upper-roman":
return mb_strtoupper(dec2roman($value));
case "lower-latin":
case "lower-alpha":
return chr( ($value % 26) + ord('a') - 1);
case "upper-latin":
case "upper-alpha":
return chr( ($value % 26) + ord('A') - 1);
case "lower-greek":
return unichr($value + 944);
case "upper-greek":
return unichr($value + 912);
}
}
}

View File

@@ -0,0 +1,756 @@
<?php
/**
* DOMPDF - PHP5 HTML to PDF renderer
*
* File: $RCSfile: block_frame_reflower.cls.php,v $
* Created on: 2004-06-17
*
* Copyright (c) 2004 - Benj Carson <benjcarson@digitaljunkies.ca>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library in the file LICENSE.LGPL; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*
* Alternatively, you may distribute this software under the terms of the
* PHP License, version 3.0 or later. A copy of this license should have
* been distributed with this file in the file LICENSE.PHP . If this is not
* the case, you can obtain a copy at http://www.php.net/license/3_0.txt.
*
* The latest version of DOMPDF might be available at:
* http://www.dompdf.com/
*
* @link http://www.dompdf.com/
* @copyright 2004 Benj Carson
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @package dompdf
*/
/* $Id: block_frame_reflower.cls.php 358 2011-01-30 22:22:47Z fabien.menager $ */
/**
* Reflows block frames
*
* @access private
* @package dompdf
*/
class Block_Frame_Reflower extends Frame_Reflower {
// Minimum line width to justify, as fraction of available width
const MIN_JUSTIFY_WIDTH = 0.80;
/**
* @var Block_Frame_Decorator
*/
protected $_frame;
function __construct(Block_Frame_Decorator $frame) { parent::__construct($frame); }
/**
* Calculate the ideal used value for the width property as per:
* http://www.w3.org/TR/CSS21/visudet.html#Computing_widths_and_margins
*
* @param float $width
* @return array
*/
protected function _calculate_width($width) {
$style = $this->_frame->get_style();
$w = $this->_frame->get_containing_block("w");
if( $style->position === "fixed" )
$w = $this->_frame->get_parent()->get_containing_block("w");
$rm = $style->length_in_pt($style->margin_right, $w);
$lm = $style->length_in_pt($style->margin_left, $w);
$left = $style->length_in_pt($style->left, $w);
$right = $style->length_in_pt($style->right, $w);
// Handle 'auto' values
$dims = array($style->border_left_width,
$style->border_right_width,
$style->padding_left,
$style->padding_right,
$width !== "auto" ? $width : 0,
$rm !== "auto" ? $rm : 0,
$lm !== "auto" ? $lm : 0);
// absolutely positioned boxes take the 'left' and 'right' properties into account
if ( $style->position === "absolute" || $style->position === "fixed" ) {
$absolute = true;
$dims[] = $left !== "auto" ? $left : 0;
$dims[] = $right !== "auto" ? $right : 0;
} else {
$absolute = false;
}
$sum = $style->length_in_pt($dims, $w);
// Compare to the containing block
$diff = $w - $sum;
if ( $diff > 0 ) {
if ( $absolute ) {
// resolve auto properties: see
// http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-width
if ( $width === "auto" && $left === "auto" && $right === "auto" ) {
if ( $lm === "auto" )
$lm = 0;
if ( $rm === "auto" )
$rm = 0;
// Technically, the width should be "shrink-to-fit" i.e. based on the
// preferred width of the content... a little too costly here as a
// special case. Just get the width to take up the slack:
$left = 0;
$right = 0;
$width = $diff;
} else if ( $width === "auto" ) {
if ( $lm === "auto" )
$lm = 0;
if ( $rm === "auto" )
$rm = 0;
if ( $left === "auto" )
$left = 0;
if ( $right === "auto" )
$right = 0;
$width = $diff;
} else if ( $left === "auto" ) {
if ( $lm === "auto" )
$lm = 0;
if ( $rm === "auto" )
$rm = 0;
if ( $right === "auto" )
$right = 0;
$left = $diff;
} else if ( $right === "auto" ) {
if ( $lm === "auto" )
$lm = 0;
if ( $rm === "auto" )
$rm = 0;
$right = $diff;
}
} else {
// Find auto properties and get them to take up the slack
if ( $width === "auto" )
$width = $diff;
else if ( $lm === "auto" && $rm === "auto" )
$lm = $rm = round($diff / 2);
else if ( $lm === "auto" )
$lm = $diff;
else if ( $rm === "auto" )
$rm = $diff;
}
} else if ($diff < 0) {
// We are over constrained--set margin-right to the difference
$rm = $diff;
}
return array("width"=> $width, "margin_left" => $lm, "margin_right" => $rm, "left" => $left, "right" => $right);
}
/**
* Call the above function, but resolve max/min widths
* @return array
*/
protected function _calculate_restricted_width() {
$frame = $this->_frame;
$style = $frame->get_style();
$cb = $frame->get_containing_block();
if ( $style->position === "fixed" )
$cb = $frame->get_root()->get_containing_block();
//if ( $style->position === "absolute" )
// $cb = $frame->find_positionned_parent()->get_containing_block();
if ( !isset($cb["w"]) )
throw new DOMPDF_Exception("Box property calculation requires containing block width");
// Treat width 100% as auto
if ( $style->width === "100%" ) {
$width = "auto";
}
else {
$width = $style->length_in_pt($style->width, $cb["w"]);
}
extract($this->_calculate_width($width));
// Handle min/max width
$min_width = $style->length_in_pt($style->min_width, $cb["w"]);
$max_width = $style->length_in_pt($style->max_width, $cb["w"]);
if ( $max_width !== "none" && $min_width > $max_width)
// Swap 'em
list($max_width, $min_width) = array($min_width, $max_width);
if ( $max_width !== "none" && $width > $max_width )
extract($this->_calculate_width($max_width));
if ( $width < $min_width )
extract($this->_calculate_width($min_width));
return array($width, $margin_left, $margin_right, $left, $right);
}
/**
* Determine the unrestricted height of content within the block
* by adding each line's height
* @return float
*/
protected function _calculate_content_height() {
$height = 0;
foreach ($this->_frame->get_lines() as $line) {
$height += $line["h"];
}
return $height;
}
/**
* Determine the frame's restricted height
* @return array
*/
protected function _calculate_restricted_height() {
$style = $this->_frame->get_style();
$content_height = $this->_calculate_content_height();
$cb = $this->_frame->get_containing_block();
$height = $style->length_in_pt($style->height, $cb["h"]);
$top = $style->length_in_pt($style->top, $cb["h"]);
$bottom = $style->length_in_pt($style->bottom, $cb["h"]);
$margin_top = $style->length_in_pt($style->margin_top, $cb["h"]);
$margin_bottom = $style->length_in_pt($style->margin_bottom, $cb["h"]);
if ( $style->position === "absolute" || $style->position === "fixed" ) {
// see http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-height
$dims = array($top !== "auto" ? $top : 0,
$style->margin_top !== "auto" ? $style->margin_top : 0,
$style->padding_top,
$style->border_top_width,
$height !== "auto" ? $height : 0,
$style->border_bottom_width,
$style->padding_bottom,
$style->margin_bottom !== "auto" ? $style->margin_bottom : 0,
$bottom !== "auto" ? $bottom : 0);
$sum = $style->length_in_pt($dims, $cb["h"]);
$diff = $cb["h"] - $sum;
if ( $diff > 0 ) {
if ( $height === "auto" && $top === "auto" && $bottom === "auto" ) {
if ( $margin_top === "auto" )
$margin_top = 0;
if ( $margin_bottom === "auto" )
$margin_bottom = 0;
$height = $diff;
} else if ( $height === "auto" && $top === "auto" ) {
if ( $margin_top === "auto" )
$margin_top = 0;
if ( $margin_bottom === "auto" )
$margin_bottom = 0;
$height = $content_height;
$top = $diff - $content_height;
} else if ( $height === "auto" && $bottom === "auto" ) {
if ( $margin_top === "auto" )
$margin_top = 0;
if ( $margin_bottom === "auto" )
$margin_bottom = 0;
$height = $content_height;
$bottom = $diff - $content_height;
} else if ( $top === "auto" && $bottom === "auto" ) {
if ( $margin_top === "auto" )
$margin_top = 0;
if ( $margin_bottom === "auto" )
$margin_bottom = 0;
$bottom = $diff;
} else if ( $top === "auto" ) {
if ( $margin_top === "auto" )
$margin_top = 0;
if ( $margin_bottom === "auto" )
$margin_bottom = 0;
$top = $diff;
} else if ( $height === "auto" ) {
if ( $margin_top === "auto" )
$margin_top = 0;
if ( $margin_bottom === "auto" )
$margin_bottom = 0;
$height = $diff;
} else if ( $bottom === "auto" ) {
if ( $margin_top === "auto" )
$margin_top = 0;
if ( $margin_bottom === "auto" )
$margin_bottom = 0;
$bottom = $diff;
} else {
if ( $style->overflow === "visible" ) {
// set all autos to zero
if ( $margin_top === "auto" )
$margin_top = 0;
if ( $margin_bottom === "auto" )
$margin_bottom = 0;
if ( $top === "auto" )
$top = 0;
if ( $bottom === "auto" )
$bottom = 0;
if ( $height === "auto" )
$height = $content_height;
}
// FIXME: overflow hidden
}
}
} else {
// Expand the height if overflow is visible
if ( $height === "auto" && $content_height > $height /* && $style->overflow === "visible" */)
$height = $content_height;
// FIXME: this should probably be moved to a seperate function as per
// _calculate_restricted_width
// Only handle min/max height if the height is independent of the frame's content
if ( !($style->overflow === "visible" ||
($style->overflow === "hidden" && $height === "auto")) ) {
$min_height = $style->min_height;
$max_height = $style->max_height;
if ( isset($cb["h"]) ) {
$min_height = $style->length_in_pt($min_height, $cb["h"]);
$max_height = $style->length_in_pt($max_height, $cb["h"]);
} else if ( isset($cb["w"]) ) {
if ( mb_strpos($min_height, "%") !== false )
$min_height = 0;
else
$min_height = $style->length_in_pt($min_height, $cb["w"]);
if ( mb_strpos($max_height, "%") !== false )
$max_height = "none";
else
$max_height = $style->length_in_pt($max_height, $cb["w"]);
}
if ( $max_height !== "none" && $min_height > $max_height )
// Swap 'em
list($max_height, $min_height) = array($min_height, $max_height);
if ( $max_height !== "none" && $height > $max_height )
$height = $max_height;
if ( $height < $min_height )
$height = $min_height;
}
}
return array($height, $margin_top, $margin_bottom, $top, $bottom);
}
/**
* Adjust the justification of each of our lines.
* http://www.w3.org/TR/CSS21/text.html#propdef-text-align
*/
protected function _text_align() {
$style = $this->_frame->get_style();
$w = $this->_frame->get_containing_block("w");
$width = $style->length_in_pt($style->width, $w);
switch ($style->text_align) {
default:
case "left":
foreach ($this->_frame->get_lines() as $line) {
if ( !$line["left"] ) continue;
foreach($line["frames"] as $frame) {
if ( $frame instanceof Block_Frame_Decorator) continue;
$frame->set_position( $frame->get_position("x") + $line["left"] );
}
}
return;
case "right":
foreach ($this->_frame->get_lines() as $line) {
// Move each child over by $dx
$dx = $width - $line["w"] - $line["right"];
foreach($line["frames"] as $frame) {
// Block frames are not aligned by text-align
if ($frame instanceof Block_Frame_Decorator) continue;
$frame->set_position( $frame->get_position("x") + $dx );
}
}
break;
case "justify":
// We justify all lines except the last one
$lines = $this->_frame->get_lines(); // needs to be a variable (strict standards)
$lines = array_splice($lines, 0, -1);
foreach($lines as $i => $line) {
if ( $line["br"] ) {
unset($lines[$i]);
}
}
// One space character's width. Will be used to get a more accurate spacing
$space_width = Font_Metrics::get_text_width(" ", $style->font_family, $style->font_size);
foreach ($lines as $i => $line) {
if ( $line["left"] ) {
foreach($line["frames"] as $frame) {
if ( !$frame instanceof Text_Frame_Decorator )
continue;
$frame->set_position( $frame->get_position("x") + $line["left"] );
}
}
// Only set the spacing if the line is long enough. This is really
// just an aesthetic choice ;)
//if ( $line["left"] + $line["w"] + $line["right"] > self::MIN_JUSTIFY_WIDTH * $width ) {
// Set the spacing for each child
if ( $line["wc"] > 1 )
$spacing = ($width - ($line["left"] + $line["w"] + $line["right"]) + $space_width) / ($line["wc"] - 1);
else
$spacing = 0;
$dx = 0;
foreach($line["frames"] as $frame) {
if ( !$frame instanceof Text_Frame_Decorator )
continue;
$text = $frame->get_text();
$spaces = mb_substr_count($text, " ");
$char_spacing = $style->length_in_pt($style->letter_spacing);
$_spacing = $spacing + $char_spacing;
$frame->set_position( $frame->get_position("x") + $dx );
$frame->set_text_spacing($_spacing);
$dx += $spaces * $_spacing;
}
// The line (should) now occupy the entire width
$this->_frame->set_line($i, null, $width);
//}
}
break;
case "center":
case "centre":
foreach ($this->_frame->get_lines() as $line) {
// Centre each line by moving each frame in the line by:
$dx = ($width + $line["left"] - $line["w"] - $line["right"] ) / 2;
foreach ($line["frames"] as $frame) {
// Block frames are not aligned by text-align
if ($frame instanceof Block_Frame_Decorator) continue;
$frame->set_position( $frame->get_position("x") + $dx );
}
}
break;
}
}
/**
* Align inline children vertically.
* Aligns each child vertically after each line is reflowed
*/
function vertical_align() {
foreach ( $this->_frame->get_lines() as $i => $line ) {
$height = $line["h"];
foreach ( $line["frames"] as $frame ) {
$style = $frame->get_style();
if ( $style->display !== "inline" && $style->display !== "text" )
continue;
// FIXME?
if ( $this instanceof Table_Cell_Frame_Reflower )
$align = $frame->get_frame()->get_style()->vertical_align;
else
$align = $frame->get_frame()->get_parent()->get_style()->vertical_align;
$frame_h = $frame->get_margin_height();
$y = $line["y"];
switch ($align) {
case "baseline":
$y += $height - $frame_h;
break;
case "middle":
$y += ($height + $frame_h) / 2;
break;
case "sub":
$y += 0.2 * $height;
break;
case "super":
$y += -0.3 * $height;
break;
case "text-top":
case "top": // Not strictly accurate, but good enough for now
break;
case "text-bottom":
case "bottom":
$y += $height - $frame_h;
break;
}
$x = $frame->get_position("x");
$frame->set_position($x, $y);
}
}
}
function reflow(Frame_Decorator $block = null) {
// Check if a page break is forced
$page = $this->_frame->get_root();
$page->check_forced_page_break($this->_frame);
// Bail if the page is full
if ( $page->is_full() )
return;
// Generated content
$this->_set_content();
// Collapse margins if required
$this->_collapse_margins();
$style = $this->_frame->get_style();
$cb = $this->_frame->get_containing_block();
if ( $style->counter_increment && ($increment = $style->counter_increment) !== "none" )
$this->_frame->increment_counter($increment);
if ( $style->position === "fixed" )
$cb = $this->_frame->get_root()->get_containing_block();
// Determine the constraints imposed by this frame: calculate the width
// of the content area:
list($w, $left_margin, $right_margin, $left, $right) = $this->_calculate_restricted_width();
// Store the calculated properties
$style->width = $w . "pt";
$style->margin_left = $left_margin."pt";
$style->margin_right = $right_margin."pt";
$style->left = $left ."pt";
$style->right = $right . "pt";
// Update the position
$this->_frame->position();
list($x, $y) = $this->_frame->get_position();
// Adjust the first line based on the text-indent property
$indent = $style->length_in_pt($style->text_indent, $cb["w"]);
$this->_frame->increase_line_width($indent);
// Determine the content edge
$top = $style->length_in_pt(array($style->margin_top,
$style->padding_top,
$style->border_top_width), $cb["h"]);
$bottom = $style->length_in_pt(array($style->border_bottom_width,
$style->margin_bottom,
$style->padding_bottom), $cb["h"]);
$cb_x = $x + $left_margin + $style->length_in_pt(array($style->border_left_width,
$style->padding_left), $cb["w"]);
$cb_y = $y + $top;
$cb_h = ($cb["h"] + $cb["y"]) - $bottom - $cb_y;
// Set the y position of the first line in this block
$this->_frame->set_current_line($cb_y);
$floating_children = array();
// Set the containing blocks and reflow each child
foreach ( $this->_frame->get_children() as $child ) {
// Bail out if the page is full
if ( $page->is_full() )
break;
// Floating siblings
if ( DOMPDF_ENABLE_CSS_FLOAT && count($floating_children) ) {
$offset_left = 0;
$offset_right = 0;
// We need to reflow the child to know its initial x position
$child->set_containing_block($cb_x, $cb_y, $w, $cb_h);
$child->reflow($this->_frame);
$current_line = $this->_frame->get_current_line();
foreach ( $floating_children as $child_key => $floating_child ) {
$float = $floating_child->get_style()->float;
$floating_width = $floating_child->get_margin_width();
$floating_x = $floating_child->get_position("x");
if ( $float === "left" ) {
if ($current_line["left"] + $child->get_position("x") > $floating_x + $floating_width) continue;
}
else {
if ($current_line["left"] + $child->get_position("x") + $child->get_margin_width() < $w - $floating_width - $current_line["right"]) continue;
}
// If the child is still shifted by the floating element
if ( $floating_child->get_position("y") + $floating_child->get_margin_height() > $current_line["y"] ) {
if ( $float === "left" )
$offset_left += $floating_width;
else
$offset_right += $floating_width;
}
// else, the floating element won't shift anymore
else {
unset($floating_children[$child_key]);
}
}
if ( $offset_left )
$this->_frame->set_current_line(array("left" => $offset_left));
if ( $offset_right )
$this->_frame->set_current_line(array("right" => $offset_right));
}
$child->set_containing_block($cb_x, $cb_y, $w, $cb_h);
$child->reflow($this->_frame);
// Don't add the child to the line if a page break has occurred
if ( $page->check_page_break($child) )
break;
$child_style = $child->get_style();
if ( DOMPDF_ENABLE_CSS_FLOAT && $child_style->float !== "none") {
$floating_children[] = $child;
// Remove next frame's beginning whitespace
$next = $child->get_next_sibling();
if ( $next && $next instanceof Text_Frame_Decorator) {
$next->set_text(ltrim($next->get_text()));
}
$float_x = $cb_x;
$float_y = $this->_frame->get_current_line("y");
$child_style = $child->get_style();
switch( $child_style->float ) {
case "left": break;
case "right":
$width = $w;
$float_x += ($width - $child->get_margin_width());
break;
}
$child->set_position($float_x, $float_y);
}
}
// Determine our height
list($height, $margin_top, $margin_bottom, $top, $bottom) = $this->_calculate_restricted_height();
$style->height = $height;
$style->margin_top = $margin_top;
$style->margin_bottom = $margin_bottom;
$style->top = $top;
$style->bottom = $bottom;
$this->_text_align();
$this->vertical_align();
if ( $block ) {
$block->add_frame_to_line($this->_frame);
}
}
}

View File

@@ -0,0 +1,86 @@
<?php
/**
* DOMPDF - PHP5 HTML to PDF renderer
*
* File: $RCSfile: block_positioner.cls.php,v $
* Created on: 2004-06-08
*
* Copyright (c) 2004 - Benj Carson <benjcarson@digitaljunkies.ca>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library in the file LICENSE.LGPL; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*
* Alternatively, you may distribute this software under the terms of the
* PHP License, version 3.0 or later. A copy of this license should have
* been distributed with this file in the file LICENSE.PHP . If this is not
* the case, you can obtain a copy at http://www.php.net/license/3_0.txt.
*
* The latest version of DOMPDF might be available at:
* http://www.dompdf.com/
*
* @link http://www.dompdf.com/
* @copyright 2004 Benj Carson
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @package dompdf
*/
/* $Id: block_positioner.cls.php 356 2011-01-28 08:56:10Z fabien.menager $ */
/**
* Positions block frames
*
* @access private
* @package dompdf
*/
class Block_Positioner extends Positioner {
function __construct(Frame_Decorator $frame) { parent::__construct($frame); }
//........................................................................
function position() {
$frame = $this->_frame;
$style = $frame->get_style();
$cb = $frame->get_containing_block();
$p = $frame->find_block_parent();
if ( $p ) {
$float = $style->float;
if ( !DOMPDF_ENABLE_CSS_FLOAT || !$float || $float === "none" ) {
$p->add_line();
}
$y = $p->get_current_line("y");
} else
$y = $cb["y"];
$x = $cb["x"];
// Relative positionning
if ( $style->position === "relative" ) {
$top = $style->length_in_pt($style->top, $cb["h"]);
//$right = $style->length_in_pt($style->right, $cb["w"]);
//$bottom = $style->length_in_pt($style->bottom, $cb["h"]);
$left = $style->length_in_pt($style->left, $cb["w"]);
$x += $left;
$y += $top;
}
$frame->set_position($x, $y);
}
}

View File

@@ -0,0 +1,211 @@
<?php
/**
* DOMPDF - PHP5 HTML to PDF renderer
*
* File: $RCSfile: block_renderer.cls.php,v $
* Created on: 2004-06-03
*
* Copyright (c) 2004 - Benj Carson <benjcarson@digitaljunkies.ca>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library in the file LICENSE.LGPL; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*
* Alternatively, you may distribute this software under the terms of the
* PHP License, version 3.0 or later. A copy of this license should have
* been distributed with this file in the file LICENSE.PHP . If this is not
* the case, you can obtain a copy at http://www.php.net/license/3_0.txt.
*
* The latest version of DOMPDF might be available at:
* http://www.dompdf.com/
*
* @link http://www.dompdf.com/
* @copyright 2004 Benj Carson
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @package dompdf
*/
/* $Id: block_renderer.cls.php 346 2011-01-09 13:23:22Z fabien.menager $ */
/**
* Renders block frames
*
* @access private
* @package dompdf
*/
class Block_Renderer extends Abstract_Renderer {
//........................................................................
function render(Frame $frame) {
$style = $frame->get_style();
list($x, $y, $w, $h) = $frame->get_padding_box();
$this->_set_opacity( $frame->get_opacity( $style->opacity ) );
// Draw our background, border and content
if ( ($bg = $style->background_color) !== "transparent" ) {
$this->_canvas->filled_rectangle( $x, $y, $w, $h, $bg );
}
if ( ($url = $style->background_image) && $url !== "none" )
$this->_background_image($url, $x, $y, $w, $h, $style);
$this->_render_border($frame);
$this->_render_outline($frame);
if (DEBUG_LAYOUT && DEBUG_LAYOUT_BLOCKS) {
$this->_debug_layout($frame->get_border_box(), "red");
if (DEBUG_LAYOUT_PADDINGBOX) {
$this->_debug_layout($frame->get_padding_box(), "red", array(0.5, 0.5));
}
}
if (DEBUG_LAYOUT && DEBUG_LAYOUT_LINES && $frame->get_decorator()) {
foreach ($frame->get_decorator()->get_lines() as $line) {
$frame->_debug_layout(array($line["x"], $line["y"], $line["w"], $line["h"]), "orange");
}
}
}
protected function _render_border(Frame_Decorator $frame, $corner_style = "bevel") {
$style = $frame->get_style();
$bbox = $frame->get_border_box();
$bp = $style->get_border_properties();
// If all the borders are "solid" with the same color and style, we'd better draw a rectangle
if (
in_array($bp["top"]["style"], array("solid", "dashed", "dotted")) &&
$bp["top"] == $bp["right"] &&
$bp["right"] == $bp["bottom"] &&
$bp["bottom"] == $bp["left"]
) {
$props = $bp["top"];
if ( $props["color"] === "transparent" || $props["width"] <= 0 ) return;
list($x, $y, $w, $h) = $bbox;
$width = $style->length_in_pt($props["width"]);
$pattern = $this->_get_dash_pattern($props["style"], $width);
$this->_canvas->rectangle($x + $width / 2, $y + $width / 2, $w - $width, $h - $width, $props["color"], $width, $pattern);
return;
}
$widths = array($style->length_in_pt($bp["top"]["width"]),
$style->length_in_pt($bp["right"]["width"]),
$style->length_in_pt($bp["bottom"]["width"]),
$style->length_in_pt($bp["left"]["width"]));
foreach ($bp as $side => $props) {
list($x, $y, $w, $h) = $bbox;
if ( !$props["style"] ||
$props["style"] === "none" ||
$props["width"] <= 0 ||
$props["color"] == "transparent" )
continue;
switch($side) {
case "top":
$length = $w;
break;
case "bottom":
$length = $w;
$y += $h;
break;
case "left":
$length = $h;
break;
case "right":
$length = $h;
$x += $w;
break;
default:
break;
}
$method = "_border_" . $props["style"];
$this->$method($x, $y, $length, $props["color"], $widths, $side, $corner_style);
}
}
protected function _render_outline(Frame_Decorator $frame, $corner_style = "bevel") {
$style = $frame->get_style();
$props = array(
"width" => $style->outline_width,
"style" => $style->outline_style,
"color" => $style->outline_color,
);
if ( !$props["style"] || $props["style"] === "none" || $props["width"] <= 0 )
return;
$bbox = $frame->get_border_box();
$offset = $style->length_in_pt($props["width"]);
$pattern = $this->_get_dash_pattern($props["style"], $offset);
// If the outline style is "solid" we'd better draw a rectangle
if ( in_array($props["style"], array("solid", "dashed", "dotted")) ) {
$bbox[0] -= $offset / 2;
$bbox[1] -= $offset / 2;
$bbox[2] += $offset;
$bbox[3] += $offset;
list($x, $y, $w, $h) = $bbox;
$this->_canvas->rectangle($x, $y, $w, $h, $props["color"], $offset, $pattern);
return;
}
$bbox[0] -= $offset;
$bbox[1] -= $offset;
$bbox[2] += $offset * 2;
$bbox[3] += $offset * 2;
$method = "_border_" . $props["style"];
$widths = array_fill(0, 4, $props["width"]);
$sides = array("top", "right", "left", "bottom");
foreach ($sides as $side) {
list($x, $y, $w, $h) = $bbox;
switch($side) {
case "top":
$length = $w;
break;
case "bottom":
$length = $w;
$y += $h;
break;
case "left":
$length = $h;
break;
case "right":
$length = $h;
$x += $w;
break;
default:
break;
}
$this->$method($x, $y, $length, $props["color"], $widths, $side, $corner_style);
}
}
}

View File

@@ -0,0 +1,186 @@
<?php
/**
* DOMPDF - PHP5 HTML to PDF renderer
*
* File: $RCSfile: cached_pdf_decorator.cls.php,v $
* Created on: 2004-07-23
*
* Copyright (c) 2004 - Benj Carson <benjcarson@digitaljunkies.ca>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library in the file LICENSE.LGPL; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*
* Alternatively, you may distribute this software under the terms of the
* PHP License, version 3.0 or later. A copy of this license should have
* been distributed with this file in the file LICENSE.PHP . If this is not
* the case, you can obtain a copy at http://www.php.net/license/3_0.txt.
*
* The latest version of DOMPDF might be available at:
* http://www.dompdf.com/
*
* @link http://www.dompdf.com/
* @copyright 2004 Benj Carson
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @package dompdf
*/
/* $Id: cached_pdf_decorator.cls.php 216 2010-03-11 22:49:18Z ryan.masten $ */
/**
* Caching canvas implementation
*
* Each rendered page is serialized and stored in the {@link Page_Cache}.
* This is useful for static forms/pages that do not need to be re-rendered
* all the time.
*
* This class decorates normal CPDF_Adapters. It is currently completely
* experimental.
*
* @access private
* @package dompdf
*/
class Cached_PDF_Decorator extends CPDF_Adapter implements Canvas {
protected $_pdf;
protected $_cache_id;
protected $_current_page_id;
protected $_fonts; // fonts used in this document
function __construct($cache_id, CPDF_Adapter $pdf) {
$this->_pdf = $pdf;
$this->_cache_id = $cache_id;
$this->_fonts = array();
$this->_current_page_id = $this->_pdf->open_object();
}
//........................................................................
function get_cpdf() { return $this->_pdf->get_cpdf(); }
function open_object() { $this->_pdf->open_object(); }
function reopen_object() { return $this->_pdf->reopen_object(); }
function close_object() { $this->_pdf->close_object(); }
function add_object($object, $where = 'all') { $this->_pdf->add_object($object, $where); }
function serialize_object($id) { $this->_pdf->serialize_object($id); }
function reopen_serialized_object($obj) { $this->_pdf->reopen_serialized_object($obj); }
//........................................................................
function get_width() { return $this->_pdf->get_width(); }
function get_height() { return $this->_pdf->get_height(); }
function get_page_number() { return $this->_pdf->get_page_number(); }
function get_page_count() { return $this->_pdf->get_page_count(); }
function set_page_number($num) { $this->_pdf->set_page_number($num); }
function set_page_count($count) { $this->_pdf->set_page_count($count); }
function line($x1, $y1, $x2, $y2, $color, $width, $style = array()) {
$this->_pdf->line($x1, $y1, $x2, $y2, $color, $width, $style);
}
function rectangle($x1, $y1, $w, $h, $color, $width, $style = array()) {
$this->_pdf->rectangle($x1, $y1, $w, $h, $color, $width, $style);
}
function filled_rectangle($x1, $y1, $w, $h, $color) {
$this->_pdf->filled_rectangle($x1, $y1, $w, $h, $color);
}
function polygon($points, $color, $width = null, $style = array(), $fill = false) {
$this->_pdf->polygon($points, $color, $width, $style, $fill);
}
function circle($x, $y, $r1, $color, $width = null, $style = null, $fill = false) {
$this->_pdf->circle($x, $y, $r1, $color, $width, $style, $fill);
}
function image($img_url, $x, $y, $w = null, $h = null) {
$this->_pdf->image($img_url, $x, $y, $w, $h);
}
function text($x, $y, $text, $font, $size, $color = array(0,0,0), $adjust = 0, $angle = 0) {
$this->_fonts[$font] = true;
$this->_pdf->text($x, $y, $text, $font, $size, $color, $adjust, $angle);
}
function page_text($x, $y, $text, $font, $size, $color = array(0,0,0), $adjust = 0, $angle = 0) {
// We want to remove this from cached pages since it may not be correct
$this->_pdf->close_object();
$this->_pdf->page_text($x, $y, $text, $font, $size, $color, $adjust, $angle);
$this->_pdf->reopen_object($this->_current_page_id);
}
function page_script($script, $type = 'text/php') {
// We want to remove this from cached pages since it may not be correct
$this->_pdf->close_object();
$this->_pdf->page_script($script, $type);
$this->_pdf->reopen_object($this->_current_page_id);
}
function new_page() {
$this->_pdf->close_object();
// Add the object to the current page
$this->_pdf->add_object($this->_current_page_id, "add");
$this->_pdf->new_page();
Page_Cache::store_page($this->_cache_id,
$this->_pdf->get_page_number() - 1,
$this->_pdf->serialize_object($this->_current_page_id));
$this->_current_page_id = $this->_pdf->open_object();
return $this->_current_page_id;
}
function stream($filename) {
// Store the last page in the page cache
if ( !is_null($this->_current_page_id) ) {
$this->_pdf->close_object();
$this->_pdf->add_object($this->_current_page_id, "add");
Page_Cache::store_page($this->_cache_id,
$this->_pdf->get_page_number(),
$this->_pdf->serialize_object($this->_current_page_id));
Page_Cache::store_fonts($this->_cache_id, $this->_fonts);
$this->_current_page_id = null;
}
$this->_pdf->stream($filename);
}
function &output() {
// Store the last page in the page cache
if ( !is_null($this->_current_page_id) ) {
$this->_pdf->close_object();
$this->_pdf->add_object($this->_current_page_id, "add");
Page_Cache::store_page($this->_cache_id,
$this->_pdf->get_page_number(),
$this->_pdf->serialize_object($this->_current_page_id));
$this->_current_page_id = null;
}
return $this->_pdf->output();
}
function get_messages() { return $this->_pdf->get_messages(); }
}

328
pdf/include/canvas.cls.php Executable file
View File

@@ -0,0 +1,328 @@
<?php
/**
* DOMPDF - PHP5 HTML to PDF renderer
*
* File: $RCSfile: canvas.cls.php,v $
* Created on: 2004-06-06
*
* Copyright (c) 2004 - Benj Carson <benjcarson@digitaljunkies.ca>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library in the file LICENSE.LGPL; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*
* Alternatively, you may distribute this software under the terms of the
* PHP License, version 3.0 or later. A copy of this license should have
* been distributed with this file in the file LICENSE.PHP . If this is not
* the case, you can obtain a copy at http://www.php.net/license/3_0.txt.
*
* The latest version of DOMPDF might be available at:
* http://www.dompdf.com/
*
* @link http://www.dompdf.com/
* @copyright 2004 Benj Carson
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @package dompdf
*/
/* $Id: canvas.cls.php 357 2011-01-30 20:56:46Z fabien.menager $ */
/**
* Main rendering interface
*
* Currently {@link CPDF_Adapter}, {@link PDFLib_Adapter}, {@link TCPDF_Adapter}, and {@link GD_Adapter}
* implement this interface.
*
* Implementations should measure x and y increasing to the left and down,
* respectively, with the origin in the top left corner. Implementations
* are free to use a unit other than points for length, but I can't
* guarantee that the results will look any good.
*
* @package dompdf
*/
interface Canvas {
/**
* Returns the current page number
*
* @return int
*/
function get_page_number();
/**
* Returns the total number of pages
*
* @return int
*/
function get_page_count();
/**
* Sets the total number of pages
*
* @param int $count
*/
function set_page_count($count);
/**
* Draws a line from x1,y1 to x2,y2
*
* See {@link Style::munge_colour()} for the format of the colour array.
* See {@link Cpdf::setLineStyle()} for a description of the format of the
* $style parameter (aka dash).
*
* @param float $x1
* @param float $y1
* @param float $x2
* @param float $y2
* @param array $color
* @param float $width
* @param array $style
*/
function line($x1, $y1, $x2, $y2, $color, $width, $style = null);
/**
* Draws a rectangle at x1,y1 with width w and height h
*
* See {@link Style::munge_colour()} for the format of the colour array.
* See {@link Cpdf::setLineStyle()} for a description of the $style
* parameter (aka dash)
*
* @param float $x1
* @param float $y1
* @param float $w
* @param float $h
* @param array $color
* @param float $width
* @param array $style
*/
function rectangle($x1, $y1, $w, $h, $color, $width, $style = null);
/**
* Draws a filled rectangle at x1,y1 with width w and height h
*
* See {@link Style::munge_colour()} for the format of the colour array.
*
* @param float $x1
* @param float $y1
* @param float $w
* @param float $h
* @param array $color
*/
function filled_rectangle($x1, $y1, $w, $h, $color);
/**
* Starts a clipping rectangle at x1,y1 with width w and height h
*
* @param float $x1
* @param float $y1
* @param float $w
* @param float $h
*/
function clipping_rectangle($x1, $y1, $w, $h);
/**
* Ends the last clipping shape
*/
function clipping_end();
/**
* Save current state
*/
function save();
/**
* Restore last state
*/
function restore();
/**
* Rotate
*/
function rotate($angle, $x, $y);
/**
* Skew
*/
function skew($angle_x, $angle_y, $x, $y);
/**
* Scale
*/
function scale($s_x, $s_y, $x, $y);
/**
* Translate
*/
function translate($t_x, $t_y);
/**
* Transform
*/
function transform($a, $b, $c, $d, $e, $f);
/**
* Draws a polygon
*
* The polygon is formed by joining all the points stored in the $points
* array. $points has the following structure:
* <code>
* array(0 => x1,
* 1 => y1,
* 2 => x2,
* 3 => y2,
* ...
* );
* </code>
*
* See {@link Style::munge_colour()} for the format of the colour array.
* See {@link Cpdf::setLineStyle()} for a description of the $style
* parameter (aka dash)
*
* @param array $points
* @param array $color
* @param float $width
* @param array $style
* @param bool $fill Fills the polygon if true
*/
function polygon($points, $color, $width = null, $style = null, $fill = false);
/**
* Draws a circle at $x,$y with radius $r
*
* See {@link Style::munge_colour()} for the format of the colour array.
* See {@link Cpdf::setLineStyle()} for a description of the $style
* parameter (aka dash)
*
* @param float $x
* @param float $y
* @param float $r
* @param array $color
* @param float $width
* @param array $style
* @param bool $fill Fills the circle if true
*/
function circle($x, $y, $r, $color, $width = null, $style = null, $fill = false);
/**
* Add an image to the pdf.
*
* The image is placed at the specified x and y coordinates with the
* given width and height.
*
* @param string $img_url the path to the image
* @param string $img_type the type (e.g. extension) of the image
* @param float $x x position
* @param float $y y position
* @param int $w width (in pixels)
* @param int $h height (in pixels)
*/
function image($img_url, $img_type, $x, $y, $w, $h);
/**
* Writes text at the specified x and y coordinates
*
* See {@link Style::munge_colour()} for the format of the colour array.
*
* @param float $x
* @param float $y
* @param string $text the text to write
* @param string $font the font file to use
* @param float $size the font size, in points
* @param array $color
* @param float $word_space word spacing adjustment
* @param float $char_space whar spacing adjustment
* @param float $angle angle
*/
function text($x, $y, $text, $font, $size, $color = array(0,0,0), $word_space = 0, $char_space = 0, $angle = 0);
/**
* Add a named destination (similar to <a name="foo">...</a> in html)
*
* @param string $anchorname The name of the named destination
*/
function add_named_dest($anchorname);
/**
* Add a link to the pdf
*
* @param string $url The url to link to
* @param float $x The x position of the link
* @param float $y The y position of the link
* @param float $width The width of the link
* @param float $height The height of the link
*/
function add_link($url, $x, $y, $width, $height);
/**
* Add meta information to the pdf
*
* @param string $label label of the value (Creator, Producer, etc.)
* @param string $value the text to set
*/
function add_info($name, $value);
/**
* Calculates text size, in points
*
* @param string $text the text to be sized
* @param string $font the desired font
* @param float $size the desired font size
* @param float $spacing word spacing, if any
* @return float
*/
function get_text_width($text, $font, $size, $word_spacing = 0, $char_spacing = 0);
/**
* Calculates font height, in points
*
* @param string $font
* @param float $size
* @return float
*/
function get_font_height($font, $size);
/**
* Sets the opacity
*
* @param float $opacity
* @param string $mode
* @return float
*/
function set_opacity($opacity, $mode = "Normal");
/**
* Starts a new page
*
* Subsequent drawing operations will appear on the new page.
*/
function new_page();
/**
* Streams the PDF directly to the browser
*
* @param string $filename the name of the PDF file
* @param array $options associative array, 'Attachment' => 0 or 1, 'compress' => 1 or 0
*/
function stream($filename, $options = null);
/**
* Returns the PDF as a string
*
* @param array $options associative array: 'compress' => 1 or 0
* @return string
*/
function output($options = null);
}

View File

@@ -0,0 +1,83 @@
<?php
/**
* DOMPDF - PHP5 HTML to PDF renderer
*
* File: $RCSfile: canvas_factory.cls.php,v $
* Created on: 2004-06-02
*
* Copyright (c) 2004 - Benj Carson <benjcarson@digitaljunkies.ca>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library in the file LICENSE.LGPL; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*
* Alternatively, you may distribute this software under the terms of the
* PHP License, version 3.0 or later. A copy of this license should have
* been distributed with this file in the file LICENSE.PHP . If this is not
* the case, you can obtain a copy at http://www.php.net/license/3_0.txt.
*
* The latest version of DOMPDF might be available at:
* http://www.dompdf.com/
*
* @link http://www.dompdf.com/
* @copyright 2004 Benj Carson
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @package dompdf
*/
/* $Id: canvas_factory.cls.php 216 2010-03-11 22:49:18Z ryan.masten $ */
/**
* Create canvas instances
*
* The canvas factory creates canvas instances based on the
* availability of rendering backends and config options.
*
* @package dompdf
*/
class Canvas_Factory {
/**
* Constructor is private: this is a static class
*/
private function __construct() { }
static function get_instance($paper = null, $orientation = null, $class = null) {
$backend = strtolower(DOMPDF_PDF_BACKEND);
if ( isset($class) && class_exists($class, false) )
$class .= "_Adapter";
else if ( (DOMPDF_PDF_BACKEND === "auto" || $backend === "pdflib" ) &&
class_exists("PDFLib", false) )
$class = "PDFLib_Adapter";
else if ( (DOMPDF_PDF_BACKEND === "auto" || $backend === "cpdf") )
$class = "CPDF_Adapter";
else if ( ( $backend === "tcpdf") )
$class = "TCPDF_Adapter";
else if ( $backend === "gd" )
$class = "GD_Adapter";
else
$class = "CPDF_Adapter";
return new $class($paper, $orientation);
}
}

730
pdf/include/cellmap.cls.php Executable file
View File

@@ -0,0 +1,730 @@
<?php
/**
* DOMPDF - PHP5 HTML to PDF renderer
*
* File: $RCSfile: cellmap.cls.php,v $
* Created on: 2004-07-28
*
* Copyright (c) 2004 - Benj Carson <benjcarson@digitaljunkies.ca>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library in the file LICENSE.LGPL; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*
* Alternatively, you may distribute this software under the terms of the
* PHP License, version 3.0 or later. A copy of this license should have
* been distributed with this file in the file LICENSE.PHP . If this is not
* the case, you can obtain a copy at http://www.php.net/license/3_0.txt.
*
* The latest version of DOMPDF might be available at:
* http://www.dompdf.com/
*
* @link http://www.dompdf.com/
* @copyright 2004 Benj Carson
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @package dompdf
*/
/* $Id: cellmap.cls.php 283 2010-07-19 17:57:40Z fabien.menager $ */
/**
* Maps table cells to the table grid.
*
* This class resolves borders in tables with collapsed borders and helps
* place row & column spanned table cells.
*
* @access private
* @package dompdf
*/
class Cellmap {
/**
* Border style weight lookup for collapsed border resolution.
*
* @var array
*/
static protected $_BORDER_STYLE_SCORE = array("inset" => 1,
"groove" => 2,
"outset" => 3,
"ridge" => 4,
"dotted" => 5,
"dashed" => 6,
"solid" => 7,
"double" => 8,
"none" => 0);
/**
* The table object this cellmap is attached to.
*
* @var Table_Frame_Decorator
*/
protected $_table;
/**
* The total number of rows in the table
*
* @var int
*/
protected $_num_rows;
/**
* The total number of columns in the table
*
* @var int
*/
protected $_num_cols;
/**
* 2D array mapping <row,column> to frames
*
* @var array
*/
protected $_cells;
/**
* 1D array of column dimensions
*
* @var array
*/
protected $_columns;
/**
* 1D array of row dimensions
*
* @var array
*/
protected $_rows;
/**
* 2D array of border specs
*
* @var array
*/
protected $_borders;
/**
* 1D Array mapping frames to (multiple) <row, col> pairs, keyed on
* frame_id.
*
* @var array
*/
protected $_frames;
/**
* Current column when adding cells, 0-based
*
* @var int
*/
private $__col;
/**
* Current row when adding cells, 0-based
*
* @var int
*/
private $__row;
//........................................................................
function __construct(Table_Frame_Decorator $table) {
$this->_table = $table;
$this->reset();
}
function __destruct() {
clear_object($this);
}
//........................................................................
function reset() {
$this->_num_rows = 0;
$this->_num_cols = 0;
$this->_cells = array();
$this->_frames = array();
$this->_columns = array();
$this->_rows = array();
$this->_borders = array();
$this->__col = $this->__row = 0;
}
//........................................................................
function get_num_rows() { return $this->_num_rows; }
function get_num_cols() { return $this->_num_cols; }
function &get_columns() {
return $this->_columns;
}
function &get_column($i) {
if ( !isset($this->_columns[$i]) )
$this->_columns[$i] = array("x" => 0,
"min-width" => 0,
"max-width" => 0,
"used-width" => null,
"absolute" => 0,
"percent" => 0,
"auto" => true);
return $this->_columns[$i];
}
function &get_rows() {
return $this->_rows;
}
function &get_row($j) {
if ( !isset($this->_rows[$j]) )
$this->_rows[$j] = array("y" => 0,
"first-column" => 0,
"height" => null);
return $this->_rows[$j];
}
function get_border($i, $j, $h_v, $prop = null) {
if ( !isset($this->_borders[$i][$j][$h_v]) )
$this->_borders[$i][$j][$h_v] = array("width" => 0,
"style" => "solid",
"color" => "black");
if ( isset($prop) )
return $this->_borders[$i][$j][$h_v][$prop];
return $this->_borders[$i][$j][$h_v];
}
function get_border_properties($i, $j) {
$left = $this->get_border($i, $j, "vertical");
$right = $this->get_border($i, $j+1, "vertical");
$top = $this->get_border($i, $j, "horizontal");
$bottom = $this->get_border($i+1, $j, "horizontal");
return compact("top", "bottom", "left", "right");
}
//........................................................................
function get_spanned_cells($frame) {
$key = $frame->get_id();
if ( !isset($this->_frames[$key]) ) {
throw new DOMPDF_Internal_Exception("Frame not found in cellmap");
}
return $this->_frames[$key];
}
function frame_exists_in_cellmap($frame) {
$key = $frame->get_id();
return isset($this->_frames[$key]);
}
function get_frame_position($frame) {
global $_dompdf_warnings;
$key = $frame->get_id();
if ( !isset($this->_frames[$key]) ) {
throw new DOMPDF_Internal_Exception("Frame not found in cellmap");
}
$col = $this->_frames[$key]["columns"][0];
$row = $this->_frames[$key]["rows"][0];
if ( !isset($this->_columns[$col])) {
$_dompdf_warnings[] = "Frame not found in columns array. Check your table layout for missing or extra TDs.";
$x = 0;
} else
$x = $this->_columns[$col]["x"];
if ( !isset($this->_rows[$row])) {
$_dompdf_warnings[] = "Frame not found in row array. Check your table layout for missing or extra TDs.";
$y = 0;
} else
$y = $this->_rows[$row]["y"];
return array($x, $y, "x" => $x, "y" => $y);
}
function get_frame_width($frame) {
$key = $frame->get_id();
if ( !isset($this->_frames[$key]) ) {
throw new DOMPDF_Internal_Exception("Frame not found in cellmap");
}
$cols = $this->_frames[$key]["columns"];
$w = 0;
foreach ($cols as $i)
$w += $this->_columns[$i]["used-width"];
return $w;
}
function get_frame_height($frame) {
$key = $frame->get_id();
if ( !isset($this->_frames[$key]) )
throw new DOMPDF_Internal_Exception("Frame not found in cellmap");
$rows = $this->_frames[$key]["rows"];
$h = 0;
foreach ($rows as $i) {
if ( !isset($this->_rows[$i]) ) {
throw new Exception("foo");
}
$h += $this->_rows[$i]["height"];
}
return $h;
}
//........................................................................
function set_column_width($j, $width) {
$col =& $this->get_column($j);
$col["used-width"] = $width;
$next_col =& $this->get_column($j+1);
$next_col["x"] = $next_col["x"] + $width;
}
function set_row_height($i, $height) {
$row =& $this->get_row($i);
if ( $height <= $row["height"] )
return;
$row["height"] = $height;
$next_row =& $this->get_row($i+1);
$next_row["y"] = $row["y"] + $height;
}
//........................................................................
protected function _resolve_border($i, $j, $h_v, $border_spec) {
$n_width = $border_spec["width"];
$n_style = $border_spec["style"];
$n_color = $border_spec["color"];
if ( !isset($this->_borders[$i][$j][$h_v]) ) {
$this->_borders[$i][$j][$h_v] = $border_spec;
return $this->_borders[$i][$j][$h_v]["width"];
}
$border = &$this->_borders[$i][$j][$h_v];
$o_width = $border["width"];
$o_style = $border["style"];
$o_color = $border["color"];
if ( ($n_style === "hidden" ||
$n_width > $o_width ||
$o_style === "none")
or
($o_width == $n_width &&
in_array($n_style, self::$_BORDER_STYLE_SCORE) &&
self::$_BORDER_STYLE_SCORE[ $n_style ] > self::$_BORDER_STYLE_SCORE[ $o_style ]) )
$border = $border_spec;
return $border["width"];
}
//........................................................................
function add_frame(Frame $frame) {
$style = $frame->get_style();
$display = $style->display;
$collapse = $this->_table->get_style()->border_collapse == "collapse";
// Recursively add the frames within tables, table-row-groups and table-rows
if ( $display === "table-row" ||
$display === "table" ||
$display === "inline-table" ||
in_array($display, Table_Frame_Decorator::$ROW_GROUPS) ) {
$start_row = $this->__row;
foreach ( $frame->get_children() as $child )
$this->add_frame( $child );
if ( $display === "table-row" )
$this->add_row();
$num_rows = $this->__row - $start_row - 1;
$key = $frame->get_id();
// Row groups always span across the entire table
$this->_frames[$key]["columns"] = range(0,max(0,$this->_num_cols-1));
$this->_frames[$key]["rows"] = range($start_row, max(0, $this->__row - 1));
$this->_frames[$key]["frame"] = $frame;
if ( $display !== "table-row" && $collapse ) {
$bp = $style->get_border_properties();
// Resolve the borders
for ( $i = 0; $i < $num_rows+1; $i++) {
$this->_resolve_border($start_row + $i, 0, "vertical", $bp["left"]);
$this->_resolve_border($start_row + $i, $this->_num_cols, "vertical", $bp["right"]);
}
for ( $j = 0; $j < $this->_num_cols; $j++) {
$this->_resolve_border($start_row, $j, "horizontal", $bp["top"]);
$this->_resolve_border($this->__row, $j, "horizontal", $bp["bottom"]);
}
}
return;
}
$node = $frame->get_node();
// Determine where this cell is going
$colspan = $node->getAttribute("colspan");
$rowspan = $node->getAttribute("rowspan");
if ( !$colspan ) {
$colspan = 1;
$node->setAttribute("colspan",1);
}
if ( !$rowspan ) {
$rowspan = 1;
$node->setAttribute("rowspan",1);
}
$key = $frame->get_id();
$bp = $style->get_border_properties();
// Add the frame to the cellmap
$max_left = $max_right = 0;
// Find the next available column (fix by Ciro Mondueri)
$ac = $this->__col;
while ( isset($this->_cells[$this->__row][$ac]) )
$ac++;
$this->__col = $ac;
// Rows:
for ( $i = 0; $i < $rowspan; $i++ ) {
$row = $this->__row + $i;
$this->_frames[$key]["rows"][] = $row;
for ( $j = 0; $j < $colspan; $j++)
$this->_cells[$row][$this->__col + $j] = $frame;
if ( $collapse ) {
// Resolve vertical borders
$max_left = max($max_left, $this->_resolve_border($row, $this->__col, "vertical", $bp["left"]));
$max_right = max($max_right, $this->_resolve_border($row, $this->__col + $colspan, "vertical", $bp["right"]));
}
}
$max_top = $max_bottom = 0;
// Columns:
for ( $j = 0; $j < $colspan; $j++ ) {
$col = $this->__col + $j;
$this->_frames[$key]["columns"][] = $col;
if ( $collapse ) {
// Resolve horizontal borders
$max_top = max($max_top, $this->_resolve_border($this->__row, $col, "horizontal", $bp["top"]));
$max_bottom = max($max_bottom, $this->_resolve_border($this->__row + $rowspan, $col, "horizontal", $bp["bottom"]));
}
}
$this->_frames[$key]["frame"] = $frame;
// Handle seperated border model
if ( !$collapse ) {
list($h, $v) = $this->_table->get_style()->border_spacing;
// Border spacing is effectively a margin between cells
$v = $style->length_in_pt($v) / 2;
$h = $style->length_in_pt($h) / 2;
$style->margin = "$v $h";
// The additional 1/2 width gets added to the table proper
} else {
// Drop the frame's actual border
$style->border_left_width = $max_left / 2;
$style->border_right_width = $max_right / 2;
$style->border_top_width = $max_top / 2;
$style->border_bottom_width = $max_bottom / 2;
$style->margin = "none";
}
// Resolve the frame's width
list($frame_min, $frame_max) = $frame->get_min_max_width();
$width = $style->width;
if ( is_percent($width) ) {
$var = "percent";
$val = (float)rtrim($width, "% ") / $colspan;
} else if ( $width !== "auto" ) {
$var = "absolute";
$val = $style->length_in_pt($frame_min) / $colspan;
}
$min = 0;
$max = 0;
for ( $cs = 0; $cs < $colspan; $cs++ ) {
// Resolve the frame's width(s) with other cells
$col =& $this->get_column( $this->__col + $cs );
// Note: $var is either 'percent' or 'absolute'. We compare the
// requested percentage or absolute values with the existing widths
// and adjust accordingly.
if ( isset($var) && $val > $col[$var] ) {
$col[$var] = $val;
$col["auto"] = false;
}
$min += $col["min-width"];
$max += $col["max-width"];
}
if ( $frame_min > $min ) {
// The frame needs more space. Expand each sub-column
$inc = ($frame_min - $min) / $colspan;
for ($c = 0; $c < $colspan; $c++) {
$col =& $this->get_column($this->__col + $c);
$col["min-width"] += $inc;
}
}
if ( $frame_max > $max ) {
$inc = ($frame_max - $max) / $colspan;
for ($c = 0; $c < $colspan; $c++) {
$col =& $this->get_column($this->__col + $c);
$col["max-width"] += $inc;
}
}
$this->__col += $colspan;
if ( $this->__col > $this->_num_cols )
$this->_num_cols = $this->__col;
}
//........................................................................
function add_row() {
$this->__row++;
$this->_num_rows++;
// Find the next available column
$i = 0;
while ( isset($this->_cells[$this->__row][$i]) )
$i++;
$this->__col = $i;
}
//........................................................................
/**
* Remove a row from the cellmap.
*
* @param Frame
*/
function remove_row(Frame $row) {
$key = $row->get_id();
if ( !isset($this->_frames[$key]) )
return; // Presumably this row has alredy been removed
$this->_row = $this->_num_rows--;
$rows = $this->_frames[$key]["rows"];
$columns = $this->_frames[$key]["columns"];
// Remove all frames from this row
foreach ( $rows as $r ) {
foreach ( $columns as $c ) {
if ( isset($this->_cells[$r][$c]) ) {
$id = $this->_cells[$r][$c]->get_id();
$this->_frames[$id] = null;
unset($this->_frames[$id]);
$this->_cells[$r][$c] = null;
unset($this->_cells[$r][$c]);
}
}
$this->_rows[$r] = null;
unset($this->_rows[$r]);
}
$this->_frames[$key] = null;
unset($this->_frames[$key]);
}
/**
* Remove a row group from the cellmap.
*
* @param Frame $group The group to remove
*/
function remove_row_group(Frame $group) {
$key = $group->get_id();
if ( !isset($this->_frames[$key]) )
return; // Presumably this row has alredy been removed
$iter = $group->get_first_child();
while ($iter) {
$this->remove_row($iter);
$iter = $iter->get_next_sibling();
}
$this->_frames[$key] = null;
unset($this->_frames[$key]);
}
/**
* Update a row group after rows have been removed
*
* @param Frame $group The group to update
* @param Frame $last_row The last row in the row group
*/
function update_row_group(Frame $group, Frame $last_row) {
$g_key = $group->get_id();
$r_key = $last_row->get_id();
$r_rows = $this->_frames[$r_key]["rows"];
$this->_frames[$g_key]["rows"] = range( $this->_frames[$g_key]["rows"][0], end($r_rows) );
}
//........................................................................
function assign_x_positions() {
// Pre-condition: widths must be resolved and assigned to columns and
// column[0]["x"] must be set.
$x = $this->_columns[0]["x"];
foreach ( array_keys($this->_columns) as $j ) {
$this->_columns[$j]["x"] = $x;
$x += $this->_columns[$j]["used-width"];
}
}
function assign_frame_heights() {
// Pre-condition: widths and heights of each column & row must be
// calcluated
foreach ( $this->_frames as $arr ) {
$frame = $arr["frame"];
$h = 0;
foreach( $arr["rows"] as $row ) {
if ( !isset($this->_rows[$row]) )
// The row has been removed because of a page split, so skip it.
continue;
$h += $this->_rows[$row]["height"];
}
if ( $frame instanceof Table_Cell_Frame_Decorator )
$frame->set_cell_height($h);
else
$frame->get_style()->height = $h;
}
}
//........................................................................
/**
* Re-adjust frame height if the table height is larger than its content
*/
function set_frame_heights($table_height, $content_height) {
// Distribute the increased height proportionally amongst each row
foreach ( $this->_frames as $arr ) {
$frame = $arr["frame"];
$h = 0;
foreach ($arr["rows"] as $row ) {
if ( !isset($this->_rows[$row]) )
continue;
$h += $this->_rows[$row]["height"];
}
if ( $content_height > 0 )
$new_height = ($h / $content_height) * $table_height;
else
$new_height = 0;
if ( $frame instanceof Table_Cell_Frame_Decorator )
$frame->set_cell_height($new_height);
else
$frame->get_style()->height = $new_height;
}
}
//........................................................................
// Used for debugging:
function __toString() {
$str = "";
$str .= "Columns:<br/>";
$str .= pre_r($this->_columns, true);
$str .= "Rows:<br/>";
$str .= pre_r($this->_rows, true);
$str .= "Frames:<br/>";
$arr = array();
foreach ( $this->_frames as $key => $val )
$arr[$key] = array("columns" => $val["columns"], "rows" => $val["rows"]);
$str .= pre_r($arr, true);
if ( php_sapi_name() == "cli" )
$str = strip_tags(str_replace(array("<br/>","<b>","</b>"),
array("\n",chr(27)."[01;33m", chr(27)."[0m"),
$str));
return $str;
}
}

919
pdf/include/cpdf_adapter.cls.php Executable file
View File

@@ -0,0 +1,919 @@
<?php
/**
* DOMPDF - PHP5 HTML to PDF renderer
*
* File: $RCSfile: cpdf_adapter.cls.php,v $
* Created on: 2004-08-04
* Modified on: 2008-01-05
*
* Copyright (c) 2004 - Benj Carson <benjcarson@digitaljunkies.ca>
* Portions copyright (c) 2008 - Orion Richardson <orionr@yahoo.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library in the file LICENSE.LGPL; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*
* Alternatively, you may distribute this software under the terms of the
* PHP License, version 3.0 or later. A copy of this license should have
* been distributed with this file in the file LICENSE.PHP . If this is not
* the case, you can obtain a copy at http://www.php.net/license/3_0.txt.
*
* The latest version of DOMPDF might be available at:
* http://www.dompdf.com/
*
* @link http://www.dompdf.com/
* @copyright 2004 Benj Carson
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @contributor Orion Richardson <orionr@yahoo.com>
* @contributor Helmut Tischer <htischer@weihenstephan.org>
* @package dompdf
*
* Changes
* @contributor Helmut Tischer <htischer@weihenstephan.org>
* @version 0.5.1.htischer.20090507
* - On gif to png conversion tmp file creation, clarify tmp name and add to tmp deletion list only on success
* - On gif to png conversion, when available add direct from gd without tmp file, skip image load if already cached.
* to safe CPU time and memory
* @contributor Helmut Tischer <htischer@weihenstephan.org>
* @version dompdf_trunk_with_helmut_mods.20090524
* - Pass temp and fontcache folders to Cpdf, to making Cpdf independent from dompdf
* @version dompdf_trunk_with_helmut_mods.20090528
* - fix text position according to glyph baseline to match background rectangle
*/
/* $Id: cpdf_adapter.cls.php 356 2011-01-28 08:56:10Z fabien.menager $ */
// FIXME: Need to sanity check inputs to this class
require_once(DOMPDF_LIB_DIR . "/class.pdf.php");
/**
* PDF rendering interface
*
* CPDF_Adapter provides a simple stateless interface to the stateful one
* provided by the Cpdf class.
*
* Unless otherwise mentioned, all dimensions are in points (1/72 in). The
* coordinate origin is in the top left corner, and y values increase
* downwards.
*
* See {@link http://www.ros.co.nz/pdf/} for more complete documentation
* on the underlying {@link Cpdf} class.
*
* @package dompdf
*/
class CPDF_Adapter implements Canvas {
/**
* Dimensions of paper sizes in points
*
* @var array;
*/
static $PAPER_SIZES = array(
"4a0" => array(0,0,4767.87,6740.79),
"2a0" => array(0,0,3370.39,4767.87),
"a0" => array(0,0,2383.94,3370.39),
"a1" => array(0,0,1683.78,2383.94),
"a2" => array(0,0,1190.55,1683.78),
"a3" => array(0,0,841.89,1190.55),
"a4" => array(0,0,595.28,841.89),
"a5" => array(0,0,419.53,595.28),
"a6" => array(0,0,297.64,419.53),
"a7" => array(0,0,209.76,297.64),
"a8" => array(0,0,147.40,209.76),
"a9" => array(0,0,104.88,147.40),
"a10" => array(0,0,73.70,104.88),
"b0" => array(0,0,2834.65,4008.19),
"b1" => array(0,0,2004.09,2834.65),
"b2" => array(0,0,1417.32,2004.09),
"b3" => array(0,0,1000.63,1417.32),
"b4" => array(0,0,708.66,1000.63),
"b5" => array(0,0,498.90,708.66),
"b6" => array(0,0,354.33,498.90),
"b7" => array(0,0,249.45,354.33),
"b8" => array(0,0,175.75,249.45),
"b9" => array(0,0,124.72,175.75),
"b10" => array(0,0,87.87,124.72),
"c0" => array(0,0,2599.37,3676.54),
"c1" => array(0,0,1836.85,2599.37),
"c2" => array(0,0,1298.27,1836.85),
"c3" => array(0,0,918.43,1298.27),
"c4" => array(0,0,649.13,918.43),
"c5" => array(0,0,459.21,649.13),
"c6" => array(0,0,323.15,459.21),
"c7" => array(0,0,229.61,323.15),
"c8" => array(0,0,161.57,229.61),
"c9" => array(0,0,113.39,161.57),
"c10" => array(0,0,79.37,113.39),
"ra0" => array(0,0,2437.80,3458.27),
"ra1" => array(0,0,1729.13,2437.80),
"ra2" => array(0,0,1218.90,1729.13),
"ra3" => array(0,0,864.57,1218.90),
"ra4" => array(0,0,609.45,864.57),
"sra0" => array(0,0,2551.18,3628.35),
"sra1" => array(0,0,1814.17,2551.18),
"sra2" => array(0,0,1275.59,1814.17),
"sra3" => array(0,0,907.09,1275.59),
"sra4" => array(0,0,637.80,907.09),
"letter" => array(0,0,612.00,792.00),
"legal" => array(0,0,612.00,1008.00),
"ledger" => array(0,0,1224.00, 792.00),
"tabloid" => array(0,0,792.00, 1224.00),
"executive" => array(0,0,521.86,756.00),
"folio" => array(0,0,612.00,936.00),
"commercial #10 envelope" => array(0,0,684,297),
"catalog #10 1/2 envelope" => array(0,0,648,864),
"8.5x11" => array(0,0,612.00,792.00),
"8.5x14" => array(0,0,612.00,1008.0),
"11x17" => array(0,0,792.00, 1224.00),
);
/**
* Instance of Cpdf class
*
* @var Cpdf
*/
private $_pdf;
/**
* PDF width, in points
*
* @var float
*/
private $_width;
/**
* PDF height, in points
*
* @var float;
*/
private $_height;
/**
* Current page number
*
* @var int
*/
private $_page_number;
/**
* Total number of pages
*
* @var int
*/
private $_page_count;
/**
* Text to display on every page
*
* @var array
*/
private $_page_text;
/**
* Array of pages for accesing after rendering is initially complete
*
* @var array
*/
private $_pages;
/**
* Array of temporary cached images to be deleted when processing is complete
*
* @var array
*/
private $_image_cache;
/**
* Class constructor
*
* @param mixed $paper The size of paper to use in this PDF ({@link CPDF_Adapter::$PAPER_SIZES})
* @param string $orientation The orienation of the document (either 'landscape' or 'portrait')
*/
function __construct($paper = "letter", $orientation = "portrait") {
if ( is_array($paper) )
$size = $paper;
else if ( isset(self::$PAPER_SIZES[mb_strtolower($paper)]) )
$size = self::$PAPER_SIZES[mb_strtolower($paper)];
else
$size = self::$PAPER_SIZES["letter"];
if ( mb_strtolower($orientation) === "landscape" ) {
list($size[2], $size[3]) = array($size[3], $size[2]);
}
$this->_pdf = new Cpdf($size, DOMPDF_UNICODE_ENABLED, DOMPDF_FONT_CACHE, DOMPDF_TEMP_DIR);
$this->_pdf->addInfo("Creator", "DOMPDF");
$time = substr_replace(date('YmdHisO'), '\'', -2, 0).'\'';
$this->_pdf->addInfo("CreationDate", "D:$time");
$this->_pdf->addInfo("ModDate", "D:$time");
$this->_width = $size[2] - $size[0];
$this->_height= $size[3] - $size[1];
$this->_pdf->openHere('Fit');
$this->_page_number = $this->_page_count = 1;
$this->_page_text = array();
$this->_pages = array($this->_pdf->getFirstPageId());
$this->_image_cache = array();
}
/**
* Class destructor
*
* Deletes all temporary image files
*/
function __destruct() {
foreach ($this->_image_cache as $img) {
//debugpng
if (DEBUGPNG) print '[__destruct unlink '.$img.']';
if (!DEBUGKEEPTEMP)
unlink($img);
}
clear_object($this);
}
/**
* Returns the Cpdf instance
*
* @return Cpdf
*/
function get_cpdf() { return $this->_pdf; }
/**
* Add meta information to the PDF
*
* @param string $label label of the value (Creator, Producer, etc.)
* @param string $value the text to set
*/
function add_info($label, $value) {
$this->_pdf->addInfo($label, $value);
}
/**
* Opens a new 'object'
*
* While an object is open, all drawing actions are recored in the object,
* as opposed to being drawn on the current page. Objects can be added
* later to a specific page or to several pages.
*
* The return value is an integer ID for the new object.
*
* @see CPDF_Adapter::close_object()
* @see CPDF_Adapter::add_object()
*
* @return int
*/
function open_object() {
$ret = $this->_pdf->openObject();
$this->_pdf->saveState();
return $ret;
}
/**
* Reopens an existing 'object'
*
* @see CPDF_Adapter::open_object()
* @param int $object the ID of a previously opened object
*/
function reopen_object($object) {
$this->_pdf->reopenObject($object);
$this->_pdf->saveState();
}
/**
* Closes the current 'object'
*
* @see CPDF_Adapter::open_object()
*/
function close_object() {
$this->_pdf->restoreState();
$this->_pdf->closeObject();
}
/**
* Adds a specified 'object' to the document
*
* $object int specifying an object created with {@link
* CPDF_Adapter::open_object()}. $where can be one of:
* - 'add' add to current page only
* - 'all' add to every page from the current one onwards
* - 'odd' add to all odd numbered pages from now on
* - 'even' add to all even numbered pages from now on
* - 'next' add the object to the next page only
* - 'nextodd' add to all odd numbered pages from the next one
* - 'nexteven' add to all even numbered pages from the next one
*
* @see Cpdf::addObject()
*
* @param int $object
* @param string $where
*/
function add_object($object, $where = 'all') {
$this->_pdf->addObject($object, $where);
}
/**
* Stops the specified 'object' from appearing in the document.
*
* The object will stop being displayed on the page following the current
* one.
*
* @param int $object
*/
function stop_object($object) {
$this->_pdf->stopObject($object);
}
/**
* @access private
*/
function serialize_object($id) {
// Serialize the pdf object's current state for retrieval later
return $this->_pdf->serializeObject($id);
}
/**
* @access private
*/
function reopen_serialized_object($obj) {
return $this->_pdf->restoreSerializedObject($obj);
}
//........................................................................
/**
* Returns the PDF's width in points
* @return float
*/
function get_width() { return $this->_width; }
/**
* Returns the PDF's height in points
* @return float
*/
function get_height() { return $this->_height; }
/**
* Returns the current page number
* @return int
*/
function get_page_number() { return $this->_page_number; }
/**
* Returns the total number of pages in the document
* @return int
*/
function get_page_count() { return $this->_page_count; }
/**
* Sets the current page number
*
* @param int $num
*/
function set_page_number($num) { $this->_page_number = $num; }
/**
* Sets the page count
*
* @param int $count
*/
function set_page_count($count) { $this->_page_count = $count; }
/**
* Sets the stroke colour
*
* See {@link Style::set_colour()} for the format of the color array.
* @param array $color
*/
protected function _set_stroke_color($color) {
$this->_pdf->setStrokeColor($color);
}
/**
* Sets the fill colour
*
* See {@link Style::set_colour()} for the format of the colour array.
* @param array $color
*/
protected function _set_fill_color($color) {
$this->_pdf->setColor($color);
}
/**
* Sets line transparency
* @see Cpdf::setLineTransparency()
*
* Valid blend modes are (case-sensitive):
*
* Normal, Multiply, Screen, Overlay, Darken, Lighten,
* ColorDodge, ColorBurn, HardLight, SoftLight, Difference,
* Exclusion
*
* @param string $mode the blending mode to use
* @param float $opacity 0.0 fully transparent, 1.0 fully opaque
*/
protected function _set_line_transparency($mode, $opacity) {
$this->_pdf->setLineTransparency($mode, $opacity);
}
/**
* Sets fill transparency
* @see Cpdf::setFillTransparency()
*
* Valid blend modes are (case-sensitive):
*
* Normal, Multiply, Screen, Overlay, Darken, Lighten,
* ColorDogde, ColorBurn, HardLight, SoftLight, Difference,
* Exclusion
*
* @param string $mode the blending mode to use
* @param float $opacity 0.0 fully transparent, 1.0 fully opaque
*/
protected function _set_fill_transparency($mode, $opacity) {
$this->_pdf->setFillTransparency($mode, $opacity);
}
/**
* Sets the line style
*
* @see Cpdf::setLineStyle()
*
* @param float width
* @param string cap
* @param string join
* @param array dash
*/
protected function _set_line_style($width, $cap, $join, $dash) {
$this->_pdf->setLineStyle($width, $cap, $join, $dash);
}
/**
* Sets the opacity
*
* @param $opacity
* @param $mode
*/
function set_opacity($opacity, $mode = "Normal") {
$this->_set_line_transparency($mode, $opacity);
$this->_set_fill_transparency($mode, $opacity);
}
//........................................................................
/**
* Remaps y coords from 4th to 1st quadrant
*
* @param float $y
* @return float
*/
protected function y($y) { return $this->_height - $y; }
// Canvas implementation
function line($x1, $y1, $x2, $y2, $color, $width, $style = array()) {
//pre_r(compact("x1", "y1", "x2", "y2", "color", "width", "style"));
$this->_set_stroke_color($color);
$this->_set_line_style($width, "butt", "", $style);
$this->_pdf->line($x1, $this->y($y1),
$x2, $this->y($y2));
}
//........................................................................
/**
* Convert a GIF or BMP image to a PNG image
*
* @return string The url of the newly converted image
*/
protected function _convert_gif_bmp_to_png($image_url, $image_type) {
$func_name = "imagecreatefrom$image_type";
if ( !function_exists($func_name) ) {
throw new DOMPDF_Exception("Function $func_name() not found. Cannot convert $image_type image: $image_url. Please install the image PHP extension.");
}
set_error_handler("record_warnings");
$im = $func_name($image_url);
if ( $im ) {
imageinterlace($im, 0);
$filename = tempnam(DOMPDF_TEMP_DIR, "{$image_type}dompdf_img_").'.png';
$this->_image_cache[] = $filename;
imagepng($im, $filename);
imagedestroy($im);
} else {
$filename = DOMPDF_LIB_DIR . "/res/broken_image.png";
}
restore_error_handler();
return $filename;
}
function rectangle($x1, $y1, $w, $h, $color, $width, $style = array()) {
$this->_set_stroke_color($color);
$this->_set_line_style($width, "butt", "", $style);
$this->_pdf->rectangle($x1, $this->y($y1) - $h, $w, $h);
}
//........................................................................
function filled_rectangle($x1, $y1, $w, $h, $color) {
$this->_set_fill_color($color);
$this->_pdf->filledRectangle($x1, $this->y($y1) - $h, $w, $h);
}
function clipping_rectangle($x1, $y1, $w, $h) {
$this->_pdf->clippingRectangle($x1, $this->y($y1) - $h, $w, $h);
}
function clipping_end() {
$this->_pdf->clippingEnd();
}
function save() {
$this->_pdf->saveState();
}
function restore() {
$this->_pdf->restoreState();
}
function rotate($angle, $x, $y) {
$this->_pdf->rotate($angle, $x, $y);
}
function skew($angle_x, $angle_y, $x, $y) {
$this->_pdf->skew($angle_x, $angle_y, $x, $y);
}
function scale($s_x, $s_y, $x, $y) {
$this->_pdf->scale($s_x, $s_y, $x, $y);
}
function translate($t_x, $t_y) {
$this->_pdf->translate($t_x, $t_y);
}
function transform($a, $b, $c, $d, $e, $f) {
$this->_pdf->transform(array($a, $b, $c, $d, $e, $f));
}
//........................................................................
function polygon($points, $color, $width = null, $style = array(), $fill = false) {
$this->_set_fill_color($color);
$this->_set_stroke_color($color);
// Adjust y values
for ( $i = 1; $i < count($points); $i += 2)
$points[$i] = $this->y($points[$i]);
$this->_pdf->polygon($points, count($points) / 2, $fill);
}
//........................................................................
function circle($x, $y, $r1, $color, $width = null, $style = null, $fill = false) {
$this->_set_fill_color($color);
$this->_set_stroke_color($color);
if ( !$fill && isset($width) )
$this->_set_line_style($width, "round", "round", $style);
$this->_pdf->ellipse($x, $this->y($y), $r1, 0, 0, 8, 0, 360, 1, $fill);
}
//........................................................................
function image($img_url, $img_type, $x, $y, $w, $h) {
//debugpng
if (DEBUGPNG) print '[image:'.$img_url.'|'.$img_type.']';
$img_type = mb_strtolower($img_type);
switch ($img_type) {
case "jpeg":
case "jpg":
//debugpng
if (DEBUGPNG) print '!!!jpg!!!';
$this->_pdf->addJpegFromFile($img_url, $x, $this->y($y) - $h, $w, $h);
break;
case "png":
//debugpng
if (DEBUGPNG) print '!!!png!!!';
$this->_pdf->addPngFromFile($img_url, $x, $this->y($y) - $h, $w, $h);
break;
case "gif":
case "bmp":
// Convert gifs or bmps to pngs
//DEBUG_IMG_TEMP
//if (0) {
if ( method_exists( $this->_pdf, "addImagePng" ) ) {
//debugpng
if (DEBUGPNG) print "!!!$img_type addImagePng!!!";
//If optimization to direct png creation from gd object is available,
//don't create temp file, but place gd object directly into the pdf
if ( method_exists( $this->_pdf, "image_iscached" ) &&
$this->_pdf->image_iscached($img_url) ) {
//If same image has occured already before, no need to load because
//duplicate will anyway be eliminated.
$img = null;
unset($img);
}
else {
$func_name = "imagecreatefrom$img_type";
$img = @$func_name($img_url);
if ( !$img ) {
return;
}
imageinterlace($img, false);
}
$this->_pdf->addImagePng($img_url, $x, $this->y($y) - $h, $w, $h, $img);
if ( $img ) {
imagedestroy($img);
}
}
else {
//debugpng
if (DEBUGPNG) print "!!!$img_type addPngFromFile!!!";
$img_url = $this->_convert_gif_bmp_to_png($img_url, $img_type);
$this->_pdf->addPngFromFile($img_url, $x, $this->y($y) - $h, $w, $h);
}
break;
default:
//debugpng
if (DEBUGPNG) print '!!!unknown!!!';
break;
}
return;
}
//........................................................................
function text($x, $y, $text, $font, $size, $color = array(0,0,0), $word_space = 0, $char_space = 0, $angle = 0) {
$pdf = $this->_pdf;
$pdf->setColor($color);
$font .= ".afm";
$pdf->selectFont($font);
//Font_Metrics::get_font_height($font, $size) ==
//$this->get_font_height($font, $size) ==
//$this->_pdf->selectFont($font),$this->_pdf->getFontHeight($size)
//- FontBBoxheight+FontHeightOffset, scaled to $size, in pt
//$this->_pdf->getFontDescender($size)
//- Descender scaled to size
//
//$this->_pdf->fonts[$this->_pdf->currentFont] sizes:
//['FontBBox'][0] left, ['FontBBox'][1] bottom, ['FontBBox'][2] right, ['FontBBox'][3] top
//Maximum extent of all glyphs of the font from the baseline point
//['Ascender'] maximum height above baseline except accents
//['Descender'] maximum depth below baseline, negative number means below baseline
//['FontHeightOffset'] manual enhancement of .afm files to trim windows fonts. currently not used.
//Values are in 1/1000 pt for a font size of 1 pt
//
//['FontBBox'][1] should be close to ['Descender']
//['FontBBox'][3] should be close to ['Ascender']+Accents
//in practice, FontBBox values are a little bigger
//
//The text position is referenced to the baseline, not to the lower corner of the FontBBox,
//for what the left,top corner is given.
//FontBBox spans also the background box for the text.
//If the lower corner would be used as reference point, the Descents of the glyphs would
//hang over the background box border.
//Therefore compensate only the extent above the Baseline.
//
//print '<pre>['.$font.','.$size.','.$pdf->getFontHeight($size).','.$pdf->getFontDescender($size).','.$pdf->fonts[$pdf->currentFont]['FontBBox'][3].','.$pdf->fonts[$pdf->currentFont]['FontBBox'][1].','.$pdf->fonts[$pdf->currentFont]['FontHeightOffset'].','.$pdf->fonts[$pdf->currentFont]['Ascender'].','.$pdf->fonts[$pdf->currentFont]['Descender'].']</pre>';
//
//$pdf->addText($x, $this->y($y) - Font_Metrics::get_font_height($font, $size), $size, $text, $angle, $word_space, $char_space);
//$pdf->addText($x, $this->y($y) - $size, $size, $text, $angle, $word_space, $char_space);
//$pdf->addText($x, $this->y($y) - $pdf->getFontHeight($size)-$pdf->getFontDescender($size), $size, $text, $angle, $word_space, $char_space);
$pdf->addText($x, $this->y($y) - ($pdf->fonts[$pdf->currentFont]['FontBBox'][3]*$size)/1000, $size, $text, $angle, $word_space, $char_space);
}
//........................................................................
function javascript($code) {
$this->_pdf->addJavascript($code);
}
//........................................................................
/**
* Add a named destination (similar to <a name="foo">...</a> in html)
*
* @param string $anchorname The name of the named destination
*/
function add_named_dest($anchorname) {
$this->_pdf->addDestination($anchorname, "Fit");
}
//........................................................................
/**
* Add a link to the pdf
*
* @param string $url The url to link to
* @param float $x The x position of the link
* @param float $y The y position of the link
* @param float $width The width of the link
* @param float $height The height of the link
*/
function add_link($url, $x, $y, $width, $height) {
$y = $this->y($y) - $height;
if ( strpos($url, '#') === 0 ) {
// Local link
$name = substr($url,1);
if ( $name )
$this->_pdf->addInternalLink($name, $x, $y, $x + $width, $y + $height);
} else {
$this->_pdf->addLink(rawurldecode($url), $x, $y, $x + $width, $y + $height);
}
}
//........................................................................
function get_text_width($text, $font, $size, $word_spacing = 0, $char_spacing = 0) {
$this->_pdf->selectFont($font);
if (!DOMPDF_UNICODE_ENABLED) {
$text = mb_convert_encoding($text, 'Windows-1252', 'UTF-8');
}
return $this->_pdf->getTextWidth($size, $text, $word_spacing, $char_spacing);
}
//........................................................................
function get_font_height($font, $size) {
$this->_pdf->selectFont($font);
return $this->_pdf->getFontHeight($size) * DOMPDF_FONT_HEIGHT_RATIO;
}
//........................................................................
/**
* Writes text at the specified x and y coordinates on every page
*
* The strings '{PAGE_NUM}' and '{PAGE_COUNT}' are automatically replaced
* with their current values.
*
* See {@link Style::munge_colour()} for the format of the colour array.
*
* @param float $x
* @param float $y
* @param string $text the text to write
* @param string $font the font file to use
* @param float $size the font size, in points
* @param array $color
* @param float $adjust word spacing adjustment
* @param float $angle angle to write the text at, measured CW starting from the x-axis
*/
function page_text($x, $y, $text, $font, $size, $color = array(0,0,0), $adjust = 0, $angle = 0) {
$_t = "text";
$this->_page_text[] = compact("_t", "x", "y", "text", "font", "size", "color", "adjust", "angle");
}
//........................................................................
/**
* Processes a script on every page
*
* The variables $pdf, $PAGE_NUM, and $PAGE_COUNT are available.
*
* This function can be used to add page numbers to all pages
* after the first one, for example.
*
* @param string $code the script code
* @param string $type the language type for script
*/
function page_script($code, $type = "text/php") {
$_t = "script";
$this->_page_text[] = compact("_t", "code", "type");
}
//........................................................................
function new_page() {
$this->_page_number++;
$this->_page_count++;
$ret = $this->_pdf->newPage();
$this->_pages[] = $ret;
return $ret;
}
//........................................................................
/**
* Add text to each page after rendering is complete
*/
protected function _add_page_text() {
if ( !count($this->_page_text) )
return;
$page_number = 1;
$eval = null;
foreach ($this->_pages as $pid) {
$this->reopen_object($pid);
foreach ($this->_page_text as $pt) {
extract($pt);
switch ($_t) {
case "text":
$text = str_replace(array("{PAGE_NUM}","{PAGE_COUNT}"),
array($page_number, $this->_page_count), $text);
$this->text($x, $y, $text, $font, $size, $color, $adjust, $angle);
break;
case "script":
if (!$eval) {
$eval = new PHP_Evaluator($this);
}
$eval->evaluate($code, array('PAGE_NUM' => $page_number, 'PAGE_COUNT' => $this->_page_count));
break;
}
}
$this->close_object();
$page_number++;
}
}
/**
* Streams the PDF directly to the browser
*
* @param string $filename the name of the PDF file
* @param array $options associative array, 'Attachment' => 0 or 1, 'compress' => 1 or 0
*/
function stream($filename, $options = null) {
// Add page text
$this->_add_page_text();
$options["Content-Disposition"] = $filename;
$this->_pdf->stream($options);
}
//........................................................................
/**
* Returns the PDF as a string
*
* @param array $options Output options
* @return string
*/
function output($options = null) {
// Add page text
$this->_add_page_text();
$debug = isset($options["compress"]) && $options["compress"] != 1;
return $this->_pdf->output($debug);
}
//........................................................................
/**
* Returns logging messages generated by the Cpdf class
*
* @return string
*/
function get_messages() { return $this->_pdf->messages; }
}

287
pdf/include/css_color.cls.php Executable file
View File

@@ -0,0 +1,287 @@
<?php
/**
* DOMPDF - PHP5 HTML to PDF renderer
*
* File: $RCSfile$
* Created on: 2010-03-03
*
* Copyright (c) 2004 - Benj Carson <benjcarson@digitaljunkies.ca>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library in the file LICENSE.LGPL; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*
* Alternatively, you may distribute this software under the terms of the
* PHP License, version 3.0 or later. A copy of this license should have
* been distributed with this file in the file LICENSE.PHP . If this is not
* the case, you can obtain a copy at http://www.php.net/license/3_0.txt.
*
* The latest version of DOMPDF might be available at:
* http://www.dompdf.com/
*
* @link http://www.dompdf.com/
* @copyright 2004 Benj Carson
* @author Fabien M<>nager
* @package dompdf
*/
/* $Id: css_color.cls.php 300 2010-08-11 22:01:09Z fabien.menager $ */
class CSS_Color {
static $cssColorNames = array(
"aliceblue" => "F0F8FF",
"antiquewhite" => "FAEBD7",
"aqua" => "00FFFF",
"aquamarine" => "7FFFD4",
"azure" => "F0FFFF",
"beige" => "F5F5DC",
"bisque" => "FFE4C4",
"black" => "000000",
"blanchedalmond" => "FFEBCD",
"blue" => "0000FF",
"blueviolet" => "8A2BE2",
"brown" => "A52A2A",
"burlywood" => "DEB887",
"cadetblue" => "5F9EA0",
"chartreuse" => "7FFF00",
"chocolate" => "D2691E",
"coral" => "FF7F50",
"cornflowerblue" => "6495ED",
"cornsilk" => "FFF8DC",
"crimson" => "DC143C",
"cyan" => "00FFFF",
"darkblue" => "00008B",
"darkcyan" => "008B8B",
"darkgoldenrod" => "B8860B",
"darkgray" => "A9A9A9",
"darkgreen" => "006400",
"darkgrey" => "A9A9A9",
"darkkhaki" => "BDB76B",
"darkmagenta" => "8B008B",
"darkolivegreen" => "556B2F",
"darkorange" => "FF8C00",
"darkorchid" => "9932CC",
"darkred" => "8B0000",
"darksalmon" => "E9967A",
"darkseagreen" => "8FBC8F",
"darkslateblue" => "483D8B",
"darkslategray" => "2F4F4F",
"darkslategrey" => "2F4F4F",
"darkturquoise" => "00CED1",
"darkviolet" => "9400D3",
"deeppink" => "FF1493",
"deepskyblue" => "00BFFF",
"dimgray" => "696969",
"dimgrey" => "696969",
"dodgerblue" => "1E90FF",
"firebrick" => "B22222",
"floralwhite" => "FFFAF0",
"forestgreen" => "228B22",
"fuchsia" => "FF00FF",
"gainsboro" => "DCDCDC",
"ghostwhite" => "F8F8FF",
"gold" => "FFD700",
"goldenrod" => "DAA520",
"gray" => "808080",
"green" => "008000",
"greenyellow" => "ADFF2F",
"grey" => "808080",
"honeydew" => "F0FFF0",
"hotpink" => "FF69B4",
"indianred" => "CD5C5C",
"indigo" => "4B0082",
"ivory" => "FFFFF0",
"khaki" => "F0E68C",
"lavender" => "E6E6FA",
"lavenderblush" => "FFF0F5",
"lawngreen" => "7CFC00",
"lemonchiffon" => "FFFACD",
"lightblue" => "ADD8E6",
"lightcoral" => "F08080",
"lightcyan" => "E0FFFF",
"lightgoldenrodyellow" => "FAFAD2",
"lightgray" => "D3D3D3",
"lightgreen" => "90EE90",
"lightgrey" => "D3D3D3",
"lightpink" => "FFB6C1",
"lightsalmon" => "FFA07A",
"lightseagreen" => "20B2AA",
"lightskyblue" => "87CEFA",
"lightslategray" => "778899",
"lightslategrey" => "778899",
"lightsteelblue" => "B0C4DE",
"lightyellow" => "FFFFE0",
"lime" => "00FF00",
"limegreen" => "32CD32",
"linen" => "FAF0E6",
"magenta" => "FF00FF",
"maroon" => "800000",
"mediumaquamarine" => "66CDAA",
"mediumblue" => "0000CD",
"mediumorchid" => "BA55D3",
"mediumpurple" => "9370DB",
"mediumseagreen" => "3CB371",
"mediumslateblue" => "7B68EE",
"mediumspringgreen" => "00FA9A",
"mediumturquoise" => "48D1CC",
"mediumvioletred" => "C71585",
"midnightblue" => "191970",
"mintcream" => "F5FFFA",
"mistyrose" => "FFE4E1",
"moccasin" => "FFE4B5",
"navajowhite" => "FFDEAD",
"navy" => "000080",
"oldlace" => "FDF5E6",
"olive" => "808000",
"olivedrab" => "6B8E23",
"orange" => "FFA500",
"orangered" => "FF4500",
"orchid" => "DA70D6",
"palegoldenrod" => "EEE8AA",
"palegreen" => "98FB98",
"paleturquoise" => "AFEEEE",
"palevioletred" => "DB7093",
"papayawhip" => "FFEFD5",
"peachpuff" => "FFDAB9",
"peru" => "CD853F",
"pink" => "FFC0CB",
"plum" => "DDA0DD",
"powderblue" => "B0E0E6",
"purple" => "800080",
"red" => "FF0000",
"rosybrown" => "BC8F8F",
"royalblue" => "4169E1",
"saddlebrown" => "8B4513",
"salmon" => "FA8072",
"sandybrown" => "F4A460",
"seagreen" => "2E8B57",
"seashell" => "FFF5EE",
"sienna" => "A0522D",
"silver" => "C0C0C0",
"skyblue" => "87CEEB",
"slateblue" => "6A5ACD",
"slategray" => "708090",
"slategrey" => "708090",
"snow" => "FFFAFA",
"springgreen" => "00FF7F",
"steelblue" => "4682B4",
"tan" => "D2B48C",
"teal" => "008080",
"thistle" => "D8BFD8",
"tomato" => "FF6347",
"turquoise" => "40E0D0",
"violet" => "EE82EE",
"wheat" => "F5DEB3",
"white" => "FFFFFF",
"whitesmoke" => "F5F5F5",
"yellow" => "FFFF00",
"yellowgreen" => "9ACD32",
);
static function parse($colour) {
if ( is_array($colour) )
// Assume the array has the right format...
// FIXME: should/could verify this.
return $colour;
$colour = strtolower($colour);
if (isset(self::$cssColorNames[$colour]))
return self::getArray(self::$cssColorNames[$colour]);
if ($colour === "transparent")
return "transparent";
$length = mb_strlen($colour);
// #rgb format
if ( $length == 4 && $colour[0] === "#" ) {
return self::getArray($colour[1].$colour[1].$colour[2].$colour[2].$colour[3].$colour[3]);
// #rrggbb format
} else if ( $length == 7 && $colour[0] === "#" ) {
return self::getArray(mb_substr($colour, 1, 6));
// rgb( r,g,b ) format
} else if ( mb_strpos($colour, "rgb") !== false ) {
$i = mb_strpos($colour, "(");
$j = mb_strpos($colour, ")");
// Bad colour value
if ($i === false || $j === false)
return null;
$triplet = explode(",", mb_substr($colour, $i+1, $j-$i-1));
if (count($triplet) != 3)
return null;
foreach (array_keys($triplet) as $c) {
$triplet[$c] = trim($triplet[$c]);
if ( $triplet[$c][mb_strlen($triplet[$c]) - 1] === "%" )
$triplet[$c] = round($triplet[$c] * 2.55);
}
return self::getArray(vsprintf("%02X%02X%02X", $triplet));
// cmyk( c,m,y,k ) format
// http://www.w3.org/TR/css3-gcpm/#cmyk-colors
} else if ( mb_strpos($colour, "cmyk") !== false ) {
$i = mb_strpos($colour, "(");
$j = mb_strpos($colour, ")");
// Bad colour value
if ($i === false || $j === false)
return null;
$values = explode(",", mb_substr($colour, $i+1, $j-$i-1));
if (count($values) != 4)
return null;
foreach ($values as &$c) {
$c = floatval(trim($c));
if ($c > 1.0) $c = 1.0;
if ($c < 0.0) $c = 0.0;
}
return self::getArray($values);
}
}
static function getArray($colour) {
$c = array(null, null, null, null, "hex" => null);
if (is_array($colour)) {
$c = $colour;
$c["c"] = $c[0];
$c["m"] = $c[1];
$c["y"] = $c[2];
$c["k"] = $c[3];
$c["hex"] = "cmyk($c[0],$c[1],$c[2],$c[3])";
}
else {
$c[0] = hexdec(mb_substr($colour, 0, 2)) / 0xff;
$c[1] = hexdec(mb_substr($colour, 2, 2)) / 0xff;
$c[2] = hexdec(mb_substr($colour, 4, 2)) / 0xff;
$c["r"] = $c[0];
$c["g"] = $c[1];
$c["b"] = $c[2];
$c["hex"] = "#$colour";
}
return $c;
}
}

792
pdf/include/dompdf.cls.php Executable file
View File

@@ -0,0 +1,792 @@
<?php
/**
* DOMPDF - PHP5 HTML to PDF renderer
*
* File: $RCSfile: dompdf.cls.php,v $
* Created on: 2004-06-09
*
* Copyright (c) 2004 - Benj Carson <benjcarson@digitaljunkies.ca>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library in the file LICENSE.LGPL; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*
* Alternatively, you may distribute this software under the terms of the
* PHP License, version 3.0 or later. A copy of this license should have
* been distributed with this file in the file LICENSE.PHP . If this is not
* the case, you can obtain a copy at http://www.php.net/license/3_0.txt.
*
* The latest version of DOMPDF might be available at:
* http://www.dompdf.com/
*
* @link http://www.dompdf.com/
* @copyright 2004 Benj Carson
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @package dompdf
*/
/* $Id: dompdf.cls.php 362 2011-02-16 22:17:28Z fabien.menager $ */
/**
* DOMPDF - PHP5 HTML to PDF renderer
*
* DOMPDF loads HTML and does its best to render it as a PDF. It gets its
* name from the new DomDocument PHP5 extension. Source HTML is first
* parsed by a DomDocument object. DOMPDF takes the resulting DOM tree and
* attaches a {@link Frame} object to each node. {@link Frame} objects store
* positioning and layout information and each has a reference to a {@link
* Style} object.
*
* Style information is loaded and parsed (see {@link Stylesheet}) and is
* applied to the frames in the tree by using XPath. CSS selectors are
* converted into XPath queries, and the computed {@link Style} objects are
* applied to the {@link Frame}s.
*
* {@link Frame}s are then decorated (in the design pattern sense of the
* word) based on their CSS display property ({@link
* http://www.w3.org/TR/CSS21/visuren.html#propdef-display}).
* Frame_Decorators augment the basic {@link Frame} class by adding
* additional properties and methods specific to the particular type of
* {@link Frame}. For example, in the CSS layout model, block frames
* (display: block;) contain line boxes that are usually filled with text or
* other inline frames. The Block_Frame_Decorator therefore adds a $lines
* property as well as methods to add {@link Frame}s to lines and to add
* additional lines. {@link Frame}s also are attached to specific
* Positioner and {@link Frame_Reflower} objects that contain the
* positioining and layout algorithm for a specific type of frame,
* respectively. This is an application of the Strategy pattern.
*
* Layout, or reflow, proceeds recursively (post-order) starting at the root
* of the document. Space constraints (containing block width & height) are
* pushed down, and resolved positions and sizes bubble up. Thus, every
* {@link Frame} in the document tree is traversed once (except for tables
* which use a two-pass layout algorithm). If you are interested in the
* details, see the reflow() method of the Reflower classes.
*
* Rendering is relatively straightforward once layout is complete. {@link
* Frame}s are rendered using an adapted {@link Cpdf} class, originally
* written by Wayne Munro, http://www.ros.co.nz/pdf/. (Some performance
* related changes have been made to the original {@link Cpdf} class, and
* the {@link CPDF_Adapter} class provides a simple, stateless interface to
* PDF generation.) PDFLib support has now also been added, via the {@link
* PDFLib_Adapter}.
*
*
* @package dompdf
*/
class DOMPDF {
/**
* DomDocument representing the HTML document
*
* @var DomDocument
*/
protected $_xml;
/**
* Frame_Tree derived from the DOM tree
*
* @var Frame_Tree
*/
protected $_tree;
/**
* Stylesheet for the document
*
* @var Stylesheet
*/
protected $_css;
/**
* Actual PDF renderer
*
* @var Canvas
*/
protected $_pdf;
/**
* Desired paper size ('letter', 'legal', 'A4', etc.)
*
* @var string
*/
protected $_paper_size;
/**
* Paper orientation ('portrait' or 'landscape')
*
* @var string
*/
protected $_paper_orientation;
/**
* Callbacks on new page and new element
*
* @var array
*/
protected $_callbacks;
/**
* Experimental caching capability
*
* @var string
*/
private $_cache_id;
/**
* Base hostname
*
* Used for relative paths/urls
* @var string
*/
protected $_base_host;
/**
* Absolute base path
*
* Used for relative paths/urls
* @var string
*/
protected $_base_path;
/**
* Protcol used to request file (file://, http://, etc)
*
* @var string
*/
protected $_protocol;
/**
* Timestamp of the script start time
*
* @var int
*/
private $_start_time = null;
/**
* @var string The system's locale
*/
private $_system_locale = null;
/**
* @var bool Tells if the system's locale is the C standard one
*/
private $_locale_standard = false;
/**
* Class constructor
*/
function __construct() {
$this->_locale_standard = sprintf('%.1f', 1.0) == '1.0';
$this->save_locale();
$this->_messages = array();
$this->_xml = new DOMDocument();
$this->_xml->preserveWhiteSpace = true;
$this->_tree = new Frame_Tree($this->_xml);
$this->_css = new Stylesheet();
$this->_pdf = null;
$this->_paper_size = "letter";
$this->_paper_orientation = "portrait";
$this->_base_protocol = "";
$this->_base_host = "";
$this->_base_path = "";
$this->_callbacks = array();
$this->_cache_id = null;
$this->restore_locale();
}
/**
* Class destructor
*/
function __destruct() {
clear_object($this);
}
/**
* Save the system's locale configuration and
* set the right value for numeric formatting
*/
private function save_locale() {
if ( $this->_locale_standard ) return;
$this->_system_locale = setlocale(LC_NUMERIC, "C");
}
/**
* Restore the system's locale configuration
*/
private function restore_locale() {
if ( $this->_locale_standard ) return;
setlocale(LC_NUMERIC, $this->_system_locale);
}
/**
* Returns the underlying {@link Frame_Tree} object
*
* @return Frame_Tree
*/
function get_tree() { return $this->_tree; }
/**
* Sets the protocol to use
* FIXME validate these
*
* @param string $proto
*/
function set_protocol($proto) { $this->_protocol = $proto; }
/**
* Sets the base hostname
*
* @param string $host
*/
function set_host($host) { $this->_base_host = $host; }
/**
* Sets the base path
*
* @param string $path
*/
function set_base_path($path) { $this->_base_path = $path; }
/**
* Returns the protocol in use
*
* @return string
*/
function get_protocol() { return $this->_protocol; }
/**
* Returns the base hostname
*
* @return string
*/
function get_host() { return $this->_base_host; }
/**
* Returns the base path
*
* @return string
*/
function get_base_path() { return $this->_base_path; }
/**
* Return the underlying Canvas instance (e.g. CPDF_Adapter, GD_Adapter)
*
* @return Canvas
*/
function get_canvas() { return $this->_pdf; }
/**
* Returns the callbacks array
*
* @return array
*/
function get_callbacks() { return $this->_callbacks; }
/**
* Loads an HTML file
*
* Parse errors are stored in the global array _dompdf_warnings.
*
* @param string $file a filename or url to load
*/
function load_html_file($file) {
$this->save_locale();
// Store parsing warnings as messages (this is to prevent output to the
// browser if the html is ugly and the dom extension complains,
// preventing the pdf from being streamed.)
if ( !$this->_protocol && !$this->_base_host && !$this->_base_path )
list($this->_protocol, $this->_base_host, $this->_base_path) = explode_url($file);
if ( !DOMPDF_ENABLE_REMOTE &&
($this->_protocol != "" && $this->_protocol !== "file://" ) )
throw new DOMPDF_Exception("Remote file requested, but DOMPDF_ENABLE_REMOTE is false.");
if ($this->_protocol == "" || $this->_protocol === "file://") {
$realfile = realpath($file);
if ( !$file )
throw new DOMPDF_Exception("File '$file' not found.");
if ( strpos($realfile, DOMPDF_CHROOT) !== 0 )
throw new DOMPDF_Exception("Permission denied on $file.");
// Exclude dot files (e.g. .htaccess)
if ( substr(basename($realfile),0,1) === "." )
throw new DOMPDF_Exception("Permission denied on $file.");
$file = $realfile;
}
$contents = file_get_contents($file);
$encoding = null;
// See http://the-stickman.com/web-development/php/getting-http-response-headers-when-using-file_get_contents/
if ( isset($http_response_header) ) {
foreach($http_response_header as $_header) {
if ( preg_match("@Content-Type:\s*[\w/]+;\s*?charset=([^\s]+)@i", $_header, $matches) ) {
$encoding = strtoupper($matches[1]);
break;
}
}
}
$this->restore_locale();
$this->load_html($contents, $encoding);
}
/**
* Loads an HTML string
*
* Parse errors are stored in the global array _dompdf_warnings.
*
* @param string $str HTML text to load
*/
function load_html($str, $encoding = null) {
$this->save_locale();
// TODO: use the $encoding variable
// FIXME: Determine character encoding, switch to UTF8, update meta tag. Need better http/file stream encoding detection, currently relies on text or meta tag.
mb_detect_order('auto');
if (mb_detect_encoding($str) !== 'UTF-8') {
$metatags = array(
'@<meta\s+http-equiv="Content-Type"\s+content="(?:[\w/]+)(?:;\s*?charset=([^\s"]+))?@i',
'@<meta\s+content="(?:[\w/]+)(?:;\s*?charset=([^\s"]+))"?\s+http-equiv="Content-Type"@i',
);
foreach($metatags as $metatag) {
if (preg_match($metatag, $str, $matches)) break;
}
if (mb_detect_encoding($str) == '') {
if (isset($matches[1])) {
$encoding = strtoupper($matches[1]);
} else {
$encoding = 'UTF-8';
}
} else {
if (isset($matches[1])) {
$encoding = strtoupper($matches[1]);
} else {
$encoding = 'auto';
}
}
if ($encoding !== 'UTF-8') {
$str = mb_convert_encoding($str, 'UTF-8', $encoding);
}
if (isset($matches[1])) {
$str = preg_replace('/charset=([^\s"]+)/i','charset=UTF-8', $str);
} else {
$str = str_replace('<head>', '<head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8">', $str);
}
}
// Parse embedded php, first-pass
if ( DOMPDF_ENABLE_PHP ) {
ob_start();
eval("?" . ">$str");
$str = ob_get_clean();
}
// if the document contains non utf-8 with a utf-8 meta tag chars and was
// detected as utf-8 by mbstring, problems could happen.
// http://devzone.zend.com/article/8855
if ( $encoding === 'UTF-8' ) {
$str = preg_replace("/<meta([^>]+)>/", "", $str);
}
// Store parsing warnings as messages
set_error_handler("record_warnings");
$this->_xml->loadHTML($str);
restore_error_handler();
/**
@todo Take the quirksmode into account
// http://hsivonen.iki.fi/doctype/
// https://developer.mozilla.org/en/mozilla's_quirks_mode
$quirksmode = false;
// HTML5 <!DOCTYPE html>
if ( !$this->_xml->doctype->publicId && !$this->_xml->doctype->systemId ) {
$quirksmode = false;
}
// not XHTML
if ( !preg_match("/xhtml/i", $this->_xml->doctype->publicId) ) {
$quirksmode = true;
}
*/
$this->restore_locale();
}
/**
* Builds the {@link Frame_Tree}, loads any CSS and applies the styles to
* the {@link Frame_Tree}
*/
protected function _process_html() {
$this->save_locale();
$this->_tree->build_tree();
$this->_css->load_css_file(Stylesheet::DEFAULT_STYLESHEET);
$acceptedmedia = Stylesheet::$ACCEPTED_GENERIC_MEDIA_TYPES;
if ( defined("DOMPDF_DEFAULT_MEDIA_TYPE") ) {
$acceptedmedia[] = DOMPDF_DEFAULT_MEDIA_TYPE;
} else {
$acceptedmedia[] = Stylesheet::$ACCEPTED_DEFAULT_MEDIA_TYPE;
}
// load <link rel="STYLESHEET" ... /> tags
$links = $this->_xml->getElementsByTagName("link");
foreach ($links as $link) {
if ( mb_strtolower($link->getAttribute("rel")) === "stylesheet" ||
mb_strtolower($link->getAttribute("type")) === "text/css" ) {
//Check if the css file is for an accepted media type
//media not given then always valid
$formedialist = preg_split("/[\s\n,]/", $link->getAttribute("media"),-1, PREG_SPLIT_NO_EMPTY);
if ( count($formedialist) > 0 ) {
$accept = false;
foreach ( $formedialist as $type ) {
if ( in_array(mb_strtolower(trim($type)), $acceptedmedia) ) {
$accept = true;
break;
}
}
if (!$accept) {
//found at least one mediatype, but none of the accepted ones
//Skip this css file.
continue;
}
}
$url = $link->getAttribute("href");
$url = build_url($this->_protocol, $this->_base_host, $this->_base_path, $url);
$this->_css->load_css_file($url);
}
}
// load <style> tags
$styles = $this->_xml->getElementsByTagName("style");
foreach ($styles as $style) {
// Accept all <style> tags by default (note this is contrary to W3C
// HTML 4.0 spec:
// http://www.w3.org/TR/REC-html40/present/styles.html#adef-media
// which states that the default media type is 'screen'
if ( $style->hasAttributes() &&
($media = $style->getAttribute("media")) &&
!in_array($media, $acceptedmedia) )
continue;
$css = "";
if ( $style->hasChildNodes() ) {
$child = $style->firstChild;
while ( $child ) {
$css .= $child->nodeValue; // Handle <style><!-- blah --></style>
$child = $child->nextSibling;
}
} else
$css = $style->nodeValue;
// Set the base path of the Stylesheet to that of the file being processed
$this->_css->set_protocol($this->_protocol);
$this->_css->set_host($this->_base_host);
$this->_css->set_base_path($this->_base_path);
$this->_css->load_css($css);
}
$this->restore_locale();
}
/**
* Sets the paper size & orientation
*
* @param string $size 'letter', 'legal', 'A4', etc. {@link CPDF_Adapter::$PAPER_SIZES}
* @param string $orientation 'portrait' or 'landscape'
*/
function set_paper($size, $orientation = "portrait") {
$this->_paper_size = $size;
$this->_paper_orientation = $orientation;
}
/**
* Enable experimental caching capability
* @access private
*/
function enable_caching($cache_id) {
$this->_cache_id = $cache_id;
}
/**
* Sets callbacks for events like rendering of pages and elements.
* The callbacks array contains arrays with 'event' set to 'begin_page',
* 'end_page', 'begin_frame', or 'end_frame' and 'f' set to a function or
* object plus method to be called.
*
* The function 'f' must take an array as argument, which contains info
* about the event.
*
* @param array $callbacks the set of callbacks to set
*/
function set_callbacks($callbacks) {
if (is_array($callbacks)) {
$this->_callbacks = array();
foreach ($callbacks as $c) {
if (is_array($c) && isset($c['event']) && isset($c['f'])) {
$event = $c['event'];
$f = $c['f'];
if (is_callable($f) && is_string($event)) {
$this->_callbacks[$event][] = $f;
}
}
}
}
}
/**
* Renders the HTML to PDF
*/
function render() {
$this->save_locale();
if ( DOMPDF_LOG_OUTPUT_FILE ) {
if ( !file_exists(DOMPDF_LOG_OUTPUT_FILE) && is_writable(dirname(DOMPDF_LOG_OUTPUT_FILE)) ) {
touch(DOMPDF_LOG_OUTPUT_FILE);
}
$this->_start_time = microtime(true);
ob_start();
}
//enable_mem_profile();
$this->_process_html();
$this->_css->apply_styles($this->_tree);
$root = null;
foreach ($this->_tree->get_frames() as $frame) {
// Set up the root frame
if ( is_null($root) ) {
$root = Frame_Factory::decorate_root( $this->_tree->get_root(), $this );
continue;
}
// Create the appropriate decorators, reflowers & positioners.
$deco = Frame_Factory::decorate_frame($frame, $this);
$deco->set_root($root);
// FIXME: handle generated content
if ( $frame->get_style()->display === "list-item" ) {
// Insert a list-bullet frame
$node = $this->_xml->createElement("bullet"); // arbitrary choice
$b_f = new Frame($node);
$parent_node = $frame->get_parent()->get_node();
if ( !$parent_node->hasAttribute("dompdf-children-count") ) {
$count = 0;
foreach ($parent_node->childNodes as $_node) {
if ( $_node instanceof DOMElement )
$count++;
}
$parent_node->setAttribute("dompdf-children-count", $count);
}
$index = 0;
if ( !$parent_node->hasAttribute("dompdf-counter") ) {
$index = 1;
$parent_node->setAttribute("dompdf-counter", 1);
}
else {
$index = $parent_node->getAttribute("dompdf-counter");
$index++;
$parent_node->setAttribute("dompdf-counter", $index);
}
$node->setAttribute("dompdf-counter", $index);
$style = $this->_css->create_style();
$style->display = "-dompdf-list-bullet";
$style->inherit($frame->get_style());
$b_f->set_style($style);
$deco->prepend_child( Frame_Factory::decorate_frame($b_f, $this) );
}
}
$page_style = $this->_css->get_page_style();
if ( $page_style && is_array($page_style->size) ) {
$this->set_paper(array(0, 0, $page_style->size[0], $page_style->size[1]));
}
$this->_pdf = Canvas_Factory::get_instance($this->_paper_size, $this->_paper_orientation);
// Add meta information
$title = $this->_xml->getElementsByTagName("title");
if ( $title->length ) {
$this->_pdf->add_info("Title", trim($title->item(0)->nodeValue));
}
$metas = $this->_xml->getElementsByTagName("meta");
$labels = array(
"author" => "Author",
"keywords" => "Keywords",
"description" => "Subject",
);
foreach($metas as $meta) {
$name = mb_strtolower($meta->getAttribute("name"));
if ( isset($labels[$name]) ) {
$this->_pdf->add_info($labels[$name], trim($meta->getAttribute("content")));
}
}
$root->set_containing_block(0, 0, $this->_pdf->get_width(), $this->_pdf->get_height());
$root->set_renderer(new Renderer($this));
// This is where the magic happens:
$root->reflow();
// Clean up cached images
Image_Cache::clear();
global $_dompdf_warnings, $_dompdf_show_warnings;
if ( $_dompdf_show_warnings ) {
echo '<b>DOMPDF Warnings</b><br><pre>';
foreach ($_dompdf_warnings as $msg)
echo $msg . "\n";
echo $this->get_canvas()->get_cpdf()->messages;
echo '</pre>';
flush();
}
$this->restore_locale();
}
/**
* Add meta information to the PDF after rendering
*/
function add_info($label, $value) {
if (!is_null($this->_pdf))
$this->_pdf->add_info($label, $value);
}
/**
* Writes the output buffer in the log file
* @return void
*/
private function write_log() {
if ( !DOMPDF_LOG_OUTPUT_FILE || !is_writable(DOMPDF_LOG_OUTPUT_FILE) ) return;
$memory = DOMPDF_memory_usage();
$memory = number_format($memory/1024);
$time = number_format((microtime(true) - $this->_start_time) * 1000, 4);
$out = "<span style='color: #900'>$memory KB</span> ".
"<span style='color: #090'>$time ms</span><br />";
$out .= ob_get_clean();
file_put_contents(DOMPDF_LOG_OUTPUT_FILE, $out);
}
/**
* Streams the PDF to the client
*
* The file will open a download dialog by default. The options
* parameter controls the output. Accepted options are:
*
* 'Accept-Ranges' => 1 or 0 - if this is not set to 1, then this
* header is not included, off by default this header seems to
* have caused some problems despite the fact that it is supposed
* to solve them, so I am leaving it off by default.
*
* 'compress' = > 1 or 0 - apply content stream compression, this is
* on (1) by default
*
* 'Attachment' => 1 or 0 - if 1, force the browser to open a
* download dialog, on (1) by default
*
* @param string $filename the name of the streamed file
* @param array $options header options (see above)
*/
function stream($filename, $options = null) {
$this->save_locale();
$this->write_log();
if (!is_null($this->_pdf))
$this->_pdf->stream($filename, $options);
$this->restore_locale();
}
/**
* Returns the PDF as a string
*
* The file will open a download dialog by default. The options
* parameter controls the output. Accepted options are:
*
*
* 'compress' = > 1 or 0 - apply content stream compression, this is
* on (1) by default
*
*
* @param array $options options (see above)
* @return string
*/
function output($options = null) {
$this->save_locale();
$this->write_log();
if ( is_null($this->_pdf) )
return null;
$output = $this->_pdf->output( $options );
$this->restore_locale();
return $output;
}
/**
* Returns the underlying HTML document as a string
*
* @return string
*/
function output_html() {
return $this->_xml->saveHTML();
}
}

View File

@@ -0,0 +1,60 @@
<?php
/**
* DOMPDF - PHP5 HTML to PDF renderer
*
* File: $RCSfile: dompdf_exception.cls.php,v $
* Created on: 2004-08-07
*
* Copyright (c) 2004 - Benj Carson <benjcarson@digitaljunkies.ca>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library in the file LICENSE.LGPL; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*
* Alternatively, you may distribute this software under the terms of the
* PHP License, version 3.0 or later. A copy of this license should have
* been distributed with this file in the file LICENSE.PHP . If this is not
* the case, you can obtain a copy at http://www.php.net/license/3_0.txt.
*
* The latest version of DOMPDF might be available at:
* http://www.dompdf.com/
*
* @link http://www.dompdf.com/
* @copyright 2004 Benj Carson
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @package dompdf
*/
/* $Id: dompdf_exception.cls.php 216 2010-03-11 22:49:18Z ryan.masten $ */
/**
* Standard exception thrown by DOMPDF classes
*
* @link http://www.zend.com/php5/articles/engine2-php5-changes.php#Heading12
* @package dompdf
*/
class DOMPDF_Exception extends Exception {
/**
* Class constructor
*
* @param string $message Error message
* @param int $code Error code
*/
function __construct($message = NULL, $code = 0) {
parent::__construct($message, $code);
}
}

View File

@@ -0,0 +1,59 @@
<?php
/**
* DOMPDF - PHP5 HTML to PDF renderer
*
* File: $RCSfile: dompdf_internal_exception.cls.php,v $
* Created on: 2004-08-07
*
* Copyright (c) 2004 - Benj Carson <benjcarson@digitaljunkies.ca>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library in the file LICENSE.LGPL; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*
* Alternatively, you may distribute this software under the terms of the
* PHP License, version 3.0 or later. A copy of this license should have
* been distributed with this file in the file LICENSE.PHP . If this is not
* the case, you can obtain a copy at http://www.php.net/license/3_0.txt.
*
* The latest version of DOMPDF might be available at:
* http://www.dompdf.com/
*
* @link http://www.dompdf.com/
* @copyright 2004 Benj Carson
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @package dompdf
*/
/* $Id: dompdf_internal_exception.cls.php 216 2010-03-11 22:49:18Z ryan.masten $ */
/**
* Exception thrown by DOMPDF classes for internal errors
*
* @package dompdf
*/
class DOMPDF_Internal_Exception extends Exception {
/**
* Class constructor
*
* @param string $message Error message
* @param int $code Error code
*/
function __construct($message = NULL, $code = 0) {
parent::__construct($message, $code);
}
}

40
pdf/include/file.skel Executable file
View File

@@ -0,0 +1,40 @@
<?php
/**
* DOMPDF - PHP5 HTML to PDF renderer
*
* File: $RCSfile: file.skel,v $
* Created on: 2004-08-04
*
* Copyright (c) 2004 - Benj Carson <benjcarson@digitaljunkies.ca>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library in the file LICENSE.LGPL; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*
* Alternatively, you may distribute this software under the terms of the
* PHP License, version 3.0 or later. A copy of this license should have
* been distributed with this file in the file LICENSE.PHP . If this is not
* the case, you can obtain a copy at http://www.php.net/license/3_0.txt.
*
* The latest version of DOMPDF might be available at:
* http://www.dompdf.com/
*
* @link http://www.dompdf.com/
* @copyright 2004 Benj Carson
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @package dompdf
* @version 0.3
*/
/* $Id: file.skel 216 2010-03-11 22:49:18Z ryan.masten $ */

View File

@@ -0,0 +1,117 @@
<?php
/**
* DOMPDF - PHP5 HTML to PDF renderer
*
* File: $RCSfile: absolute_positioner.cls.php,v $
* Created on: 2004-06-08
*
* Copyright (c) 2004 - Benj Carson <benjcarson@digitaljunkies.ca>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library in the file LICENSE.LGPL; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*
* Alternatively, you may distribute this software under the terms of the
* PHP License, version 3.0 or later. A copy of this license should have
* been distributed with this file in the file LICENSE.PHP . If this is not
* the case, you can obtain a copy at http://www.php.net/license/3_0.txt.
*
* The latest version of DOMPDF might be available at:
* http://www.dompdf.com/
*
* @link http://www.dompdf.com/
* @copyright 2004 Benj Carson
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @package dompdf
*/
/* $Id */
/**
* Positions fixely positioned frames
*/
class Fixed_Positioner extends Positioner {
function __construct(Frame_Decorator $frame) { parent::__construct($frame); }
function position() {
$frame = $this->_frame;
$style = $frame->get_original_style();
$root = $frame->get_root();
$initialcb = $root->get_containing_block();
$initialcb_style = $root->get_style();
$p = $frame->find_block_parent();
if ( $p ) {
$p->add_line();
}
// Compute the margins of the @page style
$margin_top = $initialcb_style->length_in_pt($initialcb_style->margin_top, $initialcb["h"]);
$margin_right = $initialcb_style->length_in_pt($initialcb_style->margin_right, $initialcb["w"]);
$margin_bottom = $initialcb_style->length_in_pt($initialcb_style->margin_bottom, $initialcb["h"]);
$margin_left = $initialcb_style->length_in_pt($initialcb_style->margin_left, $initialcb["w"]);
// The needed computed style of the element
$height = $style->length_in_pt($style->height, $initialcb["h"]);
$width = $style->length_in_pt($style->width, $initialcb["w"]);
$top = $style->length_in_pt($style->top, $initialcb["h"]);
$right = $style->length_in_pt($style->right, $initialcb["w"]);
$bottom = $style->length_in_pt($style->bottom, $initialcb["h"]);
$left = $style->length_in_pt($style->left, $initialcb["w"]);
$y = $margin_top;
if ( isset($top) ) {
$y = $top + $margin_top;
if ( $top === "auto" ) {
$y = $margin_top;
if ( isset($bottom) && $bottom !== "auto" ) {
$y = $initialcb["h"] - $bottom - $margin_bottom;
$margin_height = $this->_frame->get_margin_height();
if ( $margin_height !== "auto" ) {
$y -= $margin_height;
} else {
$y -= $height;
}
}
}
}
$x = $margin_left;
if ( isset($left) ) {
$x = $left + $margin_left;
if ( $left === "auto" ) {
$x = $margin_left;
if ( isset($right) && $right !== "auto" ) {
$x = $initialcb["w"] - $right - $margin_right;
$margin_width = $this->_frame->get_margin_width();
if ( $margin_width !== "auto" ) {
$x -= $margin_width;
} else {
$x -= $width;
}
}
}
}
$frame->set_position($x, $y);
$children = $frame->get_children();
foreach($children as $child) {
$child->set_position($x, $y);
}
}
}

299
pdf/include/font_metrics.cls.php Executable file
View File

@@ -0,0 +1,299 @@
<?php
/**
* DOMPDF - PHP5 HTML to PDF renderer
*
* File: $RCSfile: font_metrics.cls.php,v $
* Created on: 2004-06-02
*
* Copyright (c) 2004 - Benj Carson <benjcarson@digitaljunkies.ca>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library in the file LICENSE.LGPL; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*
* Alternatively, you may distribute this software under the terms of the
* PHP License, version 3.0 or later. A copy of this license should have
* been distributed with this file in the file LICENSE.PHP . If this is not
* the case, you can obtain a copy at http://www.php.net/license/3_0.txt.
*
* The latest version of DOMPDF might be available at:
* http://www.dompdf.com/
*
* @link http://www.dompdf.com/
* @copyright 2004 Benj Carson
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @contributor Helmut Tischer <htischer@weihenstephan.org>
* @package dompdf
*
* Changes
* @contributor Helmut Tischer <htischer@weihenstephan.org>
* @version 0.5.1.htischer.20090507
* - On missing font on explicite font selection don't change subtype and don't return default font.
* - On requesting default font and missing subtype, check similar subtypes, then any subtype, then normal. The last must exist.
* - Add comments
*/
/* $Id: font_metrics.cls.php 355 2011-01-27 07:44:54Z fabien.menager $ */
require_once(DOMPDF_LIB_DIR . "/class.pdf.php");
/**
* Name of the font cache file
*
* This file must be writable by the webserver process only to update it
* with save_font_families() after adding the .afm file references of a new font family
* with Font_Metrics::save_font_families().
* This is typically done only from command line with load_font.php on converting
* ttf fonts to afm with an external tool referenced in the define _TTF2AFM
*
* Declared here because PHP5 prevents constants from being declared with expressions
*/
if (!defined("__DOMPDF_FONT_CACHE_FILE")) {
if (file_exists(DOMPDF_FONT_DIR . "dompdf_font_family_cache")) {
define('__DOMPDF_FONT_CACHE_FILE', DOMPDF_FONT_DIR . "dompdf_font_family_cache");
} else {
define('__DOMPDF_FONT_CACHE_FILE', DOMPDF_FONT_DIR . "dompdf_font_family_cache.dist.php");
}
}
/**
* The font metrics class
*
* This class provides information about fonts and text. It can resolve
* font names into actual installed font files, as well as determine the
* size of text in a particular font and size.
*
* @static
* @package dompdf
*/
class Font_Metrics {
/**
* @see __DOMPDF_FONT_CACHE_FILE
*/
const CACHE_FILE = __DOMPDF_FONT_CACHE_FILE;
/**
* Underlying {@link Canvas} object to perform text size calculations
*
* @var Canvas
*/
static protected $_pdf = null;
/**
* Array of font family names to font files
*
* Usually cached by the {@link load_font.php} script
*
* @var array
*/
static protected $_font_lookup = array();
/**
* Class initialization
*
*/
static function init() {
if (!self::$_pdf) {
self::load_font_families();
self::$_pdf = Canvas_Factory::get_instance();
}
}
/**
* Calculates text size, in points
*
* @param string $text the text to be sized
* @param string $font the desired font
* @param float $size the desired font size
* @param float $spacing word spacing, if any
* @return float
*/
static function get_text_width($text, $font, $size, $word_spacing = 0, $char_spacing = 0) {
return self::$_pdf->get_text_width($text, $font, $size, $word_spacing, $char_spacing);
}
/**
* Calculates font height
*
* @param string $font
* @param float $size
* @return float
*/
static function get_font_height($font, $size) {
return self::$_pdf->get_font_height($font, $size);
}
/**
* Resolves a font family & subtype into an actual font file
*
* Subtype can be one of 'normal', 'bold', 'italic' or 'bold_italic'. If
* the particular font family has no suitable font file, the default font
* ({@link DOMPDF_DEFAULT_FONT}) is used. The font file returned
* is the absolute pathname to the font file on the system.
*
* @param string $family
* @param string $subtype
* @return string
*/
static function get_font($family, $subtype = "normal") {
/* Allow calling for various fonts in search path. Therefore not immediately
* return replacement on non match.
* Only when called with NULL try replacement.
* When this is also missing there is really trouble.
* If only the subtype fails, nevertheless return failure.
* Only on checking the fallback font, check various subtypes on same font.
*/
if ( $family ) {
$family = str_replace( array("'", '"'), "", mb_strtolower($family));
$subtype = mb_strtolower($subtype);
if ( isset(self::$_font_lookup[$family][$subtype]) ) {
return self::$_font_lookup[$family][$subtype];
}
return null;
}
$family = DOMPDF_DEFAULT_FONT;
if ( isset(self::$_font_lookup[$family][$subtype]) ) {
return self::$_font_lookup[$family][$subtype];
}
foreach ( self::$_font_lookup[$family] as $sub => $font ) {
if (strpos($subtype, $sub) !== false) {
return $font;
}
}
if ($subtype !== "normal") {
foreach ( self::$_font_lookup[$family] as $sub => $font ) {
if ($sub !== "normal") {
return $font;
}
}
}
$subtype = "normal";
if ( isset(self::$_font_lookup[$family][$subtype]) ) {
return self::$_font_lookup[$family][$subtype];
}
return null;
}
/**
* Saves the stored font family cache
*
* The name and location of the cache file are determined by {@link
* Font_Metrics::CACHE_FILE}. This file should be writable by the
* webserver process.
*
* @see Font_Metrics::load_font_families()
*/
static function save_font_families() {
// replace the path to the DOMPDF font directory with "DOMPDF_FONT_DIR" (allows for more portability)
$cache_data = var_export(self::$_font_lookup, true);
$cache_data = str_replace('\''.DOMPDF_FONT_DIR , 'DOMPDF_FONT_DIR . \'' , $cache_data);
$cache_data = "<"."?php return $cache_data ?".">";
file_put_contents(self::CACHE_FILE, $cache_data);
}
/**
* Loads the stored font family cache
*
* @see save_font_families()
*/
static function load_font_families() {
if ( !is_readable(self::CACHE_FILE) )
return;
self::$_font_lookup = require_once(self::CACHE_FILE);
// If the font family cache is still in the old format
if ( self::$_font_lookup === 1 ) {
$cache_data = file_get_contents(self::CACHE_FILE);
file_put_contents(self::CACHE_FILE, "<"."?php return $cache_data ?".">");
self::$_font_lookup = require_once(self::CACHE_FILE);
}
}
static function get_system_fonts() {
$files = glob("/usr/share/fonts/truetype/*.ttf") +
glob("/usr/share/fonts/truetype/*/*.ttf") +
glob("/usr/share/fonts/truetype/*/*/*.ttf") +
glob("C:\\Windows\\fonts\\*.ttf") +
glob("C:\\WinNT\\fonts\\*.ttf") +
glob("/mnt/c_drive/WINDOWS/Fonts/");
new TTF_Info;
$names = array();
foreach($files as $file) {
$info = getFontInfo($file);
$info["path"] = $file;
$type = $info[2];
if (preg_match("/regular|normal|medium|book/i", $type)) {
$type = "normal";
}
elseif (preg_match("/bold/i", $type)) {
if (preg_match("/italic|oblique/i", $type)) {
$type = "bold_italic";
}
else {
$type = "bold";
}
}
elseif (preg_match("/italic|oblique/i", $type)) {
$type = "italic";
}
$names[mb_strtolower($info[1])][$type] = $file;
}
$keys = array_keys($names);
/*$matches = array_intersect(array("times", "times new roman"), $keys);
$names["serif"] = $names[reset($matches)];
$matches = array_intersect(array("helvetica", "arial", "verdana"), $keys);
$names["sans-serif"] = $names[reset($matches)];
$matches = array_intersect(array("courier", "courier new"), $keys);
$names["monospace"] = $names[reset($matches)];
$names["fixed"] = $names[reset($matches)];*/
return $names;
}
/**
* Returns the current font lookup table
*
* @return array
*/
static function get_font_families() {
return self::$_font_lookup;
}
static function set_font_family($fontname, $entry) {
self::$_font_lookup[mb_strtolower($fontname)] = $entry;
}
}
Font_Metrics::init();

990
pdf/include/frame.cls.php Executable file
View File

@@ -0,0 +1,990 @@
<?php
/**
* DOMPDF - PHP5 HTML to PDF renderer
*
* File: $RCSfile: frame.cls.php,v $
* Created on: 2004-06-02
*
* Copyright (c) 2004 - Benj Carson <benjcarson@digitaljunkies.ca>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library in the file LICENSE.LGPL; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*
* Alternatively, you may distribute this software under the terms of the
* PHP License, version 3.0 or later. A copy of this license should have
* been distributed with this file in the file LICENSE.PHP . If this is not
* the case, you can obtain a copy at http://www.php.net/license/3_0.txt.
*
* The latest version of DOMPDF might be available at:
* http://www.dompdf.com/
*
* @link http://www.dompdf.com/
* @copyright 2004 Benj Carson
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @package dompdf
*/
/* $Id: frame.cls.php 359 2011-02-05 12:15:06Z fabien.menager $ */
/**
* The main Frame class
*
* This class represents a single HTML element. This class stores
* positioning information as well as containing block location and
* dimensions. Style information for the element is stored in a {@link
* Style} object. Tree structure is maintained via the parent & children
* links.
*
* @access protected
* @package dompdf
*/
class Frame {
/**
* The DOMNode object this frame represents
*
* @var DOMNode
*/
protected $_node;
/**
* Unique identifier for this frame. Used to reference this frame
* via the node.
*
* @var string
*/
protected $_id;
/**
* Unique id counter
*/
static protected $ID_COUNTER = 0;
/**
* This frame's calculated style
*
* @var Style
*/
protected $_style;
/**
* This frame's original style. Needed for cases where frames are
* split across pages.
*
* @var Style
*/
protected $_original_style;
/**
* This frame's parent in the document tree.
*
* @var Frame
*/
protected $_parent;
/**
* This frame's first child. All children are handled as a
* doubly-linked list.
*
* @var Frame
*/
protected $_first_child;
/**
* This frame's last child.
*
* @var Frame
*/
protected $_last_child;
/**
* This frame's previous sibling in the document tree.
*
* @var Frame
*/
protected $_prev_sibling;
/**
* This frame's next sibling in the document tree.
*
* @var Frame
*/
protected $_next_sibling;
/**
* This frame's containing block (used in layout): array(x, y, w, h)
*
* @var array
*/
protected $_containing_block;
/**
* Position on the page of the top-left corner of the margin box of
* this frame: array(x,y)
*
* @var array
*/
protected $_position;
/**
* Absolute opacity of this frame
*
* @var float
*/
protected $_opacity;
/**
* This frame's decorator
*
* @var Frame_Decorator
*/
protected $_decorator;
protected $_containing_line;
/**
* Tells wether the frame was already pushed to the next page
* @var bool
*/
public $_already_pushed = false;
/**
* Class destructor
*/
function __destruct() {
clear_object($this);
}
/**
* Class constructor
*
* @param DOMNode $node the DOMNode this frame represents
*/
function __construct(DomNode $node) {
$this->_node = $node;
$this->_parent = null;
$this->_first_child = null;
$this->_last_child = null;
$this->_prev_sibling = $this->_next_sibling = null;
$this->_style = null;
$this->_original_style = null;
$this->_containing_block = array(
"x" => null,
"y" => null,
"w" => null,
"h" => null,
);
$this->_containing_block[0] =& $this->_containing_block["x"];
$this->_containing_block[1] =& $this->_containing_block["y"];
$this->_containing_block[2] =& $this->_containing_block["w"];
$this->_containing_block[3] =& $this->_containing_block["h"];
$this->_position = array(
"x" => null,
"y" => null,
);
$this->_position[0] =& $this->_position["x"];
$this->_position[1] =& $this->_position["y"];
$this->_opacity = 1.0;
$this->_decorator = null;
$this->set_id( self::$ID_COUNTER++ );
}
/**
* "Destructor": forcibly free all references held by this frame
*
* @param bool $recursive if true, call dispose on all children
*/
function dispose($recursive = false) {
if ( $recursive ) {
while ( $child = $this->_first_child )
$child->dispose(true);
}
// Remove this frame from the tree
if ( $this->_prev_sibling ) {
$this->_prev_sibling->_next_sibling = $this->_next_sibling;
}
if ( $this->_next_sibling ) {
$this->_next_sibling->_prev_sibling = $this->_prev_sibling;
}
if ( $this->_parent && $this->_parent->_first_child === $this ) {
$this->_parent->_first_child = $this->_next_sibling;
}
if ( $this->_parent && $this->_parent->_last_child === $this ) {
$this->_parent->_last_child = $this->_prev_sibling;
}
if ( $this->_parent ) {
$this->_parent->get_node()->removeChild($this->_node);
}
$this->_style->dispose();
$this->_style = null;
unset($this->_style);
$this->_original_style->dispose();
$this->_original_style = null;
unset($this->_original_style);
}
// Re-initialize the frame
function reset() {
$this->_position["x"] = null;
$this->_position["y"] = null;
$this->_containing_block["x"] = null;
$this->_containing_block["y"] = null;
$this->_containing_block["w"] = null;
$this->_containing_block["h"] = null;
$this->_style = null;
unset($this->_style);
$this->_style = clone $this->_original_style;
}
//........................................................................
// Accessor methods
/**
* @return DOMNode
*/
function get_node() { return $this->_node; }
/**
* @return string
*/
function get_id() { return $this->_id; }
/**
* @return Style
*/
function get_style() { return $this->_style; }
/**
* @return Style
*/
function get_original_style() { return $this->_original_style; }
/**
* @return Frame
*/
function get_parent() { return $this->_parent; }
/**
* @return Frame_Decorator
*/
function get_decorator() { return $this->_decorator; }
/**
* @return Frame
*/
function get_first_child() { return $this->_first_child; }
/**
* @return Frame
*/
function get_last_child() { return $this->_last_child; }
/**
* @return Frame
*/
function get_prev_sibling() { return $this->_prev_sibling; }
/**
* @return Frame
*/
function get_next_sibling() { return $this->_next_sibling; }
/**
* @return FrameList
*/
function get_children() { return new FrameList($this); }
// Layout property accessors
/**
* Containing block dimensions
*
* @param $i string The key of the wanted containing block's dimension (x, y, x, h)
* @return array|float
*/
function get_containing_block($i = null) {
if ( isset($i) )
return $this->_containing_block[$i];
return $this->_containing_block;
}
/**
* Block position
*
* @param $i string The key of the wanted position value (x, y)
* @return array|float
*/
function get_position($i = null) {
if ( isset($i) ) {
return $this->_position[$i];
}
return $this->_position;
}
//........................................................................
/**
* Return the height of the margin box of the frame, in pt. Meaningless
* unless the height has been calculated properly.
*
* @return float
*/
function get_margin_height() {
$style = $this->_style;
return $style->length_in_pt(array(
$style->height,
$style->margin_top,
$style->margin_bottom,
$style->border_top_width,
$style->border_bottom_width,
$style->padding_top,
$style->padding_bottom
), $this->_containing_block["h"]);
}
/**
* Return the width of the margin box of the frame, in pt. Meaningless
* unless the width has been calculated properly.
*
* @return float
*/
function get_margin_width() {
$style = $this->_style;
return $style->length_in_pt(array(
$style->width,
$style->margin_left,
$style->margin_right,
$style->border_left_width,
$style->border_right_width,
$style->padding_left,
$style->padding_right
), $this->_containing_block["w"]);
}
function get_break_margins(){
$style = $this->_style;
return $style->length_in_pt(array(
//$style->height,
$style->margin_top,
$style->margin_bottom,
$style->border_top_width,
$style->border_bottom_width,
$style->padding_top,
$style->padding_bottom
), $this->_containing_block["h"]);
}
/**
* Return the padding box (x,y,w,h) of the frame
*
* @return array
*/
function get_padding_box() {
$style = $this->_style;
$cb = $this->_containing_block;
$x = $this->_position["x"] +
$style->length_in_pt(array($style->margin_left,
$style->border_left_width),
$cb["w"]);
$y = $this->_position["y"] +
$style->length_in_pt(array($style->margin_top,
$style->border_top_width),
$cb["h"]);
$w = $style->length_in_pt(array($style->padding_left,
$style->width,
$style->padding_right),
$cb["w"]);
$h = $style->length_in_pt(array($style->padding_top,
$style->height,
$style->padding_bottom),
$cb["h"]);
return array(0 => $x, "x" => $x,
1 => $y, "y" => $y,
2 => $w, "w" => $w,
3 => $h, "h" => $h);
}
/**
* Return the border box of the frame
*
* @return array
*/
function get_border_box() {
$style = $this->_style;
$cb = $this->_containing_block;
$x = $this->_position["x"] + $style->length_in_pt($style->margin_left, $cb["w"]);
$y = $this->_position["y"] + $style->length_in_pt($style->margin_top, $cb["h"]);
$w = $style->length_in_pt(array($style->border_left_width,
$style->padding_left,
$style->width,
$style->padding_right,
$style->border_right_width),
$cb["w"]);
$h = $style->length_in_pt(array($style->border_top_width,
$style->padding_top,
$style->height,
$style->padding_bottom,
$style->border_bottom_width),
$cb["h"]);
return array(0 => $x, "x" => $x,
1 => $y, "y" => $y,
2 => $w, "w" => $w,
3 => $h, "h" => $h);
}
function get_opacity($opacity = null) {
if ( $opacity !== null ) {
$this->set_opacity($opacity);
}
return $this->_opacity;
}
function &get_containing_line() {
return $this->_containing_line;
}
//........................................................................
// Set methods
function set_id($id) {
$this->_id = $id;
// We can only set attributes of DOMElement objects (nodeType == 1).
// Since these are the only objects that we can assign CSS rules to,
// this shortcoming is okay.
if ( $this->_node->nodeType == XML_ELEMENT_NODE )
$this->_node->setAttribute("frame_id", $id);
}
function set_style(Style $style) {
if ( is_null($this->_style) )
$this->_original_style = clone $style;
//$style->set_frame($this);
$this->_style = $style;
}
function set_decorator(Frame_Decorator $decorator) {
$this->_decorator = $decorator;
}
function set_containing_block($x = null, $y = null, $w = null, $h = null) {
if ( is_array($x) ){
foreach($x as $key => $val){
$$key = $val;
}
}
if (is_numeric($x)) {
$this->_containing_block["x"] = $x;
}
if (is_numeric($y)) {
$this->_containing_block["y"] = $y;
}
if (is_numeric($w)) {
$this->_containing_block["w"] = $w;
}
if (is_numeric($h)) {
$this->_containing_block["h"] = $h;
}
}
function set_position($x = null, $y = null) {
if ( is_array($x) )
extract($x);
if ( is_numeric($x) ) {
$this->_position["x"] = $x;
}
if ( is_numeric($y) ) {
$this->_position["y"] = $y;
}
}
function set_opacity($opacity) {
$parent = $this->get_parent();
$base_opacity = (($parent && $parent->_opacity !== null) ? $parent->_opacity : 1.0);
$this->_opacity = $base_opacity * $opacity;
}
function set_containing_line(&$line) {
$this->_containing_line = &$line;
}
//........................................................................
/**
* Inserts a new child at the beginning of the Frame
*
* @param $child Frame The new Frame to insert
* @param $update_node boolean Whether or not to update the DOM
*/
function prepend_child(Frame $child, $update_node = true) {
if ( $update_node )
$this->_node->insertBefore($child->_node, $this->_first_child ? $this->_first_child->_node : null);
// Remove the child from its parent
if ( $child->_parent )
$child->_parent->remove_child($child, false);
$child->_parent = $this;
$child->_prev_sibling = null;
// Handle the first child
if ( !$this->_first_child ) {
$this->_first_child = $child;
$this->_last_child = $child;
$child->_next_sibling = null;
} else {
$this->_first_child->_prev_sibling = $child;
$child->_next_sibling = $this->_first_child;
$this->_first_child = $child;
}
}
/**
* Inserts a new child at the end of the Frame
*
* @param $child Frame The new Frame to insert
* @param $update_node boolean Whether or not to update the DOM
*/
function append_child(Frame $child, $update_node = true) {
if ( $update_node )
$this->_node->appendChild($child->_node);
// Remove the child from its parent
if ( $child->_parent )
$child->_parent->remove_child($child, false);
$child->_parent = $this;
$child->_next_sibling = null;
// Handle the first child
if ( !$this->_last_child ) {
$this->_first_child = $child;
$this->_last_child = $child;
$child->_prev_sibling = null;
} else {
$this->_last_child->_next_sibling = $child;
$child->_prev_sibling = $this->_last_child;
$this->_last_child = $child;
}
}
/**
* Inserts a new child immediately before the specified frame
*
* @param $new_child Frame The new Frame to insert
* @param $ref Frame The Frame after the new Frame
* @param $update_node boolean Whether or not to update the DOM
*/
function insert_child_before(Frame $new_child, Frame $ref, $update_node = true) {
if ( $ref === $this->_first_child ) {
$this->prepend_child($new_child, $update_node);
return;
}
if ( is_null($ref) ) {
$this->append_child($new_child, $update_node);
return;
}
if ( $ref->_parent !== $this )
throw new DOMPDF_Exception("Reference child is not a child of this node.");
// Update the node
if ( $update_node )
$this->_node->insertBefore($new_child->_node, $ref->_node);
// Remove the child from its parent
if ( $new_child->_parent )
$new_child->_parent->remove_child($new_child, false);
$new_child->_parent = $this;
$new_child->_next_sibling = $ref;
$new_child->_prev_sibling = $ref->_prev_sibling;
if ( $ref->_prev_sibling )
$ref->_prev_sibling->_next_sibling = $new_child;
$ref->_prev_sibling = $new_child;
}
/**
* Inserts a new child immediately after the specified frame
*
* @param $new_child Frame The new Frame to insert
* @param $ref Frame The Frame before the new Frame
* @param $update_node boolean Whether or not to update the DOM
*/
function insert_child_after(Frame $new_child, Frame $ref, $update_node = true) {
if ( $ref === $this->_last_child ) {
$this->append_child($new_child, $update_node);
return;
}
if ( is_null($ref) ) {
$this->prepend_child($new_child, $update_node);
return;
}
if ( $ref->_parent !== $this )
throw new DOMPDF_Exception("Reference child is not a child of this node.");
// Update the node
if ( $update_node ) {
if ( $ref->_next_sibling ) {
$next_node = $ref->_next_sibling->_node;
$this->_node->insertBefore($new_child->_node, $next_node);
} else {
$new_child->_node = $this->_node->appendChild($new_child);
}
}
// Remove the child from its parent
if ( $new_child->_parent)
$new_child->_parent->remove_child($new_child, false);
$new_child->_parent = $this;
$new_child->_prev_sibling = $ref;
$new_child->_next_sibling = $ref->_next_sibling;
if ( $ref->_next_sibling )
$ref->_next_sibling->_prev_sibling = $new_child;
$ref->_next_sibling = $new_child;
}
/**
* Remove a child frame
*
* @param $child Frame
* @param $update_node boolean Whether or not to remove the DOM node
* @return Frame The removed child frame
*/
function remove_child(Frame $child, $update_node = true) {
if ( $child->_parent !== $this )
throw new DOMPDF_Exception("Child not found in this frame");
if ( $update_node )
$this->_node->removeChild($child->_node);
if ( $child === $this->_first_child )
$this->_first_child = $child->_next_sibling;
if ( $child === $this->_last_child )
$this->_last_child = $child->_prev_sibling;
if ( $child->_prev_sibling )
$child->_prev_sibling->_next_sibling = $child->_next_sibling;
if ( $child->_next_sibling )
$child->_next_sibling->_prev_sibling = $child->_prev_sibling;
$child->_next_sibling = null;
$child->_prev_sibling = null;
$child->_parent = null;
return $child;
}
//........................................................................
// Debugging function:
function __toString() {
// Skip empty text frames
// if ( $this->_node->nodeName === "#text" &&
// preg_replace("/\s/", "", $this->_node->data) === "" )
// return "";
$str = "<b>" . $this->_node->nodeName . ":</b><br/>";
//$str .= spl_object_hash($this->_node) . "<br/>";
$str .= "Id: " .$this->get_id() . "<br/>";
$str .= "Class: " .get_class($this) . "<br/>";
if ( $this->_node->nodeName === "#text" ) {
$tmp = htmlspecialchars($this->_node->nodeValue);
$str .= "<pre>'" . mb_substr($tmp,0,70) .
(mb_strlen($tmp) > 70 ? "..." : "") . "'</pre>";
} elseif ( $css_class = $this->_node->getAttribute("class") ) {
$tmp = htmlspecialchars($css_class);
$str .= "CSS class: '$css_class'<br/>";
}
if ( $this->_parent )
$str .= "\nParent:" . $this->_parent->_node->nodeName .
" (" . spl_object_hash($this->_parent->_node) . ") " .
"<br/>";
if ( $this->_prev_sibling )
$str .= "Prev: " . $this->_prev_sibling->_node->nodeName .
" (" . spl_object_hash($this->_prev_sibling->_node) . ") " .
"<br/>";
if ( $this->_next_sibling )
$str .= "Next: " . $this->_next_sibling->_node->nodeName .
" (" . spl_object_hash($this->_next_sibling->_node) . ") " .
"<br/>";
$d = $this->get_decorator();
while ($d && $d != $d->get_decorator()) {
$str .= "Decorator: " . get_class($d) . "<br/>";
$d = $d->get_decorator();
}
$str .= "Position: " . pre_r($this->_position, true);
$str .= "\nContaining block: " . pre_r($this->_containing_block, true);
$str .= "\nMargin width: " . pre_r($this->get_margin_width(), true);
$str .= "\nMargin height: " . pre_r($this->get_margin_height(), true);
$str .= "\nStyle: <pre>". $this->_style->__toString() . "</pre>";
if ( $this->_decorator instanceof Block_Frame_Decorator ) {
$str .= "Lines:<pre>";
foreach ($this->_decorator->get_lines() as $line) {
foreach ($line["frames"] as $frame) {
if ($frame instanceof Text_Frame_Decorator) {
$str .= "\ntext: ";
$str .= "'". htmlspecialchars($frame->get_text()) ."'";
} else {
$str .= "\nBlock: " . $frame->get_node()->nodeName . " (" . spl_object_hash($frame->get_node()) . ")";
}
}
$str .=
//"\ncount => " . $line["count"] . "\n".
"\ny => " . $line["y"] . "\n" .
"w => " . $line["w"] . "\n" .
"h => " . $line["h"] . "\n" .
"left => " . $line["left"] . "\n" .
"right => " . $line["right"] . "\n";
}
$str .= "</pre>";
}
$str .= "\n";
if ( php_sapi_name() === "cli" )
$str = strip_tags(str_replace(array("<br/>","<b>","</b>"),
array("\n","",""),
$str));
return $str;
}
}
//------------------------------------------------------------------------
/**
* Linked-list IteratorAggregate
*
* @access private
* @package dompdf
*/
class FrameList implements IteratorAggregate {
protected $_frame;
function __construct($frame) { $this->_frame = $frame; }
function getIterator() { return new FrameListIterator($this->_frame); }
}
/**
* Linked-list Iterator
*
* Returns children in order and allows for list to change during iteration,
* provided the changes occur to or after the current element
*
* @access private
* @package dompdf
*/
class FrameListIterator implements Iterator {
/**
* @var Frame
*/
protected $_parent;
/**
* @var Frame
*/
protected $_cur;
/**
* @var int
*/
protected $_num;
function __construct(Frame $frame) {
$this->_parent = $frame;
$this->_cur = $frame->get_first_child();
$this->_num = 0;
}
function rewind() {
$this->_cur = $this->_parent->get_first_child();
$this->_num = 0;
}
/**
* @return bool
*/
function valid() {
return isset($this->_cur);// && ($this->_cur->get_prev_sibling() === $this->_prev);
}
function key() { return $this->_num; }
/**
* @return Frame
*/
function current() { return $this->_cur; }
/**
* @return Frame
*/
function next() {
$ret = $this->_cur;
if ( !$ret )
return null;
$this->_cur = $this->_cur->get_next_sibling();
$this->_num++;
return $ret;
}
}
//------------------------------------------------------------------------
/**
* Pre-order IteratorAggregate
*
* @access private
* @package dompdf
*/
class FrameTreeList implements IteratorAggregate {
/**
* @var Frame
*/
protected $_root;
function __construct(Frame $root) { $this->_root = $root; }
/**
* @return FrameTreeIterator
*/
function getIterator() { return new FrameTreeIterator($this->_root); }
}
/**
* Pre-order Iterator
*
* Returns frames in preorder traversal order (parent then children)
*
* @access private
* @package dompdf
*/
class FrameTreeIterator implements Iterator {
/**
* @var Frame
*/
protected $_root;
protected $_stack = array();
/**
* @var int
*/
protected $_num;
function __construct(Frame $root) {
$this->_stack[] = $this->_root = $root;
$this->_num = 0;
}
function rewind() {
$this->_stack = array($this->_root);
$this->_num = 0;
}
/**
* @return bool
*/
function valid() { return count($this->_stack) > 0; }
/**
* @return int
*/
function key() { return $this->_num; }
/**
* @var Frame
*/
function current() { return end($this->_stack); }
/**
* @var Frame
*/
function next() {
$b = end($this->_stack);
// Pop last element
unset($this->_stack[ key($this->_stack) ]);
$this->_num++;
// Push all children onto the stack in reverse order
if ( $c = $b->get_last_child() ) {
$this->_stack[] = $c;
while ( $c = $c->get_prev_sibling() )
$this->_stack[] = $c;
}
return $b;
}
}

View File

@@ -0,0 +1,483 @@
<?php
/**
* DOMPDF - PHP5 HTML to PDF renderer
*
* File: $RCSfile: frame_decorator.cls.php,v $
* Created on: 2004-06-02
*
* Copyright (c) 2004 - Benj Carson <benjcarson@digitaljunkies.ca>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library in the file LICENSE.LGPL; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*
* Alternatively, you may distribute this software under the terms of the
* PHP License, version 3.0 or later. A copy of this license should have
* been distributed with this file in the file LICENSE.PHP . If this is not
* the case, you can obtain a copy at http://www.php.net/license/3_0.txt.
*
* The latest version of DOMPDF might be available at:
* http://www.dompdf.com/
*
* @link http://www.dompdf.com/
* @copyright 2004 Benj Carson
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @package dompdf
*/
/* $Id: frame_decorator.cls.php 357 2011-01-30 20:56:46Z fabien.menager $ */
/**
* Base Frame_Decorator class
*
* @access private
* @package dompdf
*/
abstract class Frame_Decorator extends Frame {
/**
* The root node of the DOM tree
*
* @var Frame
*/
protected $_root;
/**
* The decorated frame
*
* @var Frame
*/
protected $_frame;
/**
* Positioner object used to position this frame (Strategy pattern)
*
* @var Positioner
*/
protected $_positioner;
/**
* Reflower object used to calculate frame dimensions (Strategy pattern)
*
* @var Frame_Reflower
*/
protected $_reflower;
/**
* Reference to the current dompdf instance
*
* @var DOMPDF
*/
protected $_dompdf;
/**
* First block parent
*
* @var Frame_Decorator
*/
private $_block_parent;
/**
* First positionned parent (position: relative | absolute | fixed)
*
* @var Frame_Decorator
*/
private $_positionned_parent;
/**
* Class constructor
*
* @param Frame $frame the decoration target
*/
function __construct(Frame $frame, DOMPDF $dompdf) {
$this->_frame = $frame;
$this->_root = null;
$this->_dompdf = $dompdf;
$frame->set_decorator($this);
}
/**
* "Destructor": foribly free all references held by this object
*
* @param bool $recursive if true, call dispose on all children
*/
function dispose($recursive = false) {
if ( $recursive ) {
while ( $child = $this->get_first_child() )
$child->dispose(true);
}
$this->_root = null;
unset($this->_root);
$this->_frame->dispose(true);
$this->_frame = null;
unset($this->_frame);
$this->_positioner = null;
unset($this->_positioner);
$this->_reflower = null;
unset($this->_reflower);
}
/**
* Return a copy of this frame with $node as its node
*
* @param DomNode $node
* @return Frame
*/
function copy(DomNode $node) {
$frame = new Frame($node);
$frame->set_style(clone $this->_frame->get_original_style());
$deco = Frame_Factory::decorate_frame($frame, $this->_dompdf);
$deco->set_root($this->_root);
return $deco;
}
/**
* Create a deep copy: copy this node and all children
*
* @return Frame
*/
function deep_copy() {
$frame = new Frame($this->get_node()->cloneNode());
$frame->set_style(clone $this->_frame->get_original_style());
$deco = Frame_Factory::decorate_frame($frame, $this->_dompdf);
$deco->set_root($this->_root);
foreach ($this->get_children() as $child)
$deco->append_child($child->deep_copy());
return $deco;
}
//........................................................................
/**
* Delegate calls to decorated frame object
*/
function reset() {
$this->_frame->reset();
// Reset all children
foreach ($this->get_children() as $child)
$child->reset();
}
// Getters -----------
function get_id() { return $this->_frame->get_id(); }
/**
* @return Frame
*/
function get_frame() { return $this->_frame; }
/**
* @return DomNode
*/
function get_node() { return $this->_frame->get_node(); }
/**
* @return Style
*/
function get_style() { return $this->_frame->get_style(); }
/**
* @return Style
*/
function get_original_style() { return $this->_frame->get_original_style(); }
function get_containing_block($i = null) { return $this->_frame->get_containing_block($i); }
function get_position($i = null) { return $this->_frame->get_position($i); }
/**
* @return DOMPDF
*/
function get_dompdf() { return $this->_dompdf; }
function get_margin_height() { return $this->_frame->get_margin_height(); }
function get_margin_width() { return $this->_frame->get_margin_width(); }
function get_padding_box() { return $this->_frame->get_padding_box(); }
function get_border_box() { return $this->_frame->get_border_box(); }
// Setters -----------
function set_id($id) { $this->_frame->set_id($id); }
function set_style(Style $style) { $this->_frame->set_style($style); }
function set_containing_block($x = null, $y = null, $w = null, $h = null) {
$this->_frame->set_containing_block($x, $y, $w, $h);
}
function set_position($x = null, $y = null) {
$this->_frame->set_position($x, $y);
}
function __toString() { return $this->_frame->__toString(); }
function prepend_child(Frame $child, $update_node = true) {
while ( $child instanceof Frame_Decorator )
$child = $child->_frame;
$this->_frame->prepend_child($child, $update_node);
}
function append_child(Frame $child, $update_node = true) {
while ( $child instanceof Frame_Decorator )
$child = $child->_frame;
$this->_frame->append_child($child, $update_node);
}
function insert_child_before(Frame $new_child, Frame $ref, $update_node = true) {
while ( $new_child instanceof Frame_Decorator )
$new_child = $new_child->_frame;
if ( $ref instanceof Frame_Decorator )
$ref = $ref->_frame;
$this->_frame->insert_child_before($new_child, $ref, $update_node);
}
function insert_child_after(Frame $new_child, Frame $ref, $update_node = true) {
while ( $new_child instanceof Frame_Decorator )
$new_child = $new_child->_frame;
while ( $ref instanceof Frame_Decorator )
$ref = $ref->_frame;
$this->_frame->insert_child_after($new_child, $ref, $update_node);
}
function remove_child(Frame $child, $update_node = true) {
while ( $child instanceof Frame_Decorator )
$child = $new_child->_frame;
$this->_frame->remove_child($child, $update_node);
}
//........................................................................
/**
* @return Frame_Decorator
*/
function get_parent() {
$p = $this->_frame->get_parent();
if ( $p && $deco = $p->get_decorator() ) {
while ( $tmp = $deco->get_decorator() )
$deco = $tmp;
return $deco;
} else if ( $p )
return $p;
else
return null;
}
/**
* @return Frame_Decorator
*/
function get_first_child() {
$c = $this->_frame->get_first_child();
if ( $c && $deco = $c->get_decorator() ) {
while ( $tmp = $deco->get_decorator() )
$deco = $tmp;
return $deco;
} else if ( $c )
return $c;
else
return null;
}
/**
* @return Frame_Decorator
*/
function get_last_child() {
$c = $this->_frame->get_last_child();
if ( $c && $deco = $c->get_decorator() ) {
while ( $tmp = $deco->get_decorator() )
$deco = $tmp;
return $deco;
} else if ( $c )
return $c;
else
return null;
}
/**
* @return Frame_Decorator
*/
function get_prev_sibling() {
$s = $this->_frame->get_prev_sibling();
if ( $s && $deco = $s->get_decorator() ) {
while ( $tmp = $deco->get_decorator() )
$deco = $tmp;
return $deco;
} else if ( $s )
return $s;
else
return null;
}
/**
* @return Frame_Decorator
*/
function get_next_sibling() {
$s = $this->_frame->get_next_sibling();
if ( $s && $deco = $s->get_decorator() ) {
while ( $tmp = $deco->get_decorator() )
$deco = $tmp;
return $deco;
} else if ( $s )
return $s;
else
return null;
}
/**
* @return FrameList
*/
function get_children() {
return new FrameList($this);
}
/**
* @return FrameTreeList
*/
function get_subtree() {
return new FrameTreeList($this);
}
//........................................................................
function set_positioner(Positioner $posn) {
$this->_positioner = $posn;
if ( $this->_frame instanceof Frame_Decorator )
$this->_frame->set_positioner($posn);
}
//........................................................................
function set_reflower(Frame_Reflower $reflower) {
$this->_reflower = $reflower;
if ( $this->_frame instanceof Frame_Decorator )
$this->_frame->set_reflower( $reflower );
}
function get_reflower() { return $this->_reflower; }
//........................................................................
function set_root(Frame $root) {
$this->_root = $root;
if ( $this->_frame instanceof Frame_Decorator )
$this->_frame->set_root($root);
}
function get_root() { return $this->_root; }
//........................................................................
/**
* @return Frame_Decorator
*/
function find_block_parent() {
//if ( $this->_block_parent ) return $this->_block_parent; // FIXME: makes dom_anchor_link example fail
// Find our nearest block level parent
$p = $this->get_parent();
while ( $p ) {
if ( in_array($p->get_style()->display, Style::$BLOCK_TYPES) )
break;
$p = $p->get_parent();
}
return $this->_block_parent = $p;
}
/**
* @return Frame_Decorator
*/
function find_positionned_parent() {
//if ( $this->_positionned_parent ) return $this->_positionned_parent; // FIXME: makes dom_anchor_link example fail
// Find our nearest relative positionned parent
$p = $this->get_parent();
while ( $p ) {
if ( in_array($p->get_style()->position, Style::$POSITIONNED_TYPES) ) {
break;
}
$p = $p->get_parent();
}
if ( !$p ) {
$p = $this->_root;
}
return $this->_positionned_parent = $p;
}
//........................................................................
/**
* split this frame at $child.
*
* The current frame is cloned and $child and all children following
* $child are added to the clone. The clone is then passed to the
* current frame's parent->split() method.
*
* @param Frame $child
* @param boolean $force_pagebreak
*/
function split($child = null, $force_pagebreak = false) {
if ( is_null( $child ) ) {
$this->get_parent()->split($this, $force_pagebreak);
return;
}
if ( $child->get_parent() !== $this )
throw new DOMPDF_Exception("Unable to split: frame is not a child of this one.");
$split = $this->copy( $this->_frame->get_node()->cloneNode() );
$split->reset();
$this->get_parent()->insert_child_after($split, $this);
// Add $frame and all following siblings to the new split node
$iter = $child;
while ($iter) {
$frame = $iter;
$iter = $iter->get_next_sibling();
$frame->reset();
$split->append_child($frame);
}
$this->get_parent()->split($split, $force_pagebreak);
}
//........................................................................
final function position() { $this->_positioner->position(); }
final function reflow(Frame_Decorator $block = null) {
// Uncomment this to see the frames before they're laid out, instead of
// during rendering.
//echo $this->_frame; flush();
$this->_reflower->reflow($block);
}
final function get_min_max_width() { return $this->_reflower->get_min_max_width(); }
//........................................................................
}

207
pdf/include/frame_factory.cls.php Executable file
View File

@@ -0,0 +1,207 @@
<?php
/**
* DOMPDF - PHP5 HTML to PDF renderer
*
* File: $RCSfile: frame_factory.cls.php,v $
* Created on: 2004-06-17
*
* Copyright (c) 2004 - Benj Carson <benjcarson@digitaljunkies.ca>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library in the file LICENSE.LGPL; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*
* Alternatively, you may distribute this software under the terms of the
* PHP License, version 3.0 or later. A copy of this license should have
* been distributed with this file in the file LICENSE.PHP . If this is not
* the case, you can obtain a copy at http://www.php.net/license/3_0.txt.
*
* The latest version of DOMPDF might be available at:
* http://www.dompdf.com/
*
* @link http://www.dompdf.com/
* @copyright 2004 Benj Carson
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @package dompdf
*/
/* $Id: frame_factory.cls.php 356 2011-01-28 08:56:10Z fabien.menager $ */
/**
* Contains frame decorating logic
*
* This class is responsible for assigning the correct {@link Frame_Decorator},
* {@link Positioner}, and {@link Frame_Reflower} objects to {@link Frame}
* objects. This is determined primarily by the Frame's display type, but
* also by the Frame's node's type (e.g. DomElement vs. #text)
*
* @access private
* @package dompdf
*/
class Frame_Factory {
/**
* Decorate the Frame
*
* @param $root Frame The frame to decorate
* @param $dompdf DOMPDF The dompdf instance
*/
static function decorate_root(Frame $root, DOMPDF $dompdf) {
$frame = new Page_Frame_Decorator($root, $dompdf);
$frame->set_reflower( new Page_Frame_Reflower($frame) );
$root->set_decorator($frame);
return $frame;
}
/**
* Decorate a Frame
*
* @param $root Frame The frame to decorate
* @param $dompdf DOMPDF The dompdf instance
* @return Frame_Decorator
* FIXME: this is admittedly a little smelly...
*/
static function decorate_frame(Frame $frame, DOMPDF $dompdf) {
if ( is_null($dompdf) )
throw new Exception("foo");
$style = $frame->get_style();
switch ($style->display) {
case "block":
$positioner = "Block";
$decorator = "Block";
$reflower = "Block";
break;
case "inline-block":
$positioner = "Inline";
$decorator = "Block";
$reflower = "Block";
break;
case "inline":
$positioner = "Inline";
if ( $frame->get_node()->nodeName === "#text" ) {
$decorator = "Text";
$reflower = "Text";
}
else {
if ( DOMPDF_ENABLE_CSS_FLOAT && $style->float !== "none" ) {
$decorator = "Block";
$reflower = "Block";
}
else {
$decorator = "Inline";
$reflower = "Inline";
}
}
break;
case "table":
$positioner = "Block";
$decorator = "Table";
$reflower = "Table";
break;
case "inline-table":
$positioner = "Inline";
$decorator = "Table";
$reflower = "Table";
break;
case "table-row-group":
case "table-header-group":
case "table-footer-group":
$positioner = "Null";
$decorator = "Table_Row_Group";
$reflower = "Table_Row_Group";
break;
case "table-row":
$positioner = "Null";
$decorator = "Table_Row";
$reflower = "Table_Row";
break;
case "table-cell":
$positioner = "Table_Cell";
$decorator = "Table_Cell";
$reflower = "Table_Cell";
break;
case "list-item":
$positioner = "Block";
$decorator = "Block";
$reflower = "Block";
break;
case "-dompdf-list-bullet":
if ( $style->list_style_position === "inside" )
$positioner = "Inline";
else
$positioner = "List_Bullet";
if ( $style->list_style_image !== "none" )
$decorator = "List_Bullet_Image";
else
$decorator = "List_Bullet";
$reflower = "List_Bullet";
break;
case "-dompdf-image":
$positioner = "Inline";
$decorator = "Image";
$reflower = "Image";
break;
case "-dompdf-br":
$positioner = "Inline";
$decorator = "Inline";
$reflower = "Inline";
break;
default:
// FIXME: should throw some sort of warning or something?
case "none":
$positioner = "Null";
$decorator = "Null";
$reflower = "Null";
break;
}
$position = $style->position;
if ( $position === "absolute" )
$positioner = "Absolute";
else if ( $position === "fixed" )
$positioner = "Fixed";
$positioner .= "_Positioner";
$decorator .= "_Frame_Decorator";
$reflower .= "_Frame_Reflower";
$deco = new $decorator($frame, $dompdf);
$deco->set_positioner( new $positioner($deco) );
$reflow = new $reflower($deco);
$deco->set_reflower( $reflow );
return $deco;
}
}

View File

@@ -0,0 +1,414 @@
<?php
/**
* DOMPDF - PHP5 HTML to PDF renderer
*
* File: $RCSfile: frame_reflower.cls.php,v $
* Created on: 2004-06-17
*
* Copyright (c) 2004 - Benj Carson <benjcarson@digitaljunkies.ca>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library in the file LICENSE.LGPL; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*
* Alternatively, you may distribute this software under the terms of the
* PHP License, version 3.0 or later. A copy of this license should have
* been distributed with this file in the file LICENSE.PHP . If this is not
* the case, you can obtain a copy at http://www.php.net/license/3_0.txt.
*
* The latest version of DOMPDF might be available at:
* http://www.dompdf.com/
*
* @link http://www.dompdf.com/
* @copyright 2004 Benj Carson
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @package dompdf
*/
/* $Id: frame_reflower.cls.php 357 2011-01-30 20:56:46Z fabien.menager $ */
/**
* Base reflower class
*
* Reflower objects are responsible for determining the width and height of
* individual frames. They also create line and page breaks as necessary.
*
* @access private
* @package dompdf
*/
abstract class Frame_Reflower {
/**
* Frame for this reflower
*
* @var Frame
*/
protected $_frame;
/**
* Cached min/max size
*
* @var array
*/
protected $_min_max_cache;
function __construct(Frame $frame) {
$this->_frame = $frame;
$this->_min_max_cache = null;
}
function dispose() {
clear_object($this);
}
protected function _collapse_margins() {
$cb = $this->_frame->get_containing_block();
$style = $this->_frame->get_style();
$t = $style->length_in_pt($style->margin_top, $cb["h"]);
$b = $style->length_in_pt($style->margin_bottom, $cb["h"]);
// Handle 'auto' values
if ( $t === "auto" ) {
$style->margin_top = "0pt";
$t = 0;
}
if ( $b === "auto" ) {
$style->margin_bottom = "0pt";
$b = 0;
}
// Collapse vertical margins:
$n = $this->_frame->get_next_sibling();
// FIXME If there is a non-empty inline frame between the blocks, it is not taken into account
while ( $n && !in_array($n->get_style()->display, Style::$BLOCK_TYPES) ) {
$n = $n->get_next_sibling();
}
if ( $n ) { // && !$n instanceof Page_Frame_Decorator ) {
$b = max($b, $style->length_in_pt($n->get_style()->margin_top, $cb["h"]));
$n->get_style()->margin_top = "0pt";
$style->margin_bottom = $b."pt";
}
// Collapse our first child's margin
$f = $this->_frame->get_first_child();
while ( $f && !in_array($f->get_style()->display, Style::$BLOCK_TYPES) )
$f = $f->get_next_sibling();
// Margin are collapsed only between block elements
if ( $f && in_array($f->get_style()->display, Style::$BLOCK_TYPES)) {
$t = max( $t, $style->length_in_pt($f->get_style()->margin_top, $cb["h"]));
$style->margin_top = $t."pt";
$f->get_style()->margin_bottom = "0pt";
}
}
//........................................................................
abstract function reflow(Frame_Decorator $block = null);
//........................................................................
// Required for table layout: Returns an array(0 => min, 1 => max, "min"
// => min, "max" => max) of the minimum and maximum widths of this frame.
// This provides a basic implementation. Child classes should override
// this if necessary.
function get_min_max_width() {
if ( !is_null($this->_min_max_cache) ) {
return $this->_min_max_cache;
}
$style = $this->_frame->get_style();
// Account for margins & padding
$dims = array($style->padding_left,
$style->padding_right,
$style->border_left_width,
$style->border_right_width,
$style->margin_left,
$style->margin_right);
$cb_w = $this->_frame->get_containing_block("w");
$delta = $style->length_in_pt($dims, $cb_w);
// Handle degenerate case
if ( !$this->_frame->get_first_child() )
return $this->_min_max_cache = array($delta, $delta,"min" => $delta, "max" => $delta);
$low = array();
$high = array();
for ( $iter = $this->_frame->get_children()->getIterator();
$iter->valid();
$iter->next() ) {
$inline_min = 0;
$inline_max = 0;
// Add all adjacent inline widths together to calculate max width
while ( $iter->valid() && in_array( $iter->current()->get_style()->display, Style::$INLINE_TYPES ) ) {
$child = $iter->current();
$minmax = $child->get_min_max_width();
if ( in_array( $iter->current()->get_style()->white_space, array("pre", "nowrap") ) )
$inline_min += $minmax["min"];
else
$low[] = $minmax["min"];
$inline_max += $minmax["max"];
$iter->next();
}
if ( $inline_max > 0 )
$high[] = $inline_max;
if ( $inline_min > 0 )
$low[] = $inline_min;
if ( $iter->valid() ) {
list($low[], $high[]) = $iter->current()->get_min_max_width();
continue;
}
}
$min = count($low) ? max($low) : 0;
$max = count($high) ? max($high) : 0;
// Use specified width if it is greater than the minimum defined by the
// content. If the width is a percentage ignore it for now.
$width = $style->width;
if ( $width !== "auto" && !is_percent($width) ) {
$width = $style->length_in_pt($width, $cb_w);
if ( $min < $width )
$min = $width;
if ( $max < $width )
$max = $width;
}
$min += $delta;
$max += $delta;
return $this->_min_max_cache = array($min, $max, "min"=>$min, "max"=>$max);
}
/**
* Parses a CSS string containing quotes and escaped hex characters
*
* @param $string string The CSS string to parse
* @param $single_trim
* @return string
*/
protected function _parse_string($string, $single_trim = false) {
if ($single_trim) {
$string = preg_replace("/^[\"\']/", "", $string);
$string = preg_replace("/[\"\']$/", "", $string);
}
else {
$string = trim($string, "'\"");
}
$string = str_replace(array("\\\n",'\\"',"\\'"),
array("",'"',"'"), $string);
// Convert escaped hex characters into ascii characters (e.g. \A => newline)
$string = preg_replace_callback("/\\\\([0-9a-fA-F]{0,6})(\s)?(?(2)|(?=[^0-9a-fA-F]))/",
create_function('$matches',
'return chr(hexdec($matches[1]));'),
$string);
return $string;
}
/**
* Parses a CSS "quotes" property
*
* @return array An array of pairs of quotes
*/
protected function _parse_quotes() {
// Matches quote types
$re = "/(\'[^\']*\')|(\"[^\"]*\")/";
$quotes = $this->_frame->get_style()->quotes;
// split on spaces, except within quotes
if (!preg_match_all($re, "$quotes", $matches, PREG_SET_ORDER))
return;
$quotes_array = array();
foreach($matches as &$_quote){
$quotes_array[] = $this->_parse_string($_quote[0], true);
}
if ( empty($quotes_array) ) {
$quotes_array = array('"', '"');
}
return array_chunk($quotes_array, 2);
}
/**
* Parses the CSS "content" property
*
* @return string The resulting string
*/
protected function _parse_content() {
// Matches generated content
$re = "/\n".
"\s(counters?\\([^)]*\\))|\n".
"\A(counters?\\([^)]*\\))|\n".
"\s([\"']) ( (?:[^\"']|\\\\[\"'])+ )(?<!\\\\)\\3|\n".
"\A([\"']) ( (?:[^\"']|\\\\[\"'])+ )(?<!\\\\)\\5|\n" .
"\s([^\s\"']+)|\n" .
"\A([^\s\"']+)\n".
"/xi";
$content = $this->_frame->get_style()->content;
$quotes = $this->_parse_quotes();
// split on spaces, except within quotes
if (!preg_match_all($re, $content, $matches, PREG_SET_ORDER))
return;
$text = "";
foreach ($matches as $match) {
if ( isset($match[2]) && $match[2] !== "" )
$match[1] = $match[2];
if ( isset($match[6]) && $match[6] !== "" )
$match[4] = $match[6];
if ( isset($match[8]) && $match[8] !== "" )
$match[7] = $match[8];
if ( isset($match[1]) && $match[1] !== "" ) {
// counters?(...)
$match[1] = mb_strtolower(trim($match[1]));
// Handle counter() references:
// http://www.w3.org/TR/CSS21/generate.html#content
$i = mb_strpos($match[1], ")");
if ( $i === false )
continue;
$args = explode(",", mb_substr($match[1], 8, $i - 8));
$counter_id = $args[0];
if ( $match[1][7] === "(" ) {
// counter(name [,style])
if ( isset($args[1]) )
$type = trim($args[1]);
else
$type = null;
$p = $this->_frame->find_block_parent();
$text .= $p->counter_value($counter_id, $type);
} else if ( $match[1][7] === "s" ) {
// counters(name, string [,style])
if ( isset($args[1]) )
$string = $this->_parse_string(trim($args[1]));
else
$string = "";
if ( isset($args[2]) )
$type = $args[2];
else
$type = null;
$p = $this->_frame->find_block_parent();
$tmp = "";
while ($p) {
$tmp = $p->counter_value($counter_id, $type) . $string . $tmp;
$p = $p->find_block_parent();
}
$text .= $tmp;
} else
// countertops?
continue;
} else if ( isset($match[4]) && $match[4] !== "" ) {
// String match
$text .= $this->_parse_string($match[4]);
} else if ( isset($match[7]) && $match[7] !== "" ) {
// Directive match
if ( $match[7] === "open-quote" ) {
// FIXME: do something here
$text .= $quotes[0][0];
} else if ( $match[7] === "close-quote" ) {
// FIXME: do something else here
$text .= $quotes[0][1];
} else if ( $match[7] === "no-open-quote" ) {
// FIXME:
} else if ( $match[7] === "no-close-quote" ) {
// FIXME:
} else if ( mb_strpos($match[7],"attr(") === 0 ) {
$i = mb_strpos($match[7],")");
if ( $i === false )
continue;
$attr = mb_substr($match[7], 5, $i - 5);
if ( $attr == "" )
continue;
$text .= $this->_frame->get_parent()->get_node()->getAttribute($attr);
} else
continue;
}
}
return $text;
}
/**
* Sets the generated content of a generated frame
*/
protected function _set_content(){
$frame = $this->_frame;
$style = $frame->get_style();
if ( $style->content && $frame->get_node()->nodeName === "dompdf_generated" ) {
$content = $this->_parse_content();
$node = $frame->get_node()->ownerDocument->createTextNode($content);
$new_style = $style->get_stylesheet()->create_style();
$new_style->inherit($style);
$new_frame = new Frame($node);
$new_frame->set_style($new_style);
Frame_Factory::decorate_frame($new_frame, $frame->get_dompdf());
$new_frame->get_decorator()->set_root($frame->get_root());
$frame->append_child($new_frame);
}
}
}

230
pdf/include/frame_tree.cls.php Executable file
View File

@@ -0,0 +1,230 @@
<?php
/**
* DOMPDF - PHP5 HTML to PDF renderer
*
* File: $RCSfile: frame_tree.cls.php,v $
* Created on: 2004-06-02
*
* Copyright (c) 2004 - Benj Carson <benjcarson@digitaljunkies.ca>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library in the file LICENSE.LGPL; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*
* Alternatively, you may distribute this software under the terms of the
* PHP License, version 3.0 or later. A copy of this license should have
* been distributed with this file in the file LICENSE.PHP . If this is not
* the case, you can obtain a copy at http://www.php.net/license/3_0.txt.
*
* The latest version of DOMPDF might be available at:
* http://www.dompdf.com/
*
* @link http://www.dompdf.com/
* @copyright 2004 Benj Carson
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @package dompdf
*/
/* $Id: frame_tree.cls.php 332 2010-11-27 14:06:34Z fabien.menager $ */
/**
* Represents an entire document as a tree of frames
*
* The Frame_Tree consists of {@link Frame} objects each tied to specific
* DomNode objects in a specific DomDocument. The Frame_Tree has the same
* structure as the DomDocument, but adds additional capabalities for
* styling and layout.
*
* @package dompdf
* @access protected
*/
class Frame_Tree {
/**
* Tags to ignore while parsing the tree
*
* @var array
*/
static protected $_HIDDEN_TAGS = array("area", "base", "basefont", "head", "style",
"meta", "title", "colgroup",
"noembed", "noscript", "param", "#comment");
/**
* The main DomDocument
*
* @see http://ca2.php.net/manual/en/ref.dom.php
* @var DomDocument
*/
protected $_dom;
/**
* The root node of the FrameTree.
*
* @var Frame
*/
protected $_root;
/**
* Subtrees of absolutely positioned elements
*
* @var array of Frames
*/
protected $_absolute_frames;
/**
* A mapping of {@link Frame} objects to DomNode objects
*
* @var array
*/
protected $_registry;
/**
* Class constructor
*
* @param DomDocument $dom the main DomDocument object representing the current html document
*/
function __construct(DomDocument $dom) {
$this->_dom = $dom;
$this->_root = null;
$this->_registry = array();
}
function __destruct() {
clear_object($this);
}
/**
* Returns the DomDocument object representing the curent html document
*
* @return DomDocument
*/
function get_dom() { return $this->_dom; }
/**
* Returns the root frame of the tree
*
* @return Frame
*/
function get_root() { return $this->_root; }
/**
* Returns a specific frame given its id
*
* @param string $id
* @return Frame
*/
function get_frame($id) { return isset($this->_registry[$id]) ? $this->_registry[$id] : null; }
/**
* Returns a post-order iterator for all frames in the tree
*
* @return FrameTreeList
*/
function get_frames() { return new FrameTreeList($this->_root); }
/**
* Builds the tree
*/
function build_tree() {
$html = $this->_dom->getElementsByTagName("html")->item(0);
if ( is_null($html) )
$html = $this->_dom->firstChild;
if ( is_null($html) )
throw new DOMPDF_Exception("Requested HTML document contains no data.");
$this->_root = $this->_build_tree_r($html);
}
/**
* Recursively adds {@link Frame} objects to the tree
*
* Recursively build a tree of Frame objects based on a dom tree.
* No layout information is calculated at this time, although the
* tree may be adjusted (i.e. nodes and frames for generated content
* and images may be created).
*
* @param DomNode $node the current DomNode being considered
* @return Frame
*/
protected function _build_tree_r(DomNode $node) {
$frame = new Frame($node);
$id = $frame->get_id();
$this->_registry[ $id ] = $frame;
if ( !$node->hasChildNodes() )
return $frame;
// Fixes 'cannot access undefined property for object with
// overloaded access', fix by Stefan radulian
// <stefan.radulian@symbion.at>
//foreach ($node->childNodes as $child) {
// Store the children in an array so that the tree can be modified
$children = array();
for ($i = 0; $i < $node->childNodes->length; $i++)
$children[] = $node->childNodes->item($i);
foreach ($children as $child) {
$node_name = mb_strtolower($child->nodeName);
// Skip non-displaying nodes
if ( in_array($node_name, self::$_HIDDEN_TAGS) ) {
if ( $node_name !== "head" &&
$node_name !== "style" )
$child->parentNode->removeChild($child);
continue;
}
// Skip empty text nodes
if ( $node_name === "#text" && $child->nodeValue == "" ) {
$child->parentNode->removeChild($child);
continue;
}
// Skip empty image nodes
if ( $node_name === "img" && $child->getAttribute("src") == "" ) {
$child->parentNode->removeChild($child);
continue;
}
$frame->append_child($this->_build_tree_r($child), false);
}
return $frame;
}
public function insert_node(DOMNode $node, DOMNode $new_node, $pos) {
if ($pos === "after" || !$node->firstChild)
$node->appendChild($new_node);
else
$node->insertBefore($new_node, $node->firstChild);
$this->_build_tree_r($new_node);
$frame_id = $new_node->getAttribute("frame_id");
$frame = $this->get_frame($frame_id);
$parent_id = $node->getAttribute("frame_id");
$parent = $this->get_frame($parent_id);
if ($pos === "before")
$parent->prepend_child($frame, false);
else
$parent->append_child($frame, false);
}
}

942
pdf/include/functions.inc.php Executable file
View File

@@ -0,0 +1,942 @@
<?php
/**
* DOMPDF - PHP5 HTML to PDF renderer
*
* File: $RCSfile: functions.inc.php,v $
* Created on: 2004-08-04
*
* Copyright (c) 2004 - Benj Carson <benjcarson@digitaljunkies.ca>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library in the file LICENSE.LGPL; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*
* Alternatively, you may distribute this software under the terms of the
* PHP License, version 3.0 or later. A copy of this license should have
* been distributed with this file in the file LICENSE.PHP . If this is not
* the case, you can obtain a copy at http://www.php.net/license/3_0.txt.
*
* The latest version of DOMPDF might be available at:
* http://www.dompdf.com/
*
* @link http://www.dompdf.com/
* @copyright 2004 Benj Carson
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @contributor Helmut Tischer <htischer@weihenstephan.org>
* @package dompdf
*
* Changes
* @contributor Helmut Tischer <htischer@weihenstephan.org>
* @version 0.5.1.htischer.20090507
* - trailing slash of base_path in build_url is no longer optional when
* required. This allows paths not ending in a slash, e.g. on dynamically
* created sites with page id in the url parameters.
* @version 20090601
* - fix windows paths
* @version 20090610
* - relax windows path syntax, use uniform path delimiter. Used for background images.
*/
/* $Id: functions.inc.php 361 2011-02-16 21:03:05Z fabien.menager $ */
function def($name, $value = true) {
if (!defined($name)) {
define($name, $value);
}
}
if ( !function_exists("pre_r") ) {
/**
* print_r wrapper for html/cli output
*
* Wraps print_r() output in < pre > tags if the current sapi is not
* 'cli'. Returns the output string instead of displaying it if $return is
* true.
*
* @param mixed $mixed variable or expression to display
* @param bool $return
*
*/
function pre_r($mixed, $return = false) {
if ($return)
return "<pre>" . print_r($mixed, true) . "</pre>";
if ( php_sapi_name() !== "cli")
echo ("<pre>");
print_r($mixed);
if ( php_sapi_name() !== "cli")
echo("</pre>");
else
echo ("\n");
flush();
}
}
if ( !function_exists("pre_var_dump") ) {
/**
* var_dump wrapper for html/cli output
*
* Wraps var_dump() output in < pre > tags if the current sapi is not
* 'cli'.
*
* @param mixed $mixed variable or expression to display.
*/
function pre_var_dump($mixed) {
if ( php_sapi_name() !== "cli")
echo("<pre>");
var_dump($mixed);
if ( php_sapi_name() !== "cli")
echo("</pre>");
}
}
if ( !function_exists("d") ) {
/**
* generic debug function
*
* Takes everything and does its best to give a good debug output
*
* @param mixed $mixed variable or expression to display.
*/
function d($mixed) {
if ( php_sapi_name() !== "cli")
echo("<pre>");
// line
if (is_array($mixed) && array_key_exists("tallest_frame", $mixed)) {
echo "<strong>LINE</strong>:\n";
foreach($mixed as $key => $value) {
if (is_array($value) || is_object($value)) continue;
echo " $key:\t".var_export($value,true)."\n";
}
}
// other
else {
var_export($mixed);
}
if ( php_sapi_name() !== "cli")
echo("</pre>");
}
}
/**
* builds a full url given a protocol, hostname, base path and url
*
* @param string $protocol
* @param string $host
* @param string $base_path
* @param string $url
* @return string
*
* Initially the trailing slash of $base_path was optional, and conditionally appended.
* However on dynamically created sites, where the page is given as url parameter,
* the base path might not end with an url.
* Therefore do not append a slash, and **require** the $base_url to ending in a slash
* when needed.
* Vice versa, on using the local file system path of a file, make sure that the slash
* is appended (o.k. also for Windows)
*/
function build_url($protocol, $host, $base_path, $url) {
if ( mb_strlen($url) == 0 ) {
//return $protocol . $host . rtrim($base_path, "/\\") . "/";
return $protocol . $host . $base_path;
}
// Is the url already fully qualified or a Data URI?
if ( mb_strpos($url, "://") !== false || mb_strpos($url, "data:") === 0 )
return $url;
$ret = $protocol;
if (!in_array(mb_strtolower($protocol), array("http://", "https://", "ftp://", "ftps://"))) {
//On Windows local file, an abs path can begin also with a '\' or a drive letter and colon
//drive: followed by a relative path would be a drive specific default folder.
//not known in php app code, treat as abs path
//($url[1] !== ':' || ($url[2]!=='\\' && $url[2]!=='/'))
if ($url[0] !== '/' && (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN' || ($url[0] !== '\\' && $url[1] !== ':'))) {
// For rel path and local acess we ignore the host, and run the path through realpath()
$ret .= realpath($base_path).'/';
}
$ret .= $url;
$ret = preg_replace("/\?(.*)$/", "", $ret);
return $ret;
}
//remote urls with backslash in html/css are not really correct, but lets be genereous
if ( $url[0] === '/' || $url[0] === '\\' ) {
// Absolute path
$ret .= $host . $url;
} else {
// Relative path
//$base_path = $base_path !== "" ? rtrim($base_path, "/\\") . "/" : "";
$ret .= $host . $base_path . $url;
}
return $ret;
}
/**
* parse a full url or pathname and return an array(protocol, host, path,
* file + query + fragment)
*
* @param string $url
* @return array
*/
function explode_url($url) {
$protocol = "";
$host = "";
$path = "";
$file = "";
$arr = parse_url($url);
if ( isset($arr["scheme"]) &&
$arr["scheme"] !== "file" &&
mb_strlen($arr["scheme"]) > 1 ) // Exclude windows drive letters...
{
$protocol = $arr["scheme"] . "://";
if ( isset($arr["user"]) ) {
$host .= $arr["user"];
if ( isset($arr["pass"]) )
$host .= "@" . $arr["pass"];
$host .= ":";
}
if ( isset($arr["host"]) )
$host .= $arr["host"];
if ( isset($arr["port"]) )
$host .= ":" . $arr["port"];
if ( isset($arr["path"]) && $arr["path"] !== "" ) {
// Do we have a trailing slash?
if ( $arr["path"][ mb_strlen($arr["path"]) - 1 ] === "/" ) {
$path = $arr["path"];
$file = "";
} else {
$path = dirname($arr["path"]) . "/";
$file = basename($arr["path"]);
}
}
if ( isset($arr["query"]) )
$file .= "?" . $arr["query"];
if ( isset($arr["fragment"]) )
$file .= "#" . $arr["fragment"];
} else {
$i = mb_strpos($url, "file://");
if ( $i !== false)
$url = mb_substr($url, $i + 7);
$protocol = ""; // "file://"; ? why doesn't this work... It's because of
// network filenames like //COMPU/SHARENAME
$host = ""; // localhost, really
$file = basename($url);
$path = dirname($url);
// Check that the path exists
if ( $path !== false ) {
$path .= '/';
} else {
// generate a url to access the file if no real path found.
$protocol = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https://' : 'http://';
$host = isset($_SERVER["HTTP_HOST"]) ? $_SERVER["HTTP_HOST"] : php_uname("n");
if ( substr($arr["path"], 0, 1) === '/' ) {
$path = dirname($arr["path"]);
} else {
$path = '/' . rtrim(dirname($_SERVER["SCRIPT_NAME"]), '/') . '/' . $arr["path"];
}
}
}
$ret = array($protocol, $host, $path, $file,
"protocol" => $protocol,
"host" => $host,
"path" => $path,
"file" => $file);
return $ret;
}
/**
* converts decimal numbers to roman numerals
*
* @param int $num
* @return string
*/
function dec2roman($num) {
static $ones = array("", "i", "ii", "iii", "iv", "v",
"vi", "vii", "viii", "ix");
static $tens = array("", "x", "xx", "xxx", "xl", "l",
"lx", "lxx", "lxxx", "xc");
static $hund = array("", "c", "cc", "ccc", "cd", "d",
"dc", "dcc", "dccc", "cm");
static $thou = array("", "m", "mm", "mmm");
if ( !is_numeric($num) )
throw new DOMPDF_Exception("dec2roman() requires a numeric argument.");
if ( $num > 4000 || $num < 0 )
return "(out of range)";
$num = strrev((string)$num);
$ret = "";
switch (mb_strlen($num)) {
case 4: $ret .= $thou[$num[3]];
case 3: $ret .= $hund[$num[2]];
case 2: $ret .= $tens[$num[1]];
case 1: $ret .= $ones[$num[0]];
default: break;
}
return $ret;
}
/**
* Determines whether $value is a percentage or not
*
* @param float $value
* @return bool
*/
function is_percent($value) { return false !== mb_strpos($value, "%"); }
/**
* Parses a data URI scheme
* http://en.wikipedia.org/wiki/Data_URI_scheme
* @param string $data_uri The data URI to parse
* @return array The result with charset, mime type and decoded data
*/
function parse_data_uri($data_uri) {
if (!preg_match('/^data:(?P<mime>[a-z0-9\/+-.]+)(;charset=(?P<charset>[a-z0-9-])+)?(?P<base64>;base64)?\,(?P<data>.*)?/i', $data_uri, $match)) {
return false;
}
$match['data'] = rawurldecode($match['data']);
$result = array(
'charset' => $match['charset'] ? $match['charset'] : 'US-ASCII',
'mime' => $match['mime'] ? $match['mime'] : 'text/plain',
'data' => $match['base64'] ? base64_decode($match['data']) : $match['data'],
);
return $result;
}
/**
* mb_string compatibility
*/
if ( !function_exists("mb_strlen") ) {
define('MB_OVERLOAD_MAIL', 1);
define('MB_OVERLOAD_STRING', 2);
define('MB_OVERLOAD_REGEX', 4);
define('MB_CASE_UPPER', 0);
define('MB_CASE_LOWER', 1);
define('MB_CASE_TITLE', 2);
function mb_convert_encoding($data, $to_encoding, $from_encoding = 'UTF-8') {
if (str_replace('-', '', strtolower($to_encoding)) === 'utf8') {
return utf8_encode($data);
} else {
return utf8_decode($data);
}
}
function mb_detect_encoding($data, $encoding_list = array('iso-8859-1'), $strict = false) {
return 'iso-8859-1';
}
function mb_detect_order($encoding_list = array('iso-8859-1')) {
return 'iso-8859-1';
}
function mb_internal_encoding($encoding = null) {
if (isset($encoding)) {
return true;
} else {
return 'iso-8859-1';
}
}
function mb_strlen($str, $encoding = 'iso-8859-1') {
switch (str_replace('-', '', strtolower($encoding))) {
case "utf8": return strlen(utf8_encode($str));
case "8bit": return strlen($str);
default: return strlen(utf8_decode($str));
}
}
function mb_strpos($haystack, $needle, $offset = 0) {
return strpos($haystack, $needle, $offset);
}
function mb_strrpos($haystack, $needle, $offset = 0) {
return strrpos($haystack, $needle, $offset);
}
function mb_strtolower( $str ) {
return strtolower($str);
}
function mb_strtoupper( $str ) {
return strtoupper($str);
}
function mb_substr($string, $start, $length = null, $encoding = 'iso-8859-1') {
if ( is_null($length) )
return substr($string, $start);
else
return substr($string, $start, $length);
}
function mb_substr_count($haystack, $needle, $encoding = 'iso-8859-1') {
return substr_count($haystack, $needle);
}
function mb_encode_numericentity($str, $convmap, $encoding) {
return htmlspecialchars($str);
}
function mb_convert_case($str, $mode = MB_CASE_UPPER, $encoding = array()) {
switch($mode) {
case MB_CASE_UPPER: return mb_strtoupper($str);
case MB_CASE_LOWER: return mb_strtolower($str);
case MB_CASE_TITLE: return ucwords(mb_strtolower($str));
default: return $str;
}
}
function mb_list_encodings() {
return array(
"ISO-8859-1",
"UTF-8",
"8bit",
);
}
}
/**
* Decoder for RLE8 compression in windows bitmaps
* http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdi/bitmaps_6x0u.asp
*/
function rle8_decode ($str, $width){
$lineWidth = $width + (3 - ($width-1) % 4);
$out = '';
$cnt = strlen($str);
for ($i = 0; $i <$cnt; $i++) {
$o = ord($str[$i]);
switch ($o){
case 0: # ESCAPE
$i++;
switch (ord($str[$i])){
case 0: # NEW LINE
$padCnt = $lineWidth - strlen($out)%$lineWidth;
if ($padCnt<$lineWidth) $out .= str_repeat(chr(0), $padCnt); # pad line
break;
case 1: # END OF FILE
$padCnt = $lineWidth - strlen($out)%$lineWidth;
if ($padCnt<$lineWidth) $out .= str_repeat(chr(0), $padCnt); # pad line
break 3;
case 2: # DELTA
$i += 2;
break;
default: # ABSOLUTE MODE
$num = ord($str[$i]);
for ($j = 0; $j < $num; $j++)
$out .= $str[++$i];
if ($num % 2) $i++;
}
break;
default:
$out .= str_repeat($str[++$i], $o);
}
}
return $out;
}
/**
* Decoder for RLE4 compression in windows bitmaps
* see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdi/bitmaps_6x0u.asp
*/
function rle4_decode ($str, $width) {
$w = floor($width/2) + ($width % 2);
$lineWidth = $w + (3 - ( ($width-1) / 2) % 4);
$pixels = array();
$cnt = strlen($str);
for ($i = 0; $i < $cnt; $i++) {
$o = ord($str[$i]);
switch ($o) {
case 0: # ESCAPE
$i++;
switch (ord($str[$i])){
case 0: # NEW LINE
while (count($pixels)%$lineWidth!=0)
$pixels[]=0;
break;
case 1: # END OF FILE
while (count($pixels)%$lineWidth!=0)
$pixels[]=0;
break 3;
case 2: # DELTA
$i += 2;
break;
default: # ABSOLUTE MODE
$num = ord($str[$i]);
for ($j = 0; $j < $num; $j++){
if ($j%2 == 0){
$c = ord($str[++$i]);
$pixels[] = ($c & 240)>>4;
} else
$pixels[] = $c & 15;
}
if ($num % 2) $i++;
}
break;
default:
$c = ord($str[++$i]);
for ($j = 0; $j < $o; $j++)
$pixels[] = ($j%2==0 ? ($c & 240)>>4 : $c & 15);
}
}
$out = '';
if (count($pixels)%2) $pixels[]=0;
$cnt = count($pixels)/2;
for ($i = 0; $i < $cnt; $i++)
$out .= chr(16*$pixels[2*$i] + $pixels[2*$i+1]);
return $out;
}
if ( !function_exists("imagecreatefrombmp") ) {
/**
* Credit goes to mgutt
* http://www.programmierer-forum.de/function-imagecreatefrombmp-welche-variante-laeuft-t143137.htm
* Modified by Fabien Menager to support RGB555 BMP format
*/
function imagecreatefrombmp($filename) {
try {
// version 1.00
if (!($fh = fopen($filename, 'rb'))) {
trigger_error('imagecreatefrombmp: Can not open ' . $filename, E_USER_WARNING);
return false;
}
// read file header
$meta = unpack('vtype/Vfilesize/Vreserved/Voffset', fread($fh, 14));
// check for bitmap
if ($meta['type'] != 19778) {
trigger_error('imagecreatefrombmp: ' . $filename . ' is not a bitmap!', E_USER_WARNING);
return false;
}
// read image header
$meta += unpack('Vheadersize/Vwidth/Vheight/vplanes/vbits/Vcompression/Vimagesize/Vxres/Vyres/Vcolors/Vimportant', fread($fh, 40));
// read additional bitfield header
if ($meta['compression'] == 3) {
$meta += unpack('VrMask/VgMask/VbMask', fread($fh, 12));
}
//pre_r($filename);pre_r($meta);
// set bytes and padding
$meta['bytes'] = $meta['bits'] / 8;
$meta['decal'] = 4 - (4 * (($meta['width'] * $meta['bytes'] / 4)- floor($meta['width'] * $meta['bytes'] / 4)));
if ($meta['decal'] == 4) {
$meta['decal'] = 0;
}
// obtain imagesize
if ($meta['imagesize'] < 1) {
$meta['imagesize'] = $meta['filesize'] - $meta['offset'];
// in rare cases filesize is equal to offset so we need to read physical size
if ($meta['imagesize'] < 1) {
$meta['imagesize'] = @filesize($filename) - $meta['offset'];
if ($meta['imagesize'] < 1) {
trigger_error('imagecreatefrombmp: Can not obtain filesize of ' . $filename . '!', E_USER_WARNING);
return false;
}
}
}
// calculate colors
$meta['colors'] = !$meta['colors'] ? pow(2, $meta['bits']) : $meta['colors'];
// read color palette
$palette = array();
if ($meta['bits'] < 16) {
$palette = unpack('l' . $meta['colors'], fread($fh, $meta['colors'] * 4));
// in rare cases the color value is signed
if ($palette[1] < 0) {
foreach ($palette as $i => $color) {
$palette[$i] = $color + 16777216;
}
}
}
// create gd image
$im = imagecreatetruecolor($meta['width'], $meta['height']);
$data = fread($fh, $meta['imagesize']);
// uncompress data
switch ($meta['compression']) {
case 1: $data = rle8_decode($data, $meta['width']); break;
case 2: $data = rle4_decode($data, $meta['width']); break;
}
$p = 0;
$vide = chr(0);
$y = $meta['height'] - 1;
$error = 'imagecreatefrombmp: ' . $filename . ' has not enough data!';
// loop through the image data beginning with the lower left corner
while ($y >= 0) {
$x = 0;
while ($x < $meta['width']) {
switch ($meta['bits']) {
case 32:
case 24:
if (!($part = substr($data, $p, 3 /*$meta['bytes']*/))) {
trigger_error($error, E_USER_WARNING);
return $im;
}
$color = unpack('V', $part . $vide);
break;
case 16:
if (!($part = substr($data, $p, 2 /*$meta['bytes']*/))) {
trigger_error($error, E_USER_WARNING);
return $im;
}
$color = unpack('v', $part);
if (empty($meta['rMask']) || $meta['rMask'] != 0xf800)
$color[1] = (($color[1] & 0x7c00) >> 7) * 65536 + (($color[1] & 0x03e0) >> 2) * 256 + (($color[1] & 0x001f) << 3); // 555
else
$color[1] = (($color[1] & 0xf800) >> 8) * 65536 + (($color[1] & 0x07e0) >> 3) * 256 + (($color[1] & 0x001f) << 3); // 565
break;
case 8:
$color = unpack('n', $vide . substr($data, $p, 1));
$color[1] = $palette[ $color[1] + 1 ];
break;
case 4:
$color = unpack('n', $vide . substr($data, floor($p), 1));
$color[1] = ($p * 2) % 2 == 0 ? $color[1] >> 4 : $color[1] & 0x0F;
$color[1] = $palette[ $color[1] + 1 ];
break;
case 1:
$color = unpack('n', $vide . substr($data, floor($p), 1));
switch (($p * 8) % 8) {
case 0: $color[1] = $color[1] >> 7; break;
case 1: $color[1] = ($color[1] & 0x40) >> 6; break;
case 2: $color[1] = ($color[1] & 0x20) >> 5; break;
case 3: $color[1] = ($color[1] & 0x10) >> 4; break;
case 4: $color[1] = ($color[1] & 0x8 ) >> 3; break;
case 5: $color[1] = ($color[1] & 0x4 ) >> 2; break;
case 6: $color[1] = ($color[1] & 0x2 ) >> 1; break;
case 7: $color[1] = ($color[1] & 0x1 ); break;
}
$color[1] = $palette[ $color[1] + 1 ];
break;
default:
trigger_error('imagecreatefrombmp: ' . $filename . ' has ' . $meta['bits'] . ' bits and this is not supported!', E_USER_WARNING);
return false;
}
imagesetpixel($im, $x, $y, $color[1]);
$x++;
$p += $meta['bytes'];
}
$y--;
$p += $meta['decal'];
}
fclose($fh);
return $im;
} catch (Exception $e) {var_dump($e);}
}
}
/**
* getimagesize doesn't give a good size for 32bit BMP image v5
*
* @param string $filename
* @return array The same format as getimagesize($filename)
*/
function dompdf_getimagesize($filename) {
$size = getimagesize($filename);
if ( $size[0] == null || $size[1] == null ) {
$data = file_get_contents($filename, null, null, 0, 26);
if ( substr($data, 0, 2) === "BM" ) {
$meta = unpack('vtype/Vfilesize/Vreserved/Voffset/Vheadersize/Vwidth/Vheight', $data);
$size[0] = (int)$meta['width'];
$size[1] = (int)$meta['height'];
}
}
return $size;
}
/**
* Converts a CMYK color to RGB
*
* @param int $c
* @param int $m
* @param int $y
* @param int $k
* @return object
*/
function cmyk_to_rgb($c, $m = null, $y = null, $k = null) {
if (is_array($c)) {
list($c, $m, $y, $k) = $c;
}
$c *= 255;
$m *= 255;
$y *= 255;
$k *= 255;
$r = (1 - round(2.55 * ($c+$k))) ;
$g = (1 - round(2.55 * ($m+$k))) ;
$b = (1 - round(2.55 * ($y+$k))) ;
if($r<0) $r = 0;
if($g<0) $g = 0;
if($b<0) $b = 0;
return array(
$r, $g, $b,
"r" => $r, "g" => $g, "b" => $b
);
}
function unichr($c) {
if ($c <= 0x7F) {
return chr($c);
} else if ($c <= 0x7FF) {
return chr(0xC0 | $c >> 6) . chr(0x80 | $c & 0x3F);
} else if ($c <= 0xFFFF) {
return chr(0xE0 | $c >> 12) . chr(0x80 | $c >> 6 & 0x3F)
. chr(0x80 | $c & 0x3F);
} else if ($c <= 0x10FFFF) {
return chr(0xF0 | $c >> 18) . chr(0x80 | $c >> 12 & 0x3F)
. chr(0x80 | $c >> 6 & 0x3F)
. chr(0x80 | $c & 0x3F);
}
return false;
}
if ( !function_exists("date_default_timezone_get") ) {
function date_default_timezone_get() {
return "";
}
function date_default_timezone_set($timezone_identifier) {
return true;
}
}
/**
* Stores warnings in an array for display later
*
* This function allows warnings generated by the DomDocument parser
* and CSS loader ({@link Stylesheet}) to be captured and displayed
* later. Without this function, errors are displayed immediately and
* PDF streaming is impossible.
*
* @see http://www.php.net/manual/en/function.set-error_handler.php
*
* @param int $errno
* @param string $errstr
* @param string $errfile
* @param string $errline
*/
function record_warnings($errno, $errstr, $errfile, $errline) {
if ( !($errno & (E_WARNING | E_NOTICE | E_USER_NOTICE | E_USER_WARNING )) ) // Not a warning or notice
throw new DOMPDF_Exception($errstr . " $errno");
global $_dompdf_warnings;
global $_dompdf_show_warnings;
if ( $_dompdf_show_warnings )
echo $errstr . "\n";
$_dompdf_warnings[] = $errstr;
}
/**
* Print a useful backtrace
*/
function bt() {
if ( php_sapi_name() !== "cli")
echo("<pre>");
$bt = debug_backtrace();
array_shift($bt); // remove actual bt() call
echo "\n";
$i = 0;
foreach ($bt as $call) {
$file = basename($call["file"]) . " (" . $call["line"] . ")";
if ( isset($call["class"]) ) {
$func = $call["class"] . "->" . $call["function"] . "()";
} else {
$func = $call["function"] . "()";
}
echo "#" . str_pad($i, 2, " ", STR_PAD_RIGHT) . ": " . str_pad($file.":", 42) . " $func\n";
$i++;
}
echo "\n";
if ( php_sapi_name() !== "cli")
echo("</pre>");
}
/**
* Print debug messages
*
* @param string $type The type of debug messages to print
*/
function dompdf_debug($type, $msg) {
global $_DOMPDF_DEBUG_TYPES, $_dompdf_show_warnings, $_dompdf_debug;
if ( isset($_DOMPDF_DEBUG_TYPES[$type]) && ($_dompdf_show_warnings || $_dompdf_debug) ) {
$arr = debug_backtrace();
echo basename($arr[0]["file"]) . " (" . $arr[0]["line"] ."): " . $arr[1]["function"] . ": ";
pre_r($msg);
}
}
if ( !function_exists("print_memusage") ) {
/**
* Dump memory usage
*/
function print_memusage() {
global $memusage;
echo ("Memory Usage\n");
$prev = 0;
$initial = reset($memusage);
echo (str_pad("Initial:", 40) . $initial . "\n\n");
foreach ($memusage as $key=>$mem) {
$mem -= $initial;
echo (str_pad("$key:" , 40));
echo (str_pad("$mem", 12) . "(diff: " . ($mem - $prev) . ")\n");
$prev = $mem;
}
echo ("\n" . str_pad("Total:", 40) . memory_get_usage()) . "\n";
}
}
if ( !function_exists("enable_mem_profile") ) {
/**
* Initialize memory profiling code
*/
function enable_mem_profile() {
global $memusage;
$memusage = array("Startup" => memory_get_usage());
register_shutdown_function("print_memusage");
}
}
if ( !function_exists("mark_memusage") ) {
/**
* Record the current memory usage
*
* @param string $location a meaningful location
*/
function mark_memusage($location) {
global $memusage;
if ( isset($memusage) )
$memusage[$location] = memory_get_usage();
}
}
if ( !function_exists('sys_get_temp_dir')) {
/**
* Find the current system temporary directory
*
* @link http://us.php.net/manual/en/function.sys-get-temp-dir.php#85261
*/
function sys_get_temp_dir() {
if (!empty($_ENV['TMP'])) { return realpath($_ENV['TMP']); }
if (!empty($_ENV['TMPDIR'])) { return realpath( $_ENV['TMPDIR']); }
if (!empty($_ENV['TEMP'])) { return realpath( $_ENV['TEMP']); }
$tempfile=tempnam(uniqid(rand(),TRUE),'');
if (file_exists($tempfile)) {
unlink($tempfile);
return realpath(dirname($tempfile));
}
}
}
if ( function_exists("memory_get_peak_usage") ) {
function DOMPDF_memory_usage(){
return memory_get_peak_usage(true);
}
}
else if ( function_exists("memory_get_peak_usage") ) {
function DOMPDF_memory_usage(){
return memory_get_usage(true);
}
}
else {
function DOMPDF_memory_usage(){
return "N/A";
}
}
/**
* Affect null to the unused objects
* @param unknown_type $object
*/
function clear_object(&$object) {
if ( is_object($object) ) {
foreach (array_keys((array)$object) as $key) {
clear_object($property);
}
foreach(get_class_vars(get_class($object)) as $property => $value) {
clear_object($property);
}
}
$object = null;
unset($object);
}

840
pdf/include/gd_adapter.cls.php Executable file
View File

@@ -0,0 +1,840 @@
<?php
/**
* DOMPDF - PHP5 HTML to PDF renderer
*
* File: $RCSfile: gd_adapter.cls.php,v $
* Created on: 2004-06-06
*
* Copyright (c) 2004 - Benj Carson <benjcarson@digitaljunkies.ca>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library in the file LICENSE.LGPL; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*
* Alternatively, you may distribute this software under the terms of the
* PHP License, version 3.0 or later. A copy of this license should have
* been distributed with this file in the file LICENSE.PHP . If this is not
* the case, you can obtain a copy at http://www.php.net/license/3_0.txt.
*
* The latest version of DOMPDF might be available at:
* http://www.dompdf.com/
*
* @link http://www.dompdf.com/
* @copyright 2004 Benj Carson
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @package dompdf
*/
/* $Id: gd_adapter.cls.php 355 2011-01-27 07:44:54Z fabien.menager $ */
/**
* Image rendering interface
*
* Renders to an image format supported by GD (jpeg, gif, png, xpm).
* Not super-useful day-to-day but handy nonetheless
*
* @package dompdf
*/
class GD_Adapter implements Canvas {
/**
* Resoure handle for the image
*
* @var resource
*/
private $_img;
/**
* Image width in pixels
*
* @var int
*/
private $_width;
/**
* Image height in pixels
*
* @var int
*/
private $_height;
/**
* Current page number
*
* @var int
*/
private $_page_number;
/**
* Total number of pages
*
* @var int
*/
private $_page_count;
/**
* Image antialias factor
*
* @var float
*/
private $_aa_factor;
/**
* Allocated colors
*
* @var array
*/
private $_colors;
/**
* Background color
*
* @var int
*/
private $_bg_color;
/**
* Class constructor
*
* @param mixed $size The size of image to create: array(x1,y1,x2,y2) or "letter", "legal", etc.
* @param string $orientation The orientation of the document (either 'landscape' or 'portrait')
* @param float $aa_factor Anti-aliasing factor, 1 for no AA
* @param array $bg_color Image background color: array(r,g,b,a), 0 <= r,g,b,a <= 1
*/
function __construct($size, $orientation = "portrait", $aa_factor = 1, $bg_color = array(1,1,1,0) ) {
if ( !is_array($size) ) {
$size = strtolower($size);
if ( isset(CPDF_Adapter::$PAPER_SIZES[$size]) )
$size = CPDF_Adapter::$PAPER_SIZES[$size];
else
$size = CPDF_Adapter::$PAPER_SIZES["letter"];
}
if ( strtolower($orientation) === "landscape" ) {
list($size[2],$size[3]) = array($size[3],$size[2]);
}
if ( $aa_factor < 1 )
$aa_factor = 1;
$this->_aa_factor = $aa_factor;
$size[2] *= $aa_factor;
$size[3] *= $aa_factor;
$this->_width = $size[2] - $size[0];
$this->_height = $size[3] - $size[1];
$this->_img = imagecreatetruecolor($this->_width, $this->_height);
if ( is_null($bg_color) || !is_array($bg_color) ) {
// Pure white bg
$bg_color = array(1,1,1,0);
}
$this->_bg_color = $this->_allocate_color($bg_color);
imagealphablending($this->_img, true);
imagesavealpha($this->_img, true);
imagefill($this->_img, 0, 0, $this->_bg_color);
}
/**
* Return the GF image resource
*
* @return resource
*/
function get_image() { return $this->_img; }
/**
* Return the image's width in pixels
*
* @return float
*/
function get_width() { return $this->_width / $this->_aa_factor; }
/**
* Return the image's height in pixels
*
* @return float
*/
function get_height() { return $this->_height / $this->_aa_factor; }
/**
* Returns the current page number
* @return int
*/
function get_page_number() { return $this->_page_number; }
/**
* Returns the total number of pages in the document
* @return int
*/
function get_page_count() { return $this->_page_count; }
/**
* Sets the current page number
*
* @param int $num
*/
function set_page_number($num) { $this->_page_number = $num; }
/**
* Sets the page count
*
* @param int $count
*/
function set_page_count($count) { $this->_page_count = $count; }
/**
* Sets the opacity
*
* @param $opacity
* @param $mode
*/
function set_opacity($opacity, $mode = "Normal") {
// FIXME
}
/**
* Allocate a new color. Allocate with GD as needed and store
* previously allocated colors in $this->_colors.
*
* @param array $color The new current color
* @return int The allocated color
*/
private function _allocate_color($color) {
if ( isset($color["c"]) ) {
$color = cmyk_to_rgb($color);
}
// Full opacity if no alpha set
if ( !isset($color[3]) )
$color[3] = 0;
list($r,$g,$b,$a) = $color;
$r *= 255;
$g *= 255;
$b *= 255;
$a *= 127;
// Clip values
$r = $r > 255 ? 255 : $r;
$g = $g > 255 ? 255 : $g;
$b = $b > 255 ? 255 : $b;
$a = $a > 127 ? 127 : $a;
$r = $r < 0 ? 0 : $r;
$g = $g < 0 ? 0 : $g;
$b = $b < 0 ? 0 : $b;
$a = $a < 0 ? 0 : $a;
$key = sprintf("#%02X%02X%02X%02X", $r, $g, $b, $a);
if ( isset($this->_colors[$key]) )
return $this->_colors[$key];
if ( $a != 0 )
$this->_colors[$key] = imagecolorallocatealpha($this->_img, $r, $g, $b, $a);
else
$this->_colors[$key] = imagecolorallocate($this->_img, $r, $g, $b);
return $this->_colors[$key];
}
/**
* Draws a line from x1,y1 to x2,y2
*
* See {@link Style::munge_color()} for the format of the color array.
* See {@link Cpdf::setLineStyle()} for a description of the format of the
* $style parameter (aka dash).
*
* @param float $x1
* @param float $y1
* @param float $x2
* @param float $y2
* @param array $color
* @param float $width
* @param array $style
*/
function line($x1, $y1, $x2, $y2, $color, $width, $style = null) {
// Scale by the AA factor
$x1 *= $this->_aa_factor;
$y1 *= $this->_aa_factor;
$x2 *= $this->_aa_factor;
$y2 *= $this->_aa_factor;
$width *= $this->_aa_factor;
$c = $this->_allocate_color($color);
// Convert the style array if required
if ( !is_null($style) ) {
$gd_style = array();
if ( count($style) == 1 ) {
for ($i = 0; $i < $style[0] * $this->_aa_factor; $i++) {
$gd_style[] = $c;
}
for ($i = 0; $i < $style[0] * $this->_aa_factor; $i++) {
$gd_style[] = $this->_bg_color;
}
} else {
$i = 0;
foreach ($style as $length) {
if ( $i % 2 == 0 ) {
// 'On' pattern
for ($i = 0; $i < $style[0] * $this->_aa_factor; $i++)
$gd_style[] = $c;
} else {
// Off pattern
for ($i = 0; $i < $style[0] * $this->_aa_factor; $i++)
$gd_style[] = $this->_bg_color;
}
$i++;
}
}
imagesetstyle($this->_img, $gd_style);
$c = IMG_COLOR_STYLED;
}
imagesetthickness($this->_img, $width);
imageline($this->_img, $x1, $y1, $x2, $y2, $c);
}
/**
* Draws a rectangle at x1,y1 with width w and height h
*
* See {@link Style::munge_color()} for the format of the color array.
* See {@link Cpdf::setLineStyle()} for a description of the $style
* parameter (aka dash)
*
* @param float $x1
* @param float $y1
* @param float $w
* @param float $h
* @param array $color
* @param float $width
* @param array $style
*/
function rectangle($x1, $y1, $w, $h, $color, $width, $style = null) {
// Scale by the AA factor
$x1 *= $this->_aa_factor;
$y1 *= $this->_aa_factor;
$w *= $this->_aa_factor;
$h *= $this->_aa_factor;
$c = $this->_allocate_color($color);
// Convert the style array if required
if ( !is_null($style) ) {
$gd_style = array();
foreach ($style as $length) {
for ($i = 0; $i < $length; $i++) {
$gd_style[] = $c;
}
}
imagesetstyle($this->_img, $gd_style);
$c = IMG_COLOR_STYLED;
}
imagesetthickness($this->_img, $width);
imagerectangle($this->_img, $x1, $y1, $x1 + $w, $y1 + $h, $c);
}
/**
* Draws a filled rectangle at x1,y1 with width w and height h
*
* See {@link Style::munge_color()} for the format of the color array.
*
* @param float $x1
* @param float $y1
* @param float $w
* @param float $h
* @param array $color
*/
function filled_rectangle($x1, $y1, $w, $h, $color) {
// Scale by the AA factor
$x1 *= $this->_aa_factor;
$y1 *= $this->_aa_factor;
$w *= $this->_aa_factor;
$h *= $this->_aa_factor;
$c = $this->_allocate_color($color);
imagefilledrectangle($this->_img, $x1, $y1, $x1 + $w, $y1 + $h, $c);
}
/**
* Starts a clipping rectangle at x1,y1 with width w and height h
*
* @param float $x1
* @param float $y1
* @param float $w
* @param float $h
*/
function clipping_rectangle($x1, $y1, $w, $h) {
// @todo
}
/**
* Ends the last clipping shape
*/
function clipping_end() {
// @todo
}
function save() {
// @todo
}
function restore() {
// @todo
}
function rotate($angle, $x, $y) {
// @todo
}
function skew($angle_x, $angle_y, $x, $y) {
// @todo
}
function scale($s_x, $s_y, $x, $y) {
// @todo
}
function translate($t_x, $t_y) {
// @todo
}
function transform($a, $b, $c, $d, $e, $f) {
// @todo
}
/**
* Draws a polygon
*
* The polygon is formed by joining all the points stored in the $points
* array. $points has the following structure:
* <code>
* array(0 => x1,
* 1 => y1,
* 2 => x2,
* 3 => y2,
* ...
* );
* </code>
*
* See {@link Style::munge_color()} for the format of the color array.
* See {@link Cpdf::setLineStyle()} for a description of the $style
* parameter (aka dash)
*
* @param array $points
* @param array $color
* @param float $width
* @param array $style
* @param bool $fill Fills the polygon if true
*/
function polygon($points, $color, $width = null, $style = null, $fill = false) {
// Scale each point by the AA factor
foreach (array_keys($points) as $i)
$points[$i] *= $this->_aa_factor;
$c = $this->_allocate_color($color);
// Convert the style array if required
if ( !is_null($style) && !$fill ) {
$gd_style = array();
foreach ($style as $length) {
for ($i = 0; $i < $length; $i++) {
$gd_style[] = $c;
}
}
imagesetstyle($this->_img, $gd_style);
$c = IMG_COLOR_STYLED;
}
imagesetthickness($this->_img, $width);
if ( $fill )
imagefilledpolygon($this->_img, $points, count($points) / 2, $c);
else
imagepolygon($this->_img, $points, count($points) / 2, $c);
}
/**
* Draws a circle at $x,$y with radius $r
*
* See {@link Style::munge_color()} for the format of the color array.
* See {@link Cpdf::setLineStyle()} for a description of the $style
* parameter (aka dash)
*
* @param float $x
* @param float $y
* @param float $r
* @param array $color
* @param float $width
* @param array $style
* @param bool $fill Fills the circle if true
*/
function circle($x, $y, $r, $color, $width = null, $style = null, $fill = false) {
// Scale by the AA factor
$x *= $this->_aa_factor;
$y *= $this->_aa_factor;
$r *= $this->_aa_factor;
$c = $this->_allocate_color($color);
// Convert the style array if required
if ( !is_null($style) && !$fill ) {
$gd_style = array();
foreach ($style as $length) {
for ($i = 0; $i < $length; $i++) {
$gd_style[] = $c;
}
}
imagesetstyle($this->_img, $gd_style);
$c = IMG_COLOR_STYLED;
}
imagesetthickness($this->_img, $width);
if ( $fill )
imagefilledellipse($this->_img, $x, $y, $r, $r, $c);
else
imageellipse($this->_img, $x, $y, $r, $r, $c);
}
/**
* Add an image to the pdf.
*
* The image is placed at the specified x and y coordinates with the
* given width and height.
*
* @param string $img_url the path to the image
* @param string $img_type the type (e.g. extension) of the image
* @param float $x x position
* @param float $y y position
* @param int $w width (in pixels)
* @param int $h height (in pixels)
*/
function image($img_url, $img_type, $x, $y, $w, $h) {
switch ($img_type) {
case "png":
$src = @imagecreatefrompng($img_url);
break;
case "gif":
$src = @imagecreatefromgif($img_url);
break;
case "bmp":
$src = @imagecreatefrombmp($img_url);
break;
case "jpg":
case "jpeg":
$src = @imagecreatefromjpeg($img_url);
break;
default:
break;
}
if ( !$src )
return; // Probably should add to $_dompdf_errors or whatever here
// Scale by the AA factor
$x *= $this->_aa_factor;
$y *= $this->_aa_factor;
$w *= $this->_aa_factor;
$h *= $this->_aa_factor;
$img_w = imagesx($src);
$img_h = imagesy($src);
imagecopyresampled($this->_img, $src, $x, $y, 0, 0, $w, $h, $img_w, $img_h);
}
/**
* Writes text at the specified x and y coordinates
*
* See {@link Style::munge_color()} for the format of the color array.
*
* @param float $x
* @param float $y
* @param string $text the text to write
* @param string $font the font file to use
* @param float $size the font size, in points
* @param array $color
* @param float $adjust word spacing adjustment
* @param float $angle Text angle
*/
function text($x, $y, $text, $font, $size, $color = array(0,0,0), $word_spacing = 0, $char_spacing = 0, $angle = 0) {
// Scale by the AA factor
$x *= $this->_aa_factor;
$y *= $this->_aa_factor;
$size *= $this->_aa_factor;
$h = $this->get_font_height($font, $size);
$c = $this->_allocate_color($color);
$text = mb_encode_numericentity($text, array(0x0080, 0xff, 0, 0xff), 'UTF-8');
if ( strpos($font, '.ttf') === false )
$font .= ".ttf";
// FIXME: word spacing
@imagettftext($this->_img, $size, $angle, $x, $y + $h, $c, $font, $text);
}
function javascript($code) {
// Not implemented
}
/**
* Add a named destination (similar to <a name="foo">...</a> in html)
*
* @param string $anchorname The name of the named destination
*/
function add_named_dest($anchorname) {
// Not implemented
}
/**
* Add a link to the pdf
*
* @param string $url The url to link to
* @param float $x The x position of the link
* @param float $y The y position of the link
* @param float $width The width of the link
* @param float $height The height of the link
*/
function add_link($url, $x, $y, $width, $height) {
// Not implemented
}
/**
* Add meta information to the PDF
*
* @param string $label label of the value (Creator, Producer, etc.)
* @param string $value the text to set
*/
function add_info($label, $value) {
// N/A
}
/**
* Calculates text size, in points
*
* @param string $text the text to be sized
* @param string $font the desired font
* @param float $size the desired font size
* @param float $spacing word spacing, if any
* @return float
*/
function get_text_width($text, $font, $size, $word_spacing = 0, $char_spacing = 0) {
if ( strpos($font, '.ttf') === false )
$font .= ".ttf";
$text = mb_encode_numericentity($text, array(0x0080, 0xffff, 0, 0xffff), 'UTF-8');
// FIXME: word spacing
list($x1,,$x2) = @imagettfbbox($size, 0, $font, $text);
return $x2 - $x1;
}
/**
* Calculates font height, in points
*
* @param string $font
* @param float $size
* @return float
*/
function get_font_height($font, $size) {
if ( strpos($font, '.ttf') === false )
$font .= ".ttf";
// FIXME: word spacing
list(,$y2,,,,$y1) = imagettfbbox($size, 0, $font, "MXjpqytfhl"); // Test string with ascenders, descenders and caps
return ($y2 - $y1) * DOMPDF_FONT_HEIGHT_RATIO;
}
/**
* Starts a new page
*
* Subsequent drawing operations will appear on the new page.
*/
function new_page() {
$this->_page_number++;
$this->_page_count++;
}
function open_object(){
// N/A
}
function close_object(){
// N/A
}
function add_object(){
// N/A
}
function page_text(){
// N/A
}
/**
* Streams the image directly to the browser
*
* @param string $filename the name of the image file (ignored)
* @param array $options associative array, 'type' => jpeg|jpg|png, 'quality' => 0 - 100 (jpeg only)
*/
function stream($filename, $options = null) {
// Perform any antialiasing
if ( $this->_aa_factor != 1 ) {
$dst_w = $this->_width / $this->_aa_factor;
$dst_h = $this->_height / $this->_aa_factor;
$dst = imagecreatetruecolor($dst_w, $dst_h);
imagecopyresampled($dst, $this->_img, 0, 0, 0, 0,
$dst_w, $dst_h,
$this->_width, $this->_height);
} else {
$dst = $this->_img;
}
if ( !isset($options["type"]) )
$options["type"] = "png";
$type = strtolower($options["type"]);
header("Cache-Control: private");
switch ($type) {
case "jpg":
case "jpeg":
if ( !isset($options["quality"]) )
$options["quality"] = 75;
header("Content-type: image/jpeg");
imagejpeg($dst, '', $options["quality"]);
break;
case "png":
default:
header("Content-type: image/png");
imagepng($dst);
break;
}
if ( $this->_aa_factor != 1 )
imagedestroy($dst);
}
/**
* Returns the PNG as a string
*
* @param array $options associative array, 'type' => jpeg|jpg|png, 'quality' => 0 - 100 (jpeg only)
* @return string
*/
function output($options = null) {
if ( $this->_aa_factor != 1 ) {
$dst_w = $this->_width / $this->_aa_factor;
$dst_h = $this->_height / $this->_aa_factor;
$dst = imagecreatetruecolor($dst_w, $dst_h);
imagecopyresampled($dst, $this->_img, 0, 0, 0, 0,
$dst_w, $dst_h,
$this->_width, $this->_height);
} else {
$dst = $this->_img;
}
if ( !isset($options["type"]) )
$options["type"] = "png";
$type = $options["type"];
ob_start();
switch ($type) {
case "jpg":
case "jpeg":
if ( !isset($options["quality"]) )
$options["quality"] = 75;
imagejpeg($dst, '', $options["quality"]);
break;
case "png":
default:
imagepng($dst);
break;
}
$image = ob_get_clean();
if ( $this->_aa_factor != 1 )
imagedestroy($dst);
return $image;
}
}

231
pdf/include/image_cache.cls.php Executable file
View File

@@ -0,0 +1,231 @@
<?php
/**
* DOMPDF - PHP5 HTML to PDF renderer
*
* File: $RCSfile: image_cache.cls.php,v $
* Created on: 2004-08-08
*
* Copyright (c) 2004 - Benj Carson <benjcarson@digitaljunkies.ca>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library in the file LICENSE.LGPL; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*
* Alternatively, you may distribute this software under the terms of the
* PHP License, version 3.0 or later. A copy of this license should have
* been distributed with this file in the file LICENSE.PHP . If this is not
* the case, you can obtain a copy at http://www.php.net/license/3_0.txt.
*
* The latest version of DOMPDF might be available at:
* http://www.dompdf.com/
*
* @link http://www.dompdf.com/
* @copyright 2004 Benj Carson
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @contributor Helmut Tischer <htischer@weihenstephan.org>
* @package dompdf
*
* Changes
* @contributor Helmut Tischer <htischer@weihenstephan.org>
* @version 0.5.1.htischer.20090507
* - On getting type of images don't require any file endings
* and don't strip off url parameters,
* to allowing dynamically generated sites with image id
* in url parameters and not at end of url or missing file extension
* @contributor Helmut Tischer <htischer@weihenstephan.org>
* @version dompdf_trunk_with_helmut_mods.20090524
* - Made debug messages more individually configurable
* @version 20090622
* - don't cache broken image, but refer to original broken image replacement
*/
/* $Id: image_cache.cls.php 354 2011-01-24 21:59:54Z fabien.menager $ */
/**
* Static class that resolves image urls and downloads and caches
* remote images if required.
*
* @access private
* @package dompdf
*/
class Image_Cache {
/**
* Array of downloaded images. Cached so that identical images are
* not needlessly downloaded.
*
* @var array
*/
static protected $_cache = array();
/**
* Resolve and fetch an image for use.
*
* @param string $url The url of the image
* @param string $proto Default protocol if none specified in $url
* @param string $host Default host if none specified in $url
* @param string $base_path Default path if none specified in $url
* @return array An array with two elements: The local path to the image and the image extension
*/
static function resolve_url($url, $proto, $host, $base_path) {
global $_dompdf_warnings;
$parsed_url = explode_url($url);
$DEBUGPNG=DEBUGPNG; //=DEBUGPNG; Allow override of global setting for ad hoc debug
$full_url_dbg = '';
//debugpng
if ($DEBUGPNG) print 'resolve_url('.$url.','.$proto.','.$host.','.$base_path.')('.$parsed_url['protocol'].')';
$remote = ($proto != "" && $proto !== "file://");
$remote = $remote || ($parsed_url['protocol'] != "");
$datauri = strpos($parsed_url['protocol'], "data:") === 0;
if ( !DOMPDF_ENABLE_REMOTE && $remote && !$datauri ) {
$resolved_url = DOMPDF_LIB_DIR . "/res/broken_image.png";
$ext = "png";
//debugpng
if ($DEBUGPNG) $full_url_dbg = '(blockedremote)';
}
else if ( DOMPDF_ENABLE_REMOTE && $remote || $datauri ) {
// Download remote files to a temporary directory
$full_url = build_url($proto, $host, $base_path, $url);
if ( isset(self::$_cache[$full_url]) ) {
list($resolved_url,$ext) = self::$_cache[$full_url];
//debugpng
if ($DEBUGPNG) $full_url_dbg = $full_url.'(cache)';
} else {
$resolved_url = tempnam(DOMPDF_TEMP_DIR, "ca_dompdf_img_");
//debugpng
if ($DEBUGPNG) echo $resolved_url . "\n";
if ($datauri) {
if ($parsed_data_uri = parse_data_uri($url)) {
$image = $parsed_data_uri['data'];
list(, $ext) = explode('/', $parsed_data_uri['mime'], 2);
}
}
else {
$old_err = set_error_handler("record_warnings");
$image = file_get_contents($full_url);
restore_error_handler();
}
if ( strlen($image) == 0 ) {
//target image not found
$resolved_url = DOMPDF_LIB_DIR . "/res/broken_image.png";
$ext = "png";
//debugpng
if ($DEBUGPNG) $full_url_dbg = $full_url.'(missing)';
} else {
file_put_contents($resolved_url, $image);
//e.g. fetch.php?media=url.jpg&cache=1
//- Image file name might be one of the dynamic parts of the url, don't strip off!
// if ( preg_match("/.*\.(\w+)/",$url,$match) ) $ext = $match[1];
//- a remote url does not need to have a file extension at all
//- local cached file does not have a matching file extension
//Therefore get image type from the content
$imagedim = dompdf_getimagesize($resolved_url);
if( $imagedim[0] && $imagedim[1] &&
in_array($imagedim[2], array(IMAGETYPE_GIF, IMAGETYPE_PNG, IMAGETYPE_JPEG, IMAGETYPE_BMP)) ) {
//target image is valid
$imagetypes = array('','gif','jpeg','png','swf','psd','bmp');
$ext = $imagetypes[$imagedim[2]];
if ( rename($resolved_url,$resolved_url.'.'.$ext) ) {
$resolved_url .= '.'.$ext;
}
//Don't put replacement image into cache - otherwise it will be deleted on cache cleanup.
//Only execute on successfull caching of remote image.
self::$_cache[$full_url] = array($resolved_url,$ext);
} else {
//target image is not valid.
unlink($resolved_url);
$resolved_url = DOMPDF_LIB_DIR . "/res/broken_image.png";
$ext = "png";
}
}
}
} else {
$resolved_url = build_url($proto, $host, $base_path, $url);
if ($DEBUGPNG) print 'build_url('.$proto.','.$host.','.$base_path.','.$url.')('.$resolved_url.')';
if ( !preg_match("/.*\.(\w+)/",$url,$match) ) {
//debugpng
if ($DEBUGPNG) print '[resolve_url exception '.$url.']';
throw new DOMPDF_Exception("Unknown image type: $url.");
}
$ext = $match[1];
//debugpng
if ($DEBUGPNG) $full_url_dbg = '(local)';
}
if ( !is_readable($resolved_url) || !filesize($resolved_url) ) {
//debugpng
if ($DEBUGPNG) $full_url_dbg .= '(nocache'.$resolved_url.')';
$_dompdf_warnings[] = "File " .$resolved_url . " is not readable or is an empty file.\n";
$resolved_url = DOMPDF_LIB_DIR . "/res/broken_image.png";
$ext = "png";
}
//debugpng
if ($DEBUGPNG) print '[resolve_url '.$url.'|'.$full_url_dbg.'|'.$resolved_url.'|'.$ext.']';
return array($resolved_url, $ext);
}
/**
* Unlink all cached images (i.e. temporary images either downloaded
* or converted)
*/
static function clear() {
if ( count(self::$_cache) ) {
while ($entry = array_shift(self::$_cache)) {
list($file, $ext) = $entry;
//debugpng
if (DEBUGPNG) print '[clear unlink '.$file.']';
if (!DEBUGKEEPTEMP)
//XXX: Should we have some kind of fallback or warning if unlink() fails?
unlink($file);
}
}
}
}

View File

@@ -0,0 +1,116 @@
<?php
/**
* DOMPDF - PHP5 HTML to PDF renderer
*
* File: $RCSfile: image_frame_decorator.cls.php,v $
* Created on: 2004-08-08
*
* Copyright (c) 2004 - Benj Carson <benjcarson@digitaljunkies.ca>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library in the file LICENSE.LGPL; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*
* Alternatively, you may distribute this software under the terms of the
* PHP License, version 3.0 or later. A copy of this license should have
* been distributed with this file in the file LICENSE.PHP . If this is not
* the case, you can obtain a copy at http://www.php.net/license/3_0.txt.
*
* The latest version of DOMPDF might be available at:
* http://www.dompdf.com/
*
* @link http://www.dompdf.com/
* @copyright 2004 Benj Carson
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @contributor Helmut Tischer <htischer@weihenstephan.org>
* @package dompdf
*
* Changes
* @contributor Helmut Tischer <htischer@weihenstephan.org>
* @version 0.5.1.htischer.20090507
* - add optional debug output
*/
/* $Id: image_frame_decorator.cls.php 283 2010-07-19 17:57:40Z fabien.menager $ */
/**
* Decorates frames for image layout and rendering
*
* @access private
* @package dompdf
*/
class Image_Frame_Decorator extends Frame_Decorator {
/**
* The path to the image file (note that remote images are
* downloaded locally to DOMPDF_TEMP_DIR).
*
* @var string
*/
protected $_image_url;
/**
* The image's file extension (i.e. png, jpeg, gif)
*
* @var string
*/
protected $_image_ext;
/**
* Class constructor
*
* @param Frame $frame the frame to decorate
* @param DOMPDF $dompdf the document's dompdf object (required to resolve relative & remote urls)
*/
function __construct(Frame $frame, DOMPDF $dompdf) {
global $_dompdf_warnings;
parent::__construct($frame, $dompdf);
$url = $frame->get_node()->getAttribute("src");
//debugpng
if (DEBUGPNG) print '[__construct '.$url.']';
list($this->_image_url, $this->_image_ext) = Image_Cache::resolve_url($url,
$dompdf->get_protocol(),
$dompdf->get_host(),
$dompdf->get_base_path());
if ( strrpos( $this->_image_url, DOMPDF_LIB_DIR . "/res/broken_image.png", 0) !== false &&
$alt = $frame->get_node()->getAttribute("alt") ) {
$style = $frame->get_style();
$style->width = (4/3)*Font_Metrics::get_text_width($alt, $style->font_family, $style->font_size, $style->word_spacing);
$style->height = Font_Metrics::get_font_height($style->font_family, $style->font_size);
}
}
/**
* Return the image's url
*
* @return string The url of this image
*/
function get_image_url() {
return $this->_image_url;
}
/**
* Return the image's file extension
*
* @return string The image's file extension
*/
function get_image_ext() {
return $this->_image_ext;
}
}

View File

@@ -0,0 +1,162 @@
<?php
/**
* DOMPDF - PHP5 HTML to PDF renderer
*
* File: $RCSfile: image_frame_reflower.cls.php,v $
* Created on: 2004-08-08
*
* Copyright (c) 2004 - Benj Carson <benjcarson@digitaljunkies.ca>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library in the file LICENSE.LGPL; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*
* Alternatively, you may distribute this software under the terms of the
* PHP License, version 3.0 or later. A copy of this license should have
* been distributed with this file in the file LICENSE.PHP . If this is not
* the case, you can obtain a copy at http://www.php.net/license/3_0.txt.
*
* The latest version of DOMPDF might be available at:
* http://www.dompdf.com/
*
* @link http://www.dompdf.com/
* @copyright 2004 Benj Carson
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @contributor Helmut Tischer <htischer@weihenstephan.org>
* @package dompdf
*
* Changes
* @contributor Helmut Tischer <htischer@weihenstephan.org>
* @version 0.5.1.htischer.20090507
* - Fix image size as percent of wrapping box
* - Fix arithmetic rounding of image size
* - Time consuming additional image file scan only when really needed
*/
/* $Id: image_frame_reflower.cls.php 357 2011-01-30 20:56:46Z fabien.menager $ */
/**
* Image reflower class
*
* @access private
* @package dompdf
*/
class Image_Frame_Reflower extends Frame_Reflower {
function __construct(Image_Frame_Decorator $frame) {
parent::__construct($frame);
}
function reflow(Frame_Decorator $block = null) {
$this->_frame->position();
//FLOAT
//$frame = $this->_frame;
//$page = $frame->get_root();
//if (DOMPDF_ENABLE_CSS_FLOAT && $frame->get_style()->float !== "none" ) {
// $page->add_floating_frame($this);
//}
// Set the frame's width
$this->get_min_max_width();
if ( $block ) {
$block->add_frame_to_line($this->_frame);
}
}
function get_min_max_width() {
if (DEBUGPNG) {
// Determine the image's size. Time consuming. Only when really needed?
list($img_width, $img_height) = dompdf_getimagesize($this->_frame->get_image_url());
print "get_min_max_width() ".
$this->_frame->get_style()->width.' '.
$this->_frame->get_style()->height.';'.
$this->_frame->get_parent()->get_style()->width." ".
$this->_frame->get_parent()->get_style()->height.";".
$this->_frame->get_parent()->get_parent()->get_style()->width.' '.
$this->_frame->get_parent()->get_parent()->get_style()->height.';'.
$img_width. ' '.
$img_height.'|' ;
}
$style = $this->_frame->get_style();
//own style auto or invalid value: use natural size in px
//own style value: ignore suffix text including unit, use given number as px
//own style %: walk up parent chain until found available space in pt; fill available space
//
//special ignored unit: e.g. 10ex: e treated as exponent; x ignored; 10e completely invalid ->like auto
$width = ($style->width > 0 ? $style->width : 0);
if ( is_percent($width) ) {
$t = 0.0;
for ($f = $this->_frame->get_parent(); $f; $f = $f->get_parent()) {
$t = (float)($f->get_style()->width); //always in pt
if ((float)$t != 0) {
break;
}
}
$width = ((float)rtrim($width,"%") * $t)/100; //maybe 0
} elseif ( !mb_strpos($width, 'pt') ) {
// Don't set image original size if "%" branch was 0 or size not given.
// Otherwise aspect changed on %/auto combination for width/height
// Resample according to px per inch
// See also List_Bullet_Image_Frame_Decorator::__construct
$width = (float)($width * 72) / DOMPDF_DPI;
}
$height = ($style->height > 0 ? $style->height : 0);
if ( is_percent($height) ) {
$t = 0.0;
for ($f = $this->_frame->get_parent(); $f; $f = $f->get_parent()) {
$t = (float)($f->get_style()->height); //always in pt
if ((float)$t != 0) {
break;
}
}
$height = ((float)rtrim($height,"%") * $t)/100; //maybe 0
} elseif ( !mb_strpos($height, 'pt') ) {
// Don't set image original size if "%" branch was 0 or size not given.
// Otherwise aspect changed on %/auto combination for width/height
// Resample according to px per inch
// See also List_Bullet_Image_Frame_Decorator::__construct
$height = (float)($height * 72) / DOMPDF_DPI;
}
if ($width == 0 || $height == 0) {
// Determine the image's size. Time consuming. Only when really needed!
list($img_width, $img_height) = dompdf_getimagesize($this->_frame->get_image_url());
// don't treat 0 as error. Can be downscaled or can be catched elsewhere if image not readable.
// Resample according to px per inch
// See also List_Bullet_Image_Frame_Decorator::__construct
if ($width == 0 && $height == 0) {
$width = (float)($img_width * 72) / DOMPDF_DPI;
$height = (float)($img_height * 72) / DOMPDF_DPI;
} elseif ($height == 0 && $width != 0) {
$height = ($width / $img_width) * $img_height; //keep aspect ratio
} elseif ($width == 0 && $height != 0) {
$width = ($height / $img_height) * $img_width; //keep aspect ratio
}
}
if (DEBUGPNG) print $width.' '.$height.';';
$style->width = $width . "pt";
$style->height = $height . "pt";
return array( $width, $width, "min" => $width, "max" => $width);
}
}

View File

@@ -0,0 +1,95 @@
<?php
/**
* DOMPDF - PHP5 HTML to PDF renderer
*
* File: $RCSfile: image_renderer.cls.php,v $
* Created on: 2004-08-04
*
* Copyright (c) 2004 - Benj Carson <benjcarson@digitaljunkies.ca>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library in the file LICENSE.LGPL; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*
* Alternatively, you may distribute this software under the terms of the
* PHP License, version 3.0 or later. A copy of this license should have
* been distributed with this file in the file LICENSE.PHP . If this is not
* the case, you can obtain a copy at http://www.php.net/license/3_0.txt.
*
* The latest version of DOMPDF might be available at:
* http://www.dompdf.com/
*
* @link http://www.dompdf.com/
* @copyright 2004 Benj Carson
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @package dompdf
*/
/* $Id: image_renderer.cls.php 325 2010-11-07 18:05:59Z fabien.menager $ */
/**
* Image renderer
*
* @access private
* @package dompdf
*/
class Image_Renderer extends Block_Renderer {
function render(Frame $frame) {
// Render background & borders
$style = $frame->get_style();
$cb = $frame->get_containing_block();
list($x, $y, $w, $h) = $frame->get_border_box();
$this->_set_opacity( $frame->get_opacity( $style->opacity ) );
// Handle the last child
if ( ($bg = $style->background_color) !== "transparent" )
$this->_canvas->filled_rectangle( $x + $widths[3], $y + $widths[0], $w, $h, $bg);
if ( ($url = $style->background_image) && $url !== "none" )
$this->_background_image($url, $x + $widths[3], $y + $widths[0], $w, $h, $style);
$this->_render_border($frame);
$this->_render_outline($frame);
list($x, $y) = $frame->get_padding_box();
$x += $style->length_in_pt($style->padding_left, $cb["w"]);
$y += $style->length_in_pt($style->padding_top, $cb["h"]);
$w = $style->length_in_pt($style->width, $cb["w"]);
$h = $style->length_in_pt($style->height, $cb["h"]);
if ( strrpos( $frame->get_image_url(), DOMPDF_LIB_DIR . "/res/broken_image.png", 0) !== false &&
$alt = $frame->get_node()->getAttribute("alt") ) {
$font = $style->font_family;
$size = $style->font_size;
$spacing = $style->word_spacing;
$this->_canvas->text($x, $y, $alt,
$font, $size,
$style->color, $spacing);
}
else {
$this->_canvas->image( $frame->get_image_url(), $frame->get_image_ext(), $x, $y, $w, $h);
}
if (DEBUG_LAYOUT && DEBUG_LAYOUT_BLOCKS) {
$this->_debug_layout($frame->get_border_box(), "blue");
if (DEBUG_LAYOUT_PADDINGBOX) {
$this->_debug_layout($frame->get_padding_box(), "blue", array(0.5, 0.5));
}
}
}
}

View File

@@ -0,0 +1,112 @@
<?php
/**
* DOMPDF - PHP5 HTML to PDF renderer
*
* File: $RCSfile: inline_frame_decorator.cls.php,v $
* Created on: 2004-06-02
*
* Copyright (c) 2004 - Benj Carson <benjcarson@digitaljunkies.ca>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library in the file LICENSE.LGPL; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*
* Alternatively, you may distribute this software under the terms of the
* PHP License, version 3.0 or later. A copy of this license should have
* been distributed with this file in the file LICENSE.PHP . If this is not
* the case, you can obtain a copy at http://www.php.net/license/3_0.txt.
*
* The latest version of DOMPDF might be available at:
* http://www.dompdf.com/
*
* @link http://www.dompdf.com/
* @copyright 2004 Benj Carson
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @contributor Helmut Tischer <htischer@weihenstephan.org>
* @package dompdf
*
* Changes
* @contributor Helmut Tischer <htischer@weihenstephan.org>
* @version 20090610
* - don't repeat non repeatable background images after a line break
*/
/* $Id: inline_frame_decorator.cls.php 252 2010-04-19 08:10:45Z flaviencrochard $ */
/**
* Decorates frames for inline layout
*
* @access private
* @package dompdf
*/
class Inline_Frame_Decorator extends Frame_Decorator {
function __construct(Frame $frame, DOMPDF $dompdf) { parent::__construct($frame, $dompdf); }
function split($frame = null, $force_pagebreak = false) {
if ( is_null($frame) ) {
$this->get_parent()->split($this, $force_pagebreak);
return;
}
if ( $frame->get_parent() !== $this )
throw new DOMPDF_Exception("Unable to split: frame is not a child of this one.");
$split = $this->copy( $this->_frame->get_node()->cloneNode() );
$this->get_parent()->insert_child_after($split, $this);
// Unset the current node's right style properties
$style = $this->_frame->get_style();
$style->margin_right = "0";
$style->padding_right = "0";
$style->border_right_width = "0";
// Unset the split node's left style properties since we don't want them
// to propagate
$style = $split->get_style();
$style->margin_left = "0";
$style->padding_left = "0";
$style->border_left_width = "0";
//On continuation of inline element on next line,
//don't repeat non-vertically repeatble background images
//See e.g. in testcase image_variants, long desriptions
if ( ($url = $style->background_image) && $url !== "none"
&& ($repeat = $style->background_repeat) && $repeat !== "repeat" && $repeat !== "repeat-y"
) {
$style->background_image = "none";
}
// Add $frame and all following siblings to the new split node
$iter = $frame;
while ($iter) {
$frame = $iter;
$iter = $iter->get_next_sibling();
$frame->reset();
$split->append_child($frame);
}
$page_breaks = array("always", "left", "right");
$frame_style = $frame->get_style();
if( $force_pagebreak ||
in_array($frame_style->page_break_before, $page_breaks) ||
in_array($frame_style->page_break_after, $page_breaks) ) {
$this->get_parent()->split($split, true);
}
}
}

View File

@@ -0,0 +1,99 @@
<?php
/**
* DOMPDF - PHP5 HTML to PDF renderer
*
* File: $RCSfile: inline_frame_reflower.cls.php,v $
* Created on: 2004-06-17
*
* Copyright (c) 2004 - Benj Carson <benjcarson@digitaljunkies.ca>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library in the file LICENSE.LGPL; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*
* Alternatively, you may distribute this software under the terms of the
* PHP License, version 3.0 or later. A copy of this license should have
* been distributed with this file in the file LICENSE.PHP . If this is not
* the case, you can obtain a copy at http://www.php.net/license/3_0.txt.
*
* The latest version of DOMPDF might be available at:
* http://www.dompdf.com/
*
* @link http://www.dompdf.com/
* @copyright 2004 Benj Carson
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @package dompdf
*/
/* $Id: inline_frame_reflower.cls.php 357 2011-01-30 20:56:46Z fabien.menager $ */
/**
* Reflows inline frames
*
* @access private
* @package dompdf
*/
class Inline_Frame_Reflower extends Frame_Reflower {
function __construct(Frame $frame) { parent::__construct($frame); }
//........................................................................
function reflow(Frame_Decorator $block = null) {
$frame = $this->_frame;
// Check if a page break is forced
$page = $frame->get_root();
$page->check_forced_page_break($frame);
if ( $page->is_full() )
return;
$style = $frame->get_style();
// Generated content
$this->_set_content();
$frame->position();
$cb = $frame->get_containing_block();
// Add our margin, padding & border to the first and last children
if ( ($f = $frame->get_first_child()) && $f instanceof Text_Frame_Decorator ) {
$f_style = $f->get_style();
$f_style->margin_left = $style->margin_left;
$f_style->padding_left = $style->padding_left;
$f_style->border_left = $style->border_left;
}
if ( ($l = $frame->get_last_child()) && $l instanceof Text_Frame_Decorator ) {
$l_style = $l->get_style();
$l_style->margin_right = $style->margin_right;
$l_style->padding_right = $style->padding_right;
$l_style->border_right = $style->border_right;
}
if ( $block ) {
$block->add_frame_to_line($this->_frame);
}
// Set the containing blocks and reflow each child. The containing
// block is not changed by line boxes.
foreach ( $frame->get_children() as $child ) {
$child->set_containing_block($cb);
$child->reflow($block);
}
}
}

View File

@@ -0,0 +1,102 @@
<?php
/**
* DOMPDF - PHP5 HTML to PDF renderer
*
* File: $RCSfile: inline_positioner.cls.php,v $
* Created on: 2004-06-08
*
* Copyright (c) 2004 - Benj Carson <benjcarson@digitaljunkies.ca>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library in the file LICENSE.LGPL; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*
* Alternatively, you may distribute this software under the terms of the
* PHP License, version 3.0 or later. A copy of this license should have
* been distributed with this file in the file LICENSE.PHP . If this is not
* the case, you can obtain a copy at http://www.php.net/license/3_0.txt.
*
* The latest version of DOMPDF might be available at:
* http://www.dompdf.com/
*
* @link http://www.dompdf.com/
* @copyright 2004 Benj Carson
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @package dompdf
*/
/* $Id: inline_positioner.cls.php 357 2011-01-30 20:56:46Z fabien.menager $ */
/**
* Positions inline frames
*
* @access private
* @package dompdf
*/
class Inline_Positioner extends Positioner {
function __construct(Frame_Decorator $frame) { parent::__construct($frame); }
//........................................................................
function position() {
// Find our nearest block level parent and access its lines property.
$p = $this->_frame->find_block_parent();
// Debugging code:
// pre_r("\nPositioning:");
// pre_r("Me: " . $this->_frame->get_node()->nodeName . " (" . spl_object_hash($this->_frame->get_node()) . ")");
// pre_r("Parent: " . $p->get_node()->nodeName . " (" . spl_object_hash($p->get_node()) . ")");
// End debugging
if ( !$p )
throw new DOMPDF_Exception("No block-level parent found. Not good.");
$f = $this->_frame;
$cb = $f->get_containing_block();
$style = $f->get_style();
$line = $p->get_current_line();
// Skip the page break if in a fixed position element
$is_fixed = false;
while($f = $f->get_parent()) {
if($f->get_style()->position === "fixed") {
$is_fixed = true;
break;
}
}
$f = $this->_frame;
if ( !$is_fixed && $f->get_parent() &&
$f->get_parent() instanceof Inline_Frame_Decorator &&
$f->get_node()->nodeName === "#text" ) {
$min_max = $f->get_reflower()->get_min_max_width();
$initialcb = $f->get_root()->get_containing_block();
$height = $style->length_in_pt($style->height, $initialcb["h"]);
// If the frame doesn't fit in the current line, a line break occurs
if ( $min_max["min"] > ($cb["w"]-$line["left"]-$line["w"]-$line["right"]) ) {
$p->add_line();
}
}
$this->_frame->set_position($cb["x"] + $line["w"], $line["y"]);
}
}

View File

@@ -0,0 +1,207 @@
<?php
/**
* DOMPDF - PHP5 HTML to PDF renderer
*
* File: $RCSfile: inline_renderer.cls.php,v $
* Created on: 2004-06-30
*
* Copyright (c) 2004 - Benj Carson <benjcarson@digitaljunkies.ca>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library in the file LICENSE.LGPL; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*
* Alternatively, you may distribute this software under the terms of the
* PHP License, version 3.0 or later. A copy of this license should have
* been distributed with this file in the file LICENSE.PHP . If this is not
* the case, you can obtain a copy at http://www.php.net/license/3_0.txt.
*
* The latest version of DOMPDF might be available at:
* http://www.dompdf.com/
*
* @link http://www.dompdf.com/
* @copyright 2004 Benj Carson
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @package dompdf
*/
/* $Id: inline_renderer.cls.php 332 2010-11-27 14:06:34Z fabien.menager $ */
/**
* Renders inline frames
*
* @access private
* @package dompdf
*/
class Inline_Renderer extends Abstract_Renderer {
//........................................................................
function render(Frame $frame) {
$style = $frame->get_style();
if ( !$frame->get_first_child() )
return; // No children, no service
// Draw the left border if applicable
$bp = $style->get_border_properties();
$widths = array($style->length_in_pt($bp["top"]["width"]),
$style->length_in_pt($bp["right"]["width"]),
$style->length_in_pt($bp["bottom"]["width"]),
$style->length_in_pt($bp["left"]["width"]));
// Draw the background & border behind each child. To do this we need
// to figure out just how much space each child takes:
list($x, $y) = $frame->get_first_child()->get_position();
$w = null;
$h = 0;
// $x += $widths[3];
// $y += $widths[0];
$this->_set_opacity( $frame->get_opacity( $style->opacity ) );
$first_row = true;
foreach ($frame->get_children() as $child) {
list($child_x, $child_y, $child_w, $child_h) = $child->get_padding_box();
if ( !is_null($w) && $child_x < $x + $w ) {
//This branch seems to be supposed to being called on the first part
//of an inline html element, and the part after the if clause for the
//parts after a line break.
//But because $w initially mostly is 0, and gets updated only on the next
//round, this seem to be never executed and the common close always.
// The next child is on another line. Draw the background &
// borders on this line.
// Background:
if ( ($bg = $style->background_color) !== "transparent" )
$this->_canvas->filled_rectangle( $x, $y, $w, $h, $bg);
if ( ($url = $style->background_image) && $url !== "none" ) {
$this->_background_image($url, $x, $y, $w, $h, $style);
}
// If this is the first row, draw the left border
if ( $first_row ) {
if ( $bp["left"]["style"] !== "none" && $bp["left"]["color"] !== "transparent" && $bp["left"]["width"] > 0 ) {
$method = "_border_" . $bp["left"]["style"];
$this->$method($x, $y, $h + $widths[0] + $widths[2], $bp["left"]["color"], $widths, "left");
}
$first_row = false;
}
// Draw the top & bottom borders
if ( $bp["top"]["style"] !== "none" && $bp["top"]["color"] !== "transparent" && $bp["top"]["width"] > 0 ) {
$method = "_border_" . $bp["top"]["style"];
$this->$method($x, $y, $w + $widths[1] + $widths[3], $bp["top"]["color"], $widths, "top");
}
if ( $bp["bottom"]["style"] !== "none" && $bp["bottom"]["color"] !== "transparent" && $bp["bottom"]["width"] > 0 ) {
$method = "_border_" . $bp["bottom"]["style"];
$this->$method($x, $y + $h + $widths[0] + $widths[2], $w + $widths[1] + $widths[3], $bp["bottom"]["color"], $widths, "bottom");
}
// Handle anchors & links
if ( $frame->get_node()->nodeName === "a" ) {
if ( $href = $frame->get_node()->getAttribute("href") )
$this->_canvas->add_link($href, $x, $y, $w, $h);
}
$x = $child_x;
$y = $child_y;
$w = $child_w;
$h = $child_h;
continue;
}
if ( is_null($w) )
$w = $child_w;
else
$w += $child_w;
$h = max($h, $child_h);
}
// Handle the last child
if ( ($bg = $style->background_color) !== "transparent" )
$this->_canvas->filled_rectangle( $x + $widths[3], $y + $widths[0], $w, $h, $bg);
//On continuation lines (after line break) of inline elements, the style got copied.
//But a non repeatable background image should not be repeated on the next line.
//But removing the background image above has never an effect, and removing it below
//removes it always, even on the initial line.
//Need to handle it elsewhere, e.g. on certain ...clone()... usages.
// Repeat not given: default is Style::__construct
// ... && (!($repeat = $style->background_repeat) || $repeat === "repeat" ...
//different position? $this->_background_image($url, $x, $y, $w, $h, $style);
if ( ($url = $style->background_image) && $url !== "none" )
$this->_background_image($url, $x + $widths[3], $y + $widths[0], $w, $h, $style);
// Add the border widths
$w += $widths[1] + $widths[3];
$h += $widths[0] + $widths[2];
// make sure the border and background start inside the left margin
$left_margin = $style->length_in_pt($style->margin_left);
$x += $left_margin;
// If this is the first row, draw the left border too
if ( $first_row && $bp["left"]["style"] !== "none" && $bp["left"]["color"] !== "transparent" && $widths[3] > 0 ) {
$method = "_border_" . $bp["left"]["style"];
$this->$method($x, $y, $h, $bp["left"]["color"], $widths, "left");
}
// Draw the top & bottom borders
if ( $bp["top"]["style"] !== "none" && $bp["top"]["color"] !== "transparent" && $widths[0] > 0 ) {
$method = "_border_" . $bp["top"]["style"];
$this->$method($x, $y, $w, $bp["top"]["color"], $widths, "top");
}
if ( $bp["bottom"]["style"] !== "none" && $bp["bottom"]["color"] !== "transparent" && $widths[2] > 0 ) {
$method = "_border_" . $bp["bottom"]["style"];
$this->$method($x, $y + $h, $w, $bp["bottom"]["color"], $widths, "bottom");
}
// pre_var_dump(get_class($frame->get_next_sibling()));
// $last_row = get_class($frame->get_next_sibling()) !== 'Inline_Frame_Decorator';
// Draw the right border if this is the last row
if ( $bp["right"]["style"] !== "none" && $bp["right"]["color"] !== "transparent" && $widths[1] > 0 ) {
$method = "_border_" . $bp["right"]["style"];
$this->$method($x + $w, $y, $h, $bp["right"]["color"], $widths, "right");
}
$node = $frame->get_node();
// Handle anchors & links
if ( $node->nodeName === "a" ) {
if ( $name = $node->getAttribute("name") )
$this->_canvas->add_named_dest($name);
if ( $href = $node->getAttribute("href") )
$this->_canvas->add_link($href, $x, $y, $w, $h);
}
if (DEBUG_LAYOUT && DEBUG_LAYOUT_INLINE) {
$this->_debug_layout($child->get_border_box(), "blue");
if (DEBUG_LAYOUT_PADDINGBOX) {
$this->_debug_layout($child->get_padding_box(), "blue", array(0.5, 0.5));
}
}
}
}

View File

@@ -0,0 +1,68 @@
<?php
/**
* DOMPDF - PHP5 HTML to PDF renderer
*
* File: $RCSfile: php_evaluator.cls.php,v $
* Created on: 2004-07-12
*
* Copyright (c) 2004 - Benj Carson <benjcarson@digitaljunkies.ca>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library in the file LICENSE.LGPL; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*
* Alternatively, you may distribute this software under the terms of the
* PHP License, version 3.0 or later. A copy of this license should have
* been distributed with this file in the file LICENSE.PHP . If this is not
* the case, you can obtain a copy at http://www.php.net/license/3_0.txt.
*
* The latest version of DOMPDF might be available at:
* http://www.dompdf.com/
*
* @link http://www.dompdf.com/
* @copyright 2004 Benj Carson
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @package dompdf
*/
/* $Id: javascript_embedder.cls.php 291 2010-08-02 20:55:23Z fabien.menager $ */
/**
* Embeds Javascript into the PDF document
*
* @access private
* @package dompdf
*/
class Javascript_Embedder {
/**
* @var DOMPDF
*/
protected $_dompdf;
function __construct(DOMPDF $dompdf) {
$this->_dompdf = $dompdf;
}
function insert($code) {
$this->_dompdf->get_canvas()->javascript($code);
}
function render($frame) {
if ( !DOMPDF_ENABLE_JAVASCRIPT )
return;
$this->insert($frame->get_node()->nodeValue);
}
}

View File

@@ -0,0 +1,96 @@
<?php
/**
* DOMPDF - PHP5 HTML to PDF renderer
*
* File: $RCSfile: list_bullet_frame_decorator.cls.php,v $
* Created on: 2004-06-23
*
* Copyright (c) 2004 - Benj Carson <benjcarson@digitaljunkies.ca>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library in the file LICENSE.LGPL; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*
* Alternatively, you may distribute this software under the terms of the
* PHP License, version 3.0 or later. A copy of this license should have
* been distributed with this file in the file LICENSE.PHP . If this is not
* the case, you can obtain a copy at http://www.php.net/license/3_0.txt.
*
* The latest version of DOMPDF might be available at:
* http://www.dompdf.com/
*
* @link http://www.dompdf.com/
* @copyright 2004 Benj Carson
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @contributor Helmut Tischer <htischer@weihenstephan.org>
* @package dompdf
*
* Changes
* @contributor Helmut Tischer <htischer@weihenstephan.org>
* @version 20090622
* - bullet size proportional to font size, center position
*/
/* $Id: list_bullet_frame_decorator.cls.php 325 2010-11-07 18:05:59Z fabien.menager $ */
/**
* Decorates frames for list bullet rendering
*
* @access private
* @package dompdf
*/
class List_Bullet_Frame_Decorator extends Frame_Decorator {
const BULLET_PADDING = 1; // Distance from bullet to text in pt
// As fraction of font size (including descent). See also DECO_THICKNESS.
const BULLET_THICKNESS = 0.04; // Thickness of bullet outline. Screen: 0.08, print: better less, e.g. 0.04
const BULLET_DESCENT = 0.3; //descent of font below baseline. Todo: Guessed for now.
const BULLET_SIZE = 0.35; // bullet diameter. For now 0.5 of font_size without descent.
static $BULLET_TYPES = array("disc", "circle", "square");
//........................................................................
function __construct(Frame $frame, DOMPDF $dompdf) {
parent::__construct($frame, $dompdf);
}
function get_margin_width() {
$style = $this->_frame->get_style();
// Small hack to prevent extra indenting of list text on list_style_position === "inside"
// and on suppressed bullet
if ( $style->list_style_position === "outside" ||
$style->list_style_type === "none" )
return 0;
return $style->get_font_size()*self::BULLET_SIZE + 2 * self::BULLET_PADDING;
}
//hits only on "inset" lists items, to increase height of box
function get_margin_height() {
$style = $this->_frame->get_style();
if ( $style->list_style_type === 'none' ) return 0;
return $style->get_font_size() * self::BULLET_SIZE + 2 * self::BULLET_PADDING;
}
function get_width() {
return $this->get_margin_height();
}
function get_height() {
return $this->get_margin_height();
}
//........................................................................
}

View File

@@ -0,0 +1,66 @@
<?php
/**
* DOMPDF - PHP5 HTML to PDF renderer
*
* File: $RCSfile: list_bullet_frame_reflower.cls.php,v $
* Created on: 2004-06-23
*
* Copyright (c) 2004 - Benj Carson <benjcarson@digitaljunkies.ca>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library in the file LICENSE.LGPL; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*
* Alternatively, you may distribute this software under the terms of the
* PHP License, version 3.0 or later. A copy of this license should have
* been distributed with this file in the file LICENSE.PHP . If this is not
* the case, you can obtain a copy at http://www.php.net/license/3_0.txt.
*
* The latest version of DOMPDF might be available at:
* http://www.dompdf.com/
*
* @link http://www.dompdf.com/
* @copyright 2004 Benj Carson
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @package dompdf
*/
/* $Id: list_bullet_frame_reflower.cls.php 357 2011-01-30 20:56:46Z fabien.menager $ */
/**
* Reflows list bullets
*
* @access private
* @package dompdf
*/
class List_Bullet_Frame_Reflower extends Frame_Reflower {
function __construct(Frame_Decorator $frame) { parent::__construct($frame); }
//........................................................................
function reflow(Frame_Decorator $block = null) {
$style = $this->_frame->get_style();
$style->width = $this->_frame->get_width();
$this->_frame->position();
if ( $style->list_style_position === "inside" ) {
$p = $this->_frame->find_block_parent();
$p->add_frame_to_line($this->_frame);
}
}
}

View File

@@ -0,0 +1,188 @@
<?php
/**
* DOMPDF - PHP5 HTML to PDF renderer
*
* File: $RCSfile: list_bullet_image_frame_decorator.cls.php,v $
* Created on: 2005-06-28
*
* Copyright (c) 2004 - Benj Carson <benjcarson@digitaljunkies.ca>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library in the file LICENSE.LGPL; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*
* Alternatively, you may distribute this software under the terms of the
* PHP License, version 3.0 or later. A copy of this license should have
* been distributed with this file in the file LICENSE.PHP . If this is not
* the case, you can obtain a copy at http://www.php.net/license/3_0.txt.
*
* The latest version of DOMPDF might be available at:
* http://www.dompdf.com/
*
* @link http://www.dompdf.com/
* @copyright 2004 Benj Carson
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @contributor Helmut Tischer <htischer@weihenstephan.org>
* @package dompdf
*
* Changes
* @contributor Helmut Tischer <htischer@weihenstephan.org>
* @version 0.5.1.htischer.20090507
* - added comment
*/
/* $Id: list_bullet_image_frame_decorator.cls.php 354 2011-01-24 21:59:54Z fabien.menager $ */
/**
* Decorates frames for list bullets with custom images
*
* @access private
* @package dompdf
*/
class List_Bullet_Image_Frame_Decorator extends Frame_Decorator {
/**
* The underlying image frame
*
* @var Image_Frame_Decorator
*/
protected $_img;
/**
* The image's width in pixels
*
* @var int
*/
protected $_width;
/**
* The image's height in pixels
*
* @var int
*/
protected $_height;
/**
* Class constructor
*
* @param Frame $frame the bullet frame to decorate
* @param DOMPDF $dompdf the document's dompdf object
*/
function __construct(Frame $frame, DOMPDF $dompdf) {
$style = $frame->get_style();
$url = $style->list_style_image;
$frame->get_node()->setAttribute("src", $url);
$this->_img = new Image_Frame_Decorator($frame, $dompdf);
parent::__construct($this->_img, $dompdf);
list($width, $height) = dompdf_getimagesize($this->_img->get_image_url());
// Resample the bullet image to be consistent with 'auto' sized images
// See also Image_Frame_Reflower::get_min_max_width
// Tested php ver: value measured in px, suffix "px" not in value: rtrim unnecessary.
$this->_width = (((float)rtrim($width, "px")) * 72) / DOMPDF_DPI;
$this->_height = (((float)rtrim($height, "px")) * 72) / DOMPDF_DPI;
//If an image is taller as the containing block/box, the box should be extended.
//Neighbour elements are overwriting the overlapping image areas.
//Todo: Where can the box size be extended?
//Code below has no effect.
//See block_frame_reflower _calculate_restricted_height
//See generated_frame_reflower, Dompdf:render() "list-item", "-dompdf-list-bullet"S.
//Leave for now
//if ($style->min_height < $this->_height ) {
// $style->min_height = $this->_height;
//}
//$style->height = "auto";
}
/**
* Return the bullet's width
*
* @return int
*/
function get_width() {
//ignore image width, use same width as on predefined bullet List_Bullet_Frame_Decorator
//for proper alignment of bullet image and text. Allow image to not fitting on left border.
//This controls the distance between bullet image and text
//return $this->_width;
return $this->_frame->get_style()->get_font_size()*List_Bullet_Frame_Decorator::BULLET_SIZE +
2 * List_Bullet_Frame_Decorator::BULLET_PADDING;
}
/**
* Return the bullet's height
*
* @return int
*/
function get_height() {
//based on image height
return $this->_height;
}
/**
* Override get_margin_width
*
* @return int
*/
function get_margin_width() {
//ignore image width, use same width as on predefined bullet List_Bullet_Frame_Decorator
//for proper alignment of bullet image and text. Allow image to not fitting on left border.
//This controls the extra indentation of text to make room for the bullet image.
//Here use actual image size, not predefined bullet size
//return $this->_frame->get_style()->get_font_size()*List_Bullet_Frame_Decorator::BULLET_SIZE +
// 2 * List_Bullet_Frame_Decorator::BULLET_PADDING;
// Small hack to prevent indenting of list text
// Image Might not exist, then position like on list_bullet_frame_decorator fallback to none.
if ( $this->_frame->get_style()->list_style_position === "outside" ||
$this->_width == 0)
return 0;
//This aligns the "inside" image position with the text.
//The text starts to the right of the image.
//Between the image and the text there is an added margin of image width.
//Where this comes from is unknown.
//The corresponding List_Bullet_Frame_Decorator sets a smaller margin. bullet size?
return $this->_width + 2 * List_Bullet_Frame_Decorator::BULLET_PADDING;
}
/**
* Override get_margin_height()
*
* @return int
*/
function get_margin_height() {
//Hits only on "inset" lists items, to increase height of box
//based on image height
return $this->_height + 2 * List_Bullet_Frame_Decorator::BULLET_PADDING;
}
/**
* Return image url
*
* @return string
*/
function get_image_url() {
return $this->_img->get_image_url();
}
/**
* Return the image extension
*
* @return string
*/
function get_image_ext() {
return $this->_img->get_image_ext();
}
}

View File

@@ -0,0 +1,111 @@
<?php
/**
* DOMPDF - PHP5 HTML to PDF renderer
*
* File: $RCSfile: list_bullet_positioner.cls.php,v $
* Created on: 2004-06-23
*
* Copyright (c) 2004 - Benj Carson <benjcarson@digitaljunkies.ca>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library in the file LICENSE.LGPL; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*
* Alternatively, you may distribute this software under the terms of the
* PHP License, version 3.0 or later. A copy of this license should have
* been distributed with this file in the file LICENSE.PHP . If this is not
* the case, you can obtain a copy at http://www.php.net/license/3_0.txt.
*
* The latest version of DOMPDF might be available at:
* http://www.dompdf.com/
*
* @link http://www.dompdf.com/
* @copyright 2004 Benj Carson
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @contributor Helmut Tischer <htischer@weihenstephan.org>
* @package dompdf
*
* Changes
* @contributor Helmut Tischer <htischer@weihenstephan.org>
* @version 20090622
* - try to adjust top position of bullet to top corner of subsequent font
*/
/* $Id: list_bullet_positioner.cls.php 216 2010-03-11 22:49:18Z ryan.masten $ */
/**
* Positions list bullets
*
* @access private
* @package dompdf
*/
class List_Bullet_Positioner extends Positioner {
function __construct(Frame_Decorator $frame) { parent::__construct($frame); }
//........................................................................
function position() {
// Bullets & friends are positioned an absolute distance to the left of
// the content edge of their parent element
$cb = $this->_frame->get_containing_block();
$style = $this->_frame->get_style();
// Note: this differs from most frames in that we must position
// ourselves after determining our width
$x = $cb["x"] - $this->_frame->get_width();
$p = $this->_frame->find_block_parent();
$y = $p->get_current_line("y");
// This is a bit of a hack...
$n = $this->_frame->get_next_sibling();
if ( $n ) {
$style = $n->get_style();
$y += $style->length_in_pt( array($style->margin_top, $style->padding_top),
$n->get_containing_block("w") );
}
// Now the position is the left top of the block which should be marked with the bullet.
// We tried to find out the y of the start of the first text character within the block.
// But the top margin/padding does not fit, neither from this nor from the next sibling
// The "bit of a hack" above does not work also.
// Instead let's position the bullet vertically centered to the block which should be marked.
// But for get_next_sibling() the get_containing_block is all zero, and for find_block_parent()
// the get_containing_block is paper width and the entire list as height.
// if ($p) {
// //$cb = $n->get_containing_block();
// $cb = $p->get_containing_block();
// $y += $cb["h"]/2;
// print 'cb:'.$cb["x"].':'.$cb["y"].':'.$cb["w"].':'.$cb["h"].':';
// }
// Todo:
// For now give up on the above. Use Guesswork with font y-pos in the middle of the line spacing
$style = $p->get_style();
$font_size = $style->get_font_size();
$line_height = $style->length_in_pt($style->line_height, $font_size);
$y += ($line_height - $font_size) / 2;
//Position is x-end y-top of character position of the bullet.
$this->_frame->set_position($x, $y);
}
}

View File

@@ -0,0 +1,200 @@
<?php
/**
* DOMPDF - PHP5 HTML to PDF renderer
*
* File: $RCSfile: list_bullet_renderer.cls.php,v $
* Created on: 2004-06-23
*
* Copyright (c) 2004 - Benj Carson <benjcarson@digitaljunkies.ca>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library in the file LICENSE.LGPL; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*
* Alternatively, you may distribute this software under the terms of the
* PHP License, version 3.0 or later. A copy of this license should have
* been distributed with this file in the file LICENSE.PHP . If this is not
* the case, you can obtain a copy at http://www.php.net/license/3_0.txt.
*
* The latest version of DOMPDF might be available at:
* http://www.dompdf.com/
*
* @link http://www.dompdf.com/
* @copyright 2004 Benj Carson
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @contributor Helmut Tischer <htischer@weihenstephan.org>
* @package dompdf
*
* Changes
* @contributor Helmut Tischer <htischer@weihenstephan.org>
* @version 20090622
* - bullet size proportional to font size, center position
*/
/* $Id: list_bullet_renderer.cls.php 354 2011-01-24 21:59:54Z fabien.menager $ */
/**
* Renders list bullets
*
* @access private
* @package dompdf
*/
class List_Bullet_Renderer extends Abstract_Renderer {
//........................................................................
private function make_counter($n, $type, $pad = null){
$n = intval($n);
$text = "";
$uppercase = false;
switch ($type) {
case "decimal-leading-zero":
case "decimal":
case "1":
if ($pad)
$text = str_pad($n, $pad, "0", STR_PAD_LEFT);
else
$text = $n;
break;
case "upper-alpha":
case "upper-latin":
case "A":
$uppercase = true;
case "lower-alpha":
case "lower-latin":
case "a":
$text = chr( ($n % 26) + ord('a') - 1);
break;
case "upper-roman":
case "I":
$uppercase = true;
case "lower-roman":
case "i":
$text = dec2roman($n);
break;
case "lower-greek":
$text = unichr($n + 944);
break;
}
if ($uppercase)
$text = strtoupper($text);
return $text.".";
}
function render(Frame $frame) {
$style = $frame->get_style();
$font_size = $style->get_font_size();
$line_height = $style->length_in_pt($style->line_height, $frame->get_containing_block("w"));
$this->_set_opacity( $frame->get_opacity( $style->opacity ) );
// Handle list-style-image
// If list style image is requested but missing, fall back to predefined types
if ( $style->list_style_image !== "none" &&
strcmp($img = $frame->get_image_url(), DOMPDF_LIB_DIR . "/res/broken_image.png") != 0) {
list($x,$y) = $frame->get_position();
//For expected size and aspect, instead of box size, use image natural size scaled to DPI.
// Resample the bullet image to be consistent with 'auto' sized images
// See also Image_Frame_Reflower::get_min_max_width
// Tested php ver: value measured in px, suffix "px" not in value: rtrim unnecessary.
//$w = $frame->get_width();
//$h = $frame->get_height();
list($width, $height) = dompdf_getimagesize($img);
$w = (((float)rtrim($width, "px")) * 72) / DOMPDF_DPI;
$h = (((float)rtrim($height, "px")) * 72) / DOMPDF_DPI;
$x -= $w;
$y -= ($line_height - $font_size)/2; //Reverse hinting of list_bullet_positioner
$this->_canvas->image( $img, $frame->get_image_ext(), $x, $y, $w, $h);
} else {
$bullet_style = $style->list_style_type;
$fill = false;
switch ($bullet_style) {
default:
case "disc":
$fill = true;
case "circle":
list($x,$y) = $frame->get_position();
$r = ($font_size*(List_Bullet_Frame_Decorator::BULLET_SIZE /*-List_Bullet_Frame_Decorator::BULLET_THICKNESS*/ ))/2;
$x -= $font_size*(List_Bullet_Frame_Decorator::BULLET_SIZE/2);
$y += ($font_size*(1-List_Bullet_Frame_Decorator::BULLET_DESCENT))/2;
$o = $font_size*List_Bullet_Frame_Decorator::BULLET_THICKNESS;
$this->_canvas->circle($x, $y, $r, $style->color, $o, null, $fill);
break;
case "square":
list($x, $y) = $frame->get_position();
$w = $font_size*List_Bullet_Frame_Decorator::BULLET_SIZE;
$x -= $w;
$y += ($font_size*(1-List_Bullet_Frame_Decorator::BULLET_DESCENT-List_Bullet_Frame_Decorator::BULLET_SIZE))/2;
$this->_canvas->filled_rectangle($x, $y, $w, $w, $style->color);
break;
case "decimal-leading-zero":
case "decimal":
case "lower-alpha":
case "lower-latin":
case "lower-roman":
case "lower-greek":
case "upper-alpha":
case "upper-latin":
case "upper-roman":
case "1": // HTML 4.0 compatibility
case "a":
case "i":
case "A":
case "I":
list($x,$y) = $frame->get_position();
$pad = null;
if ( $bullet_style === "decimal-leading-zero" ) {
$pad = strlen($frame->get_parent()->get_parent()->get_node()->getAttribute("dompdf-children-count"));
}
$index = $frame->get_node()->getAttribute("dompdf-counter");
$text = $this->make_counter($index, $bullet_style, $pad);
$font_family = $style->font_family;
$spacing = 0; //$frame->get_text_spacing() + $style->word_spacing;
if ( trim($text) == "" )
return;
$x -= Font_Metrics::get_text_width($text, $font_family, $font_size, $spacing);
$this->_canvas->text($x, $y, $text,
$font_family, $font_size,
$style->color, $spacing);
case "none":
break;
}
}
}
}

View File

@@ -0,0 +1,59 @@
<?php
/**
* DOMPDF - PHP5 HTML to PDF renderer
*
* File: $RCSfile: null_frame_decorator.cls.php,v $
* Created on: 2004-07-12
*
* Copyright (c) 2004 - Benj Carson <benjcarson@digitaljunkies.ca>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library in the file LICENSE.LGPL; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*
* Alternatively, you may distribute this software under the terms of the
* PHP License, version 3.0 or later. A copy of this license should have
* been distributed with this file in the file LICENSE.PHP . If this is not
* the case, you can obtain a copy at http://www.php.net/license/3_0.txt.
*
* The latest version of DOMPDF might be available at:
* http://www.dompdf.com/
*
* @link http://www.dompdf.com/
* @copyright 2004 Benj Carson
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @package dompdf
*/
/* $Id: null_frame_decorator.cls.php 216 2010-03-11 22:49:18Z ryan.masten $ */
/**
* Dummy decorator
*
* @access private
* @package dompdf
*/
class Null_Frame_Decorator extends Frame_Decorator {
function __construct(Frame $frame, DOMPDF $dompdf) {
parent::__construct($frame, $dompdf);
$style = $this->_frame->get_style();
$style->width = 0;
$style->height = 0;
$style->margin = 0;
$style->padding = 0;
}
}

View File

@@ -0,0 +1,54 @@
<?php
/**
* DOMPDF - PHP5 HTML to PDF renderer
*
* File: $RCSfile: null_frame_reflower.cls.php,v $
* Created on: 2004-07-12
*
* Copyright (c) 2004 - Benj Carson <benjcarson@digitaljunkies.ca>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library in the file LICENSE.LGPL; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*
* Alternatively, you may distribute this software under the terms of the
* PHP License, version 3.0 or later. A copy of this license should have
* been distributed with this file in the file LICENSE.PHP . If this is not
* the case, you can obtain a copy at http://www.php.net/license/3_0.txt.
*
* The latest version of DOMPDF might be available at:
* http://www.dompdf.com/
*
* @link http://www.dompdf.com/
* @copyright 2004 Benj Carson
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @package dompdf
*/
/* $Id: null_frame_reflower.cls.php 357 2011-01-30 20:56:46Z fabien.menager $ */
/**
* Dummy reflower
*
* @access private
* @package dompdf
*/
class Null_Frame_Reflower extends Frame_Reflower {
function __construct(Frame $frame) { parent::__construct($frame); }
function reflow(Frame_Decorator $block = null) { return; }
}

View File

@@ -0,0 +1,56 @@
<?php
/**
* DOMPDF - PHP5 HTML to PDF renderer
*
* File: $RCSfile: null_positioner.cls.php,v $
* Created on: 2004-07-12
*
* Copyright (c) 2004 - Benj Carson <benjcarson@digitaljunkies.ca>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library in the file LICENSE.LGPL; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*
* Alternatively, you may distribute this software under the terms of the
* PHP License, version 3.0 or later. A copy of this license should have
* been distributed with this file in the file LICENSE.PHP . If this is not
* the case, you can obtain a copy at http://www.php.net/license/3_0.txt.
*
* The latest version of DOMPDF might be available at:
* http://www.dompdf.com/
*
* @link http://www.dompdf.com/
* @copyright 2004 Benj Carson
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @package dompdf
*/
/* $Id: null_positioner.cls.php 216 2010-03-11 22:49:18Z ryan.masten $ */
/**
* Dummy positioner
*
* @access private
* @package dompdf
*/
class Null_Positioner extends Positioner {
function __construct(Frame_Decorator $frame) {
parent::__construct($frame);
}
function position() { return; }
}

159
pdf/include/page_cache.cls.php Executable file
View File

@@ -0,0 +1,159 @@
<?php
/**
* DOMPDF - PHP5 HTML to PDF renderer
*
* File: $RCSfile: page_cache.cls.php,v $
* Created on: 2004-07-23
*
* Copyright (c) 2004 - Benj Carson <benjcarson@digitaljunkies.ca>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library in the file LICENSE.LGPL; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*
* Alternatively, you may distribute this software under the terms of the
* PHP License, version 3.0 or later. A copy of this license should have
* been distributed with this file in the file LICENSE.PHP . If this is not
* the case, you can obtain a copy at http://www.php.net/license/3_0.txt.
*
* The latest version of DOMPDF might be available at:
* http://www.dompdf.com/
*
* @link http://www.dompdf.com/
* @copyright 2004 Benj Carson
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @package dompdf
*/
/* $Id: page_cache.cls.php 216 2010-03-11 22:49:18Z ryan.masten $ */
/**
* Caches individual rendered PDF pages
*
* Not totally implmented yet. Use at your own risk ;)
*
* @access private
* @package dompdf
* @static
*/
class Page_Cache {
const DB_USER = "dompdf_page_cache";
const DB_PASS = "some meaningful password";
const DB_NAME = "dompdf_page_cache";
static private $__connection = null;
function init() {
if ( is_null(self::$__connection) ) {
$con_str = "host=" . DB_HOST .
" dbname=" . self::DB_NAME .
" user=" . self::DB_USER .
" password=" . self::DB_PASS;
if ( !self::$__connection = pg_connect($con_str) )
throw new Exception("Database connection failed.");
}
}
function __construct() { throw new Exception("Can not create instance of Page_Class. Class is static."); }
private static function __query($sql) {
if ( !($res = pg_query(self::$__connection, $sql)) )
throw new Exception(pg_last_error(self::$__connection));
return $res;
}
static function store_page($id, $page_num, $data) {
$where = "WHERE id='" . pg_escape_string($id) . "' AND ".
"page_num=". pg_escape_string($page_num);
$res = self::__query("SELECT timestamp FROM page_cache ". $where);
$row = pg_fetch_assoc($res);
if ( $row )
self::__query("UPDATE page_cache SET data='" . pg_escape_string($data) . "' " . $where);
else
self::__query("INSERT INTO page_cache (id, page_num, data) VALUES ('" . pg_escape_string($id) . "', ".
pg_escape_string($page_num) . ", ".
"'". pg_escape_string($data) . "')");
}
static function store_fonts($id, $fonts) {
self::__query("BEGIN");
// Update the font information
self::__query("DELETE FROM page_fonts WHERE id='" . pg_escape_string($id) . "'");
foreach (array_keys($fonts) as $font)
self::__query("INSERT INTO page_fonts (id, font_name) VALUES ('" .
pg_escape_string($id) . "', '" . pg_escape_string($font) . "')");
self::__query("COMMIT");
}
// static function retrieve_page($id, $page_num) {
// $res = self::__query("SELECT data FROM page_cache WHERE id='" . pg_escape_string($id) . "' AND ".
// "page_num=". pg_escape_string($page_num));
// $row = pg_fetch_assoc($res);
// return pg_unescape_bytea($row["data"]);
// }
static function get_page_timestamp($id, $page_num) {
$res = self::__query("SELECT timestamp FROM page_cache WHERE id='" . pg_escape_string($id) . "' AND ".
"page_num=". pg_escape_string($page_num));
$row = pg_fetch_assoc($res);
return $row["timestamp"];
}
// Adds the cached document referenced by $id to the provided pdf
static function insert_cached_document(CPDF_Adapter $pdf, $id, $new_page = true) {
$res = self::__query("SELECT font_name FROM page_fonts WHERE id='" . pg_escape_string($id) . "'");
// Ensure that the fonts needed by the cached document are loaded into
// the pdf
while ($row = pg_fetch_assoc($res))
$pdf->get_cpdf()->selectFont($row["font_name"]);
$res = self::__query("SELECT data FROM page_cache WHERE id='" . pg_escape_string($id) . "'");
if ( $new_page )
$pdf->new_page();
$first = true;
while ($row = pg_fetch_assoc($res)) {
if ( !$first )
$pdf->new_page();
else
$first = false;
$page = $pdf->reopen_serialized_object($row["data"]);
//$pdf->close_object();
$pdf->add_object($page, "add");
}
}
}
Page_Cache::init();

View File

@@ -0,0 +1,583 @@
<?php
/**
* DOMPDF - PHP5 HTML to PDF renderer
*
* File: $RCSfile: page_frame_decorator.cls.php,v $
* Created on: 2004-06-15
*
* Copyright (c) 2004 - Benj Carson <benjcarson@digitaljunkies.ca>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library in the file LICENSE.LGPL; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*
* Alternatively, you may distribute this software under the terms of the
* PHP License, version 3.0 or later. A copy of this license should have
* been distributed with this file in the file LICENSE.PHP . If this is not
* the case, you can obtain a copy at http://www.php.net/license/3_0.txt.
*
* The latest version of DOMPDF might be available at:
* http://www.dompdf.com/
*
* @link http://www.dompdf.com/
* @copyright 2004 Benj Carson
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @package dompdf
*/
/* $Id: page_frame_decorator.cls.php 337 2010-12-01 21:30:27Z fabien.menager $ */
/**
* Decorates frames for page layout
*
* @access private
* @package dompdf
*/
class Page_Frame_Decorator extends Frame_Decorator {
/**
* y value of bottom page margin
*
* @var float
*/
protected $_bottom_page_margin;
/**
* Flag indicating page is full.
*
* @var bool
*/
protected $_page_full;
/**
* Number of tables currently being reflowed
*
* @var int
*/
protected $_in_table;
/**
* The pdf renderer
*
* @var Renderer
*/
protected $_renderer;
//........................................................................
/**
* Class constructor
*
* @param Frame $frame the frame to decorate
*/
function __construct(Frame $frame, DOMPDF $dompdf) {
parent::__construct($frame, $dompdf);
$this->_page_full = false;
$this->_in_table = 0;
$this->_bottom_page_margin = null;
}
/**
* Set the renderer used for this pdf
*
* @param Renderer $renderer the renderer to use
*/
function set_renderer($renderer) {
$this->_renderer = $renderer;
}
/**
* Return the renderer used for this pdf
*
* @return Renderer
*/
function get_renderer() {
return $this->_renderer;
}
/**
* Set the frame's containing block. Overridden to set $this->_bottom_page_margin.
*
* @param float $x
* @param float $y
* @param float $w
* @param float $h
*/
function set_containing_block($x = null, $y = null, $w = null, $h = null) {
parent::set_containing_block($x,$y,$w,$h);
$w = $this->get_containing_block("w");
if ( isset($h) )
$this->_bottom_page_margin = $h; // - $this->_frame->get_style()->length_in_pt($this->_frame->get_style()->margin_bottom, $w);
}
/**
* Returns true if the page is full and is no longer accepting frames.
*
* @return bool
*/
function is_full() {
return $this->_page_full;
}
/**
* Start a new page by resetting the full flag.
*/
function next_page() {
$this->_renderer->new_page();
$this->_page_full = false;
}
/**
* Indicate to the page that a table is currently being reflowed.
*/
function table_reflow_start() {
$this->_in_table++;
}
/**
* Indicate to the page that table reflow is finished.
*/
function table_reflow_end() {
$this->_in_table--;
}
/**
* Return whether we are currently in a nested table or not
*
* @return bool
*/
function in_nested_table() {
return $this->_in_table > 1;
}
/**
* Check if a forced page break is required before $frame. This uses the
* frame's page_break_before property as well as the preceeding frame's
* page_break_after property.
*
* @link http://www.w3.org/TR/CSS21/page.html#forced
*
* @param Frame $frame the frame to check
* @return bool true if a page break occured
*/
function check_forced_page_break(Frame $frame) {
// Skip check if page is already split
if ( $this->_page_full )
return;
$block_types = array("block", "list-item", "table", "inline");
$page_breaks = array("always", "left", "right");
$style = $frame->get_style();
if ( !in_array($style->display, $block_types) )
return false;
// Find the previous block-level sibling
$prev = $frame->get_prev_sibling();
while ( $prev && !in_array($prev->get_style()->display, $block_types) )
$prev = $prev->get_prev_sibling();
if ( in_array($style->page_break_before, $page_breaks) ) {
// Prevent cascading splits
$frame->split(null, true);
// We have to grab the style again here because split() resets
// $frame->style to the frame's orignal style.
$frame->get_style()->page_break_before = "auto";
$this->_page_full = true;
return true;
}
if ( $prev && in_array($prev->get_style()->page_break_after, $page_breaks) ) {
// Prevent cascading splits
$frame->split(null, true);
$prev->get_style()->page_break_after = "auto";
$this->_page_full = true;
return true;
}
if( $prev && $prev->get_last_child() && $frame->get_node()->nodeName != "body" ) {
$prev_last_child = $prev->get_last_child();
if ( in_array($prev_last_child->get_style()->page_break_after, $page_breaks) ) {
$frame->split(null, true);
$prev_last_child->get_style()->page_break_after = "auto";
$this->_page_full = true;
return true;
}
}
return false;
}
/**
* Determine if a page break is allowed before $frame
* http://www.w3.org/TR/CSS21/page.html#allowed-page-breaks
*
* In the normal flow, page breaks can occur at the following places:
*
* 1. In the vertical margin between block boxes. When a page
* break occurs here, the used values of the relevant
* 'margin-top' and 'margin-bottom' properties are set to '0'.
* 2. Between line boxes inside a block box.
*
* These breaks are subject to the following rules:
*
* * Rule A: Breaking at (1) is allowed only if the
* 'page-break-after' and 'page-break-before' properties of
* all the elements generating boxes that meet at this margin
* allow it, which is when at least one of them has the value
* 'always', 'left', or 'right', or when all of them are
* 'auto'.
*
* * Rule B: However, if all of them are 'auto' and the
* nearest common ancestor of all the elements has a
* 'page-break-inside' value of 'avoid', then breaking here is
* not allowed.
*
* * Rule C: Breaking at (2) is allowed only if the number of
* line boxes between the break and the start of the enclosing
* block box is the value of 'orphans' or more, and the number
* of line boxes between the break and the end of the box is
* the value of 'widows' or more.
*
* * Rule D: In addition, breaking at (2) is allowed only if
* the 'page-break-inside' property is 'auto'.
*
* If the above doesn't provide enough break points to keep
* content from overflowing the page boxes, then rules B and D are
* dropped in order to find additional breakpoints.
*
* If that still does not lead to sufficient break points, rules A
* and C are dropped as well, to find still more break points.
*
* We will also allow breaks between table rows. However, when
* splitting a table, the table headers should carry over to the
* next page (but they don't yet).
*
* @param Frame $frame the frame to check
* @return bool true if a break is allowed, false otherwise
*/
protected function _page_break_allowed(Frame $frame) {
$block_types = array("block", "list-item", "table", "-dompdf-image");
dompdf_debug("page-break", "_page_break_allowed(" . $frame->get_node()->nodeName. ")");
$display = $frame->get_style()->display;
// Block Frames (1):
if ( in_array($display, $block_types) ) {
// Avoid breaks within table-cells
if ( $this->_in_table ) {
dompdf_debug("page-break", "In table: " . $this->_in_table);
return false;
}
// Rules A & B
if ( $frame->get_style()->page_break_before === "avoid" ) {
dompdf_debug("page-break", "before: avoid");
return false;
}
// Find the preceeding block-level sibling
$prev = $frame->get_prev_sibling();
while ( $prev && !in_array($prev->get_style()->display, $block_types) )
$prev = $prev->get_prev_sibling();
// Does the previous element allow a page break after?
if ( $prev && $prev->get_style()->page_break_after === "avoid" ) {
dompdf_debug("page-break", "after: avoid");
return false;
}
// If both $prev & $frame have the same parent, check the parent's
// page_break_inside property.
$parent = $frame->get_parent();
if ( $prev && $parent && $parent->get_style()->page_break_inside === "avoid" ) {
dompdf_debug("page-break", "parent inside: avoid");
return false;
}
// To prevent cascading page breaks when a top-level element has
// page-break-inside: avoid, ensure that at least one frame is
// on the page before splitting.
if ( $parent->get_node()->nodeName === "body" && !$prev ) {
// We are the body's first child
dompdf_debug("page-break", "Body's first child.");
return false;
}
// If the frame is the first block-level frame, use the value from
// $frame's parent instead.
if ( !$prev && $parent )
return $this->_page_break_allowed( $parent );
dompdf_debug("page-break", "block: break allowed");
return true;
}
// Inline frames (2):
else if ( in_array($display, Style::$INLINE_TYPES) ) {
// Avoid breaks within table-cells
if ( $this->_in_table ) {
dompdf_debug("page-break", "In table: " . $this->_in_table);
return false;
}
// Rule C
$block_parent = $frame->find_block_parent();
if ( count($block_parent->get_lines() ) < $frame->get_style()->orphans ) {
dompdf_debug("page-break", "orphans");
return false;
}
// FIXME: Checking widows is tricky without having laid out the
// remaining line boxes. Just ignore it for now...
// Rule D
$p = $block_parent;
while ($p) {
if ( $p->get_style()->page_break_inside === "avoid" ) {
dompdf_debug("page-break", "parent->inside: avoid");
return false;
}
$p = $p->find_block_parent();
}
// To prevent cascading page breaks when a top-level element has
// page-break-inside: avoid, ensure that at least one frame with
// some content is on the page before splitting.
$prev = $frame->get_prev_sibling();
while ( $prev && ($prev->get_node()->nodeName === "#text" && trim($prev->get_node()->nodeValue) == "") )
$prev = $prev->get_prev_sibling();
if ( $block_parent->get_node()->nodeName === "body" && !$prev ) {
// We are the body's first child
dompdf_debug("page-break", "Body's first child.");
return false;
}
// Skip breaks on empty text nodes
if ( $frame->get_node()->nodeName === "#text" &&
$frame->get_node()->nodeValue == "" )
return false;
dompdf_debug("page-break", "inline: break allowed");
return true;
// Table-rows
} else if ( $display === "table-row" ) {
// Simply check if the parent table's page_break_inside property is
// not 'avoid'
$p = Table_Frame_Decorator::find_parent_table($frame);
while ($p) {
if ( $p->get_style()->page_break_inside === "avoid" ) {
dompdf_debug("page-break", "parent->inside: avoid");
return false;
}
$p = $p->find_block_parent();
}
// Avoid breaking after the first row of a table
if ( $p && $p->get_first_child() === $frame) {
dompdf_debug("page-break", "table: first-row");
return false;
}
// If this is a nested table, prevent the page from breaking
if ( $this->_in_table > 1 ) {
dompdf_debug("page-break", "table: nested table");
return false;
}
dompdf_debug("page-break","table-row/row-groups: break allowed");
return true;
} else if ( in_array($display, Table_Frame_Decorator::$ROW_GROUPS) ) {
// Disallow breaks at row-groups: only split at row boundaries
return false;
} else {
dompdf_debug("page-break", "? " . $frame->get_style()->display . "");
return false;
}
}
/**
* Check if $frame will fit on the page. If the frame does not fit,
* the frame tree is modified so that a page break occurs in the
* correct location.
*
* @param Frame $frame the frame to check
* @return Frame the frame following the page break
*/
function check_page_break(Frame $frame) {
// Do not split if we have already or if the frame was already
// pushed to the next page (prevents infinite loops)
if ( $this->_page_full || $frame->_already_pushed ) {
return false;
}
// If the frame is absolute of fixed it shouldn't break
$p = $frame;
do {
if ( in_array($p->get_style()->position, array("fixed", "absolute")) )
return false;
} while ( $p = $p->get_parent() );
$margin_height = $frame->get_margin_height();
// FIXME If the row is taller than the page and
// if it the first of the page, we don't break
if ( $frame->get_style()->display === "table-row" &&
!$frame->get_prev_sibling() &&
$margin_height > $this->get_margin_height() )
return false;
// Determine the frame's maximum y value
$max_y = $frame->get_position("y") + $margin_height;
// If a split is to occur here, then the bottom margins & paddings of all
// parents of $frame must fit on the page as well:
$p = $frame->get_parent();
while ( $p ) {
$style = $p->get_style();
$max_y += $style->length_in_pt(array($style->margin_bottom,
$style->padding_bottom,
$style->border_bottom_width));
$p = $p->get_parent();
}
// Check if $frame flows off the page
if ( $max_y <= $this->_bottom_page_margin )
// no: do nothing
return false;
dompdf_debug("page-break", "check_page_break");
dompdf_debug("page-break", "in_table: " . $this->_in_table);
// yes: determine page break location
$iter = $frame;
$flg = false;
$in_table = $this->_in_table;
dompdf_debug("page-break","Starting search");
while ( $iter ) {
// echo "\nbacktrack: " .$iter->get_node()->nodeName ." ".spl_object_hash($iter->get_node()). "";
if ( $iter === $this ) {
dompdf_debug("page-break", "reached root.");
// We've reached the root in our search. Just split at $frame.
break;
}
if ( $this->_page_break_allowed($iter) ) {
dompdf_debug("page-break","break allowed, splitting.");
$iter->split(null, true);
$this->_page_full = true;
$this->_in_table = $in_table;
$frame->_already_pushed = true;
return true;
}
if ( !$flg && $next = $iter->get_last_child() ) {
dompdf_debug("page-break", "following last child.");
if ( in_array($next->get_style()->display, Style::$TABLE_TYPES) )
$this->_in_table++;
$iter = $next;
continue;
}
if ( $next = $iter->get_prev_sibling() ) {
dompdf_debug("page-break", "following prev sibling.");
if ( in_array($next->get_style()->display, Style::$TABLE_TYPES) &&
!in_array($iter->get_style()->display, Style::$TABLE_TYPES) )
$this->_in_table++;
else if ( !in_array($next->get_style()->display, Style::$TABLE_TYPES) &&
in_array($iter->get_style()->display, Style::$TABLE_TYPES) )
$this->_in_table--;
$iter = $next;
$flg = false;
continue;
}
if ( $next = $iter->get_parent() ) {
dompdf_debug("page-break", "following parent.");
if ( in_array($iter->get_style()->display, Style::$TABLE_TYPES) )
$this->_in_table--;
$iter = $next;
$flg = true;
continue;
}
break;
}
$this->_in_table = $in_table;
// No valid page break found. Just break at $frame.
dompdf_debug("page-break", "no valid break found, just splitting.");
// If we are in a table, backtrack to the nearest top-level table row
if ( $this->_in_table ) {
$num_tables = $this->_in_table - 1;
$iter = $frame;
while ( $iter && $num_tables && $iter->get_style->display !== "table" ) {
$iter = $iter->get_parent();
$num_tables--;
}
$iter = $frame;
while ($iter && $iter->get_style()->display !== "table-row" )
$iter = $iter->get_parent();
}
$frame->split(null, true);
$this->_page_full = true;
$frame->_already_pushed = true;
return true;
}
//........................................................................
function split($frame = null, $force_pagebreak = false) {
// Do nothing
}
}

View File

@@ -0,0 +1,245 @@
<?php
/**
* DOMPDF - PHP5 HTML to PDF renderer
*
* File: $RCSfile: page_frame_reflower.cls.php,v $
* Created on: 2004-06-16
*
* Copyright (c) 2004 - Benj Carson <benjcarson@digitaljunkies.ca>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library in the file LICENSE.LGPL; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*
* Alternatively, you may distribute this software under the terms of the
* PHP License, version 3.0 or later. A copy of this license should have
* been distributed with this file in the file LICENSE.PHP . If this is not
* the case, you can obtain a copy at http://www.php.net/license/3_0.txt.
*
* The latest version of DOMPDF might be available at:
* http://www.dompdf.com/
*
* @link http://www.dompdf.com/
* @copyright 2004 Benj Carson
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @package dompdf
*/
/* $Id: page_frame_reflower.cls.php 357 2011-01-30 20:56:46Z fabien.menager $ */
/**
* Reflows pages
*
* @access private
* @package dompdf
*/
class Page_Frame_Reflower extends Frame_Reflower {
/**
* Cache of the callbacks array
*
* @var array
*/
private $_callbacks;
/**
* Cache of the canvas
*
* @var Canvas
*/
private $_canvas;
/**
* This page's floating frames
*
* @var array
*/
private $_floating_frames;
/**
* true if the page has floating frales
* Used to improve performances as it will
* be accesses everytime
* @var bool
*/
private $_has_floating_frames;
/**
* The stacking context, containing all z-indexed frames
* @var array
*/
private $_stacking_context = array();
function __construct(Page_Frame_Decorator $frame) { parent::__construct($frame); }
/**
* @return array
*/
function get_floating_frames() { return $this->_floating_frames; }
/**
* @param $frame Frame
* @return void
*/
function add_frame_to_stacking_context(Frame $frame, $z_index) {
$this->_stacking_context[$z_index][] = $frame;
}
/**
* Add a floating frame
*
* @param $child Frame
*/
function add_floating_frame(Frame $frame) {
$this->_floating_frames[] = $frame;
$this->_has_floating_frames = true;
}
/**
* Tells if the page has floating frames
*
* @return bool
*/
function has_floating_frames() {
return $this->_has_floating_frames;
}
//........................................................................
function reflow(Frame_Decorator $block = null) {
$style = $this->_frame->get_style();
// Paged layout:
// http://www.w3.org/TR/CSS21/page.html
// Pages are only concerned with margins
$cb = $this->_frame->get_containing_block();
$left = $style->length_in_pt($style->margin_left, $cb["w"]);
$right = $style->length_in_pt($style->margin_right, $cb["w"]);
$top = $style->length_in_pt($style->margin_top, $cb["h"]);
$bottom = $style->length_in_pt($style->margin_bottom, $cb["h"]);
$content_x = $cb["x"] + $left;
$content_y = $cb["y"] + $top;
$content_width = $cb["w"] - $left - $right;
$content_height = $cb["h"] - $top - $bottom;
$fixed_children = array();
$prev_child = null;
$child = $this->_frame->get_first_child();
$current_page = 0;
while ($child) {
// Only if it's the first page, we save the nodes with a fixed position
if ($current_page == 0) {
$children = $child->get_children();
foreach ($children as $onechild) {
if ($onechild->get_style()->position === "fixed") {
$fixed_children[] = $onechild->deep_copy();
}
}
$fixed_children = array_reverse($fixed_children);
}
$child->set_containing_block($content_x, $content_y, $content_width, $content_height);
// Check for begin reflow callback
$this->_check_callbacks("begin_page_reflow", $child);
//Insert a copy of each node which have a fixed position
if ($current_page >= 1) {
foreach ($fixed_children as $fixed_child) {
$child->insert_child_before($fixed_child->deep_copy(), $child->get_first_child());
}
}
$child->reflow();
$next_child = $child->get_next_sibling();
// Check for begin render callback
$this->_check_callbacks("begin_page_render", $child);
$renderer = $this->_frame->get_renderer();
// Render the page
$renderer->render($child);
// http://www.w3.org/TR/CSS21/visuren.html#z-index
ksort($this->_stacking_context);
foreach( $this->_stacking_context as $_z_index => $_frames ) {
foreach ( $_frames as $_frame ) {
$renderer->render($_frame);
}
}
$this->_stacking_context = array();
// Check for end render callback
$this->_check_callbacks("end_page_render", $child);
if ( $next_child ) {
$this->_frame->next_page();
}
// Wait to dispose of all frames on the previous page
// so callback will have access to them
if ( $prev_child ) {
$prev_child->dispose(true);
}
$prev_child = $child;
$child = $next_child;
$current_page++;
}
// Dispose of previous page if it still exists
if ( $prev_child ) {
$prev_child->dispose(true);
}
}
//........................................................................
/**
* Check for callbacks that need to be performed when a given event
* gets triggered on a page
*
* @param string $event the type of event
* @param Frame $frame the frame that event is triggered on
*/
protected function _check_callbacks($event, $frame) {
if (!isset($this->_callbacks)) {
$dompdf = $this->_frame->get_dompdf();
$this->_callbacks = $dompdf->get_callbacks();
$this->_canvas = $dompdf->get_canvas();
}
if (is_array($this->_callbacks) && isset($this->_callbacks[$event])) {
$info = array(0 => $this->_canvas, "canvas" => $this->_canvas,
1 => $frame, "frame" => $frame);
$fs = $this->_callbacks[$event];
foreach ($fs as $f) {
if (is_callable($f)) {
if (is_array($f)) {
$f[0]->$f[1]($info);
} else {
$f($info);
}
}
}
}
}
}

1053
pdf/include/pdflib_adapter.cls.php Executable file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,76 @@
<?php
/**
* DOMPDF - PHP5 HTML to PDF renderer
*
* File: $RCSfile: php_evaluator.cls.php,v $
* Created on: 2004-07-12
*
* Copyright (c) 2004 - Benj Carson <benjcarson@digitaljunkies.ca>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library in the file LICENSE.LGPL; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*
* Alternatively, you may distribute this software under the terms of the
* PHP License, version 3.0 or later. A copy of this license should have
* been distributed with this file in the file LICENSE.PHP . If this is not
* the case, you can obtain a copy at http://www.php.net/license/3_0.txt.
*
* The latest version of DOMPDF might be available at:
* http://www.dompdf.com/
*
* @link http://www.dompdf.com/
* @copyright 2004 Benj Carson
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @package dompdf
*/
/* $Id: php_evaluator.cls.php 216 2010-03-11 22:49:18Z ryan.masten $ */
/**
* Executes inline PHP code during the rendering process
*
* @access private
* @package dompdf
*/
class PHP_Evaluator {
protected $_canvas;
function __construct(Canvas $canvas) {
$this->_canvas = $canvas;
}
function evaluate($code, $vars = array()) {
if ( !DOMPDF_ENABLE_PHP )
return;
// Set up some variables for the inline code
$pdf = $this->_canvas;
$PAGE_NUM = $pdf->get_page_number();
$PAGE_COUNT = $pdf->get_page_count();
// Override those variables if passed in
foreach ($vars as $k => $v) {
$$k = $v;
}
eval(utf8_decode($code));
}
function render($frame) {
$this->evaluate($frame->get_node()->nodeValue);
}
}

71
pdf/include/positioner.cls.php Executable file
View File

@@ -0,0 +1,71 @@
<?php
/**
* DOMPDF - PHP5 HTML to PDF renderer
*
* File: $RCSfile: positioner.cls.php,v $
* Created on: 2004-06-08
*
* Copyright (c) 2004 - Benj Carson <benjcarson@digitaljunkies.ca>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library in the file LICENSE.LGPL; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*
* Alternatively, you may distribute this software under the terms of the
* PHP License, version 3.0 or later. A copy of this license should have
* been distributed with this file in the file LICENSE.PHP . If this is not
* the case, you can obtain a copy at http://www.php.net/license/3_0.txt.
*
* The latest version of DOMPDF might be available at:
* http://www.dompdf.com/
*
* @link http://www.dompdf.com/
* @copyright 2004 Benj Carson
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @package dompdf
*/
/* $Id: positioner.cls.php 283 2010-07-19 17:57:40Z fabien.menager $ */
/**
* Base Positioner class
*
* Defines postioner interface
*
* @access private
* @package dompdf
*/
abstract class Positioner {
// protected members
protected $_frame;
//........................................................................
function __construct(Frame_Decorator $frame) {
$this->_frame = $frame;
}
/**
* Class destructor
*/
function __destruct() {
clear_object($this);
}
//........................................................................
abstract function position();
}

293
pdf/include/renderer.cls.php Executable file
View File

@@ -0,0 +1,293 @@
<?php
/**
* DOMPDF - PHP5 HTML to PDF renderer
*
* File: $RCSfile: renderer.cls.php,v $
* Created on: 2004-06-03
*
* Copyright (c) 2004 - Benj Carson <benjcarson@digitaljunkies.ca>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library in the file LICENSE.LGPL; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*
* Alternatively, you may distribute this software under the terms of the
* PHP License, version 3.0 or later. A copy of this license should have
* been distributed with this file in the file LICENSE.PHP . If this is not
* the case, you can obtain a copy at http://www.php.net/license/3_0.txt.
*
* The latest version of DOMPDF might be available at:
* http://www.dompdf.com/
*
* @link http://www.dompdf.com/
* @copyright 2004 Benj Carson
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @package dompdf
*/
/* $Id: renderer.cls.php 351 2011-01-19 20:27:02Z fabien.menager $ */
/**
* Concrete renderer
*
* Instantiates several specific renderers in order to render any given
* frame.
*
* @access private
* @package dompdf
*/
class Renderer extends Abstract_Renderer {
/**
* Array of renderers for specific frame types
*
* @var array
*/
protected $_renderers;
/**
* Cache of the callbacks array
*
* @var array
*/
private $_callbacks;
/**
* Class destructor
*/
function __destruct() {
clear_object($this);
}
/**
* Advance the canvas to the next page
*/
function new_page() {
$this->_canvas->new_page();
}
/**
* Render frames recursively
*
* @param Frame $frame the frame to render
*/
function render(Frame $frame) {
global $_dompdf_debug;
if ( $_dompdf_debug ) {
echo $frame;
flush();
}
$style = $frame->get_style();
$display = $style->display;
// Starts the CSS transformation
if ( $style->transform && is_array($style->transform) ) {
$this->_canvas->save();
list($x, $y, $w, $h) = $frame->get_padding_box();
$origin = $style->transform_origin;
foreach($style->transform as $transform) {
list($function, $values) = $transform;
if ( $function === "matrix" ) {
$function = "transform";
}
$values = array_map("floatval", $values);
$values[] = $x + $style->length_in_pt($origin[0], $style->width);
$values[] = $y + $style->length_in_pt($origin[1], $style->height);
call_user_func_array(array($this->_canvas, $function), $values);
}
}
switch ($display) {
case "block":
case "list-item":
case "inline-block":
case "table":
case "table-row-group":
case "table-header-group":
case "table-footer-group":
case "inline-table":
$this->_render_frame("block", $frame);
break;
case "inline":
if ( $frame->get_node()->nodeName === "#text" )
$this->_render_frame("text", $frame);
else
$this->_render_frame("inline", $frame);
break;
case "table-cell":
$this->_render_frame("table-cell", $frame);
break;
case "-dompdf-list-bullet":
$this->_render_frame("list-bullet", $frame);
break;
case "-dompdf-image":
$this->_render_frame("image", $frame);
break;
case "none":
$node = $frame->get_node();
if ( $node->nodeName === "script" ) {
if ( $node->getAttribute("type") === "text/php" ||
$node->getAttribute("language") === "php" ) {
// Evaluate embedded php scripts
$this->_render_frame("php", $frame);
}
elseif ( $node->getAttribute("type") === "text/javascript" ||
$node->getAttribute("language") === "javascript" ) {
// Insert JavaScript
$this->_render_frame("javascript", $frame);
}
}
// Don't render children, so skip to next iter
return;
default:
break;
}
// Check for begin frame callback
$this->_check_callbacks("begin_frame", $frame);
// Starts the overflow: hidden box
if ( $style->overflow === "hidden" ) {
list($x, $y, $w, $h) = $frame->get_padding_box();
$this->_canvas->clipping_rectangle($x, $y, $w, $h);
}
$page = $frame->get_root()->get_reflower();
foreach ($frame->get_children() as $child) {
$child_style = $child->get_style();
// Stacking context
if ( $child_style->z_index !== false && ($child_style->z_index !== "auto" || in_array($child_style->position, Style::$POSITIONNED_TYPES)) ) {
$z_index = ($child_style->z_index === "auto") ? 0 : intval($child_style->z_index);
$page->add_frame_to_stacking_context($child, $z_index);
$child_style->z_index = false;
}
else {
$this->render($child);
}
}
// Ends the overflow: hidden box
if ( $style->overflow === "hidden" ) {
$this->_canvas->clipping_end();
}
if ( $style->transform && is_array($style->transform) ) {
$this->_canvas->restore();
}
// Check for end frame callback
$this->_check_callbacks("end_frame", $frame);
}
/**
* Check for callbacks that need to be performed when a given event
* gets triggered on a frame
*
* @param string $event the type of event
* @param Frame $frame the frame that event is triggered on
*/
protected function _check_callbacks($event, $frame) {
if (!isset($this->_callbacks)) {
$this->_callbacks = $this->_dompdf->get_callbacks();
}
if (is_array($this->_callbacks) && isset($this->_callbacks[$event])) {
$info = array(0 => $this->_canvas, "canvas" => $this->_canvas,
1 => $frame, "frame" => $frame);
$fs = $this->_callbacks[$event];
foreach ($fs as $f) {
if (is_callable($f)) {
if (is_array($f)) {
$f[0]->$f[1]($info);
} else {
$f($info);
}
}
}
}
}
/**
* Render a single frame
*
* Creates Renderer objects on demand
*
* @param string $type type of renderer to use
* @param Frame $frame the frame to render
*/
protected function _render_frame($type, $frame) {
if ( !isset($this->_renderers[$type]) ) {
switch ($type) {
case "block":
$this->_renderers[$type] = new Block_Renderer($this->_dompdf);
break;
case "inline":
$this->_renderers[$type] = new Inline_Renderer($this->_dompdf);
break;
case "text":
$this->_renderers[$type] = new Text_Renderer($this->_dompdf);
break;
case "image":
$this->_renderers[$type] = new Image_Renderer($this->_dompdf);
break;
case "table-cell":
$this->_renderers[$type] = new Table_Cell_Renderer($this->_dompdf);
break;
case "list-bullet":
$this->_renderers[$type] = new List_Bullet_Renderer($this->_dompdf);
break;
case "php":
$this->_renderers[$type] = new PHP_Evaluator($this->_canvas);
break;
case "javascript":
$this->_renderers[$type] = new Javascript_Embedder($this->_dompdf);
break;
}
}
$this->_renderers[$type]->render($frame);
}
}

2149
pdf/include/style.cls.php Executable file

File diff suppressed because it is too large Load Diff

1202
pdf/include/stylesheet.cls.php Executable file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,135 @@
<?php
/**
* DOMPDF - PHP5 HTML to PDF renderer
*
* File: $RCSfile: table_cell_frame_decorator.cls.php,v $
* Created on: 2004-07-29
*
* Copyright (c) 2004 - Benj Carson <benjcarson@digitaljunkies.ca>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library in the file LICENSE.LGPL; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*
* Alternatively, you may distribute this software under the terms of the
* PHP License, version 3.0 or later. A copy of this license should have
* been distributed with this file in the file LICENSE.PHP . If this is not
* the case, you can obtain a copy at http://www.php.net/license/3_0.txt.
*
* The latest version of DOMPDF might be available at:
* http://www.dompdf.com/
*
* @link http://www.dompdf.com/
* @copyright 2004 Benj Carson
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @package dompdf
*/
/* $Id: table_cell_frame_decorator.cls.php 216 2010-03-11 22:49:18Z ryan.masten $ */
/**
* Decorates table cells for layout
*
* @access private
* @package dompdf
*/
class Table_Cell_Frame_Decorator extends Block_Frame_Decorator {
protected $_resolved_borders;
protected $_content_height;
//........................................................................
function __construct(Frame $frame, DOMPDF $dompdf) {
parent::__construct($frame, $dompdf);
$this->_resolved_borders = array();
$this->_content_height = 0;
}
//........................................................................
function reset() {
parent::reset();
$this->_resolved_borders = array();
$this->_content_height = 0;
$this->_frame->reset();
}
function get_content_height() {
return $this->_content_height;
}
function set_content_height($height) {
$this->_content_height = $height;
}
function set_cell_height($height) {
$style = $this->get_style();
$v_space = $style->length_in_pt(array($style->margin_top,
$style->padding_top,
$style->border_top_width,
$style->border_bottom_width,
$style->padding_bottom,
$style->margin_bottom),
$style->width);
$new_height = $height - $v_space;
$style->height = $new_height;
if ( $new_height > $this->_content_height ) {
// Adjust our vertical alignment
$valign = $style->vertical_align;
switch ($valign) {
default:
case "baseline":
// FIXME: this isn't right
case "top":
// Don't need to do anything
return;
case "middle":
$delta = ($new_height - $this->_content_height) / 2;
break;
case "bottom":
$delta = $new_height - $this->_content_height;
break;
}
// Move our children
foreach ( $this->get_lines() as $i => $line ) {
foreach ( $line["frames"] as $frame )
$frame->set_position( null, $frame->get_position("y") + $delta );
}
}
}
function set_resolved_border($side, $border_spec) {
$this->_resolved_borders[$side] = $border_spec;
}
//........................................................................
function get_resolved_border($side) {
return $this->_resolved_borders[$side];
}
function get_resolved_borders() { return $this->_resolved_borders; }
}

View File

@@ -0,0 +1,147 @@
<?php
/**
* DOMPDF - PHP5 HTML to PDF renderer
*
* File: $RCSfile: table_cell_frame_reflower.cls.php,v $
* Created on: 2004-06-07
*
* Copyright (c) 2004 - Benj Carson <benjcarson@digitaljunkies.ca>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library in the file LICENSE.LGPL; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*
* Alternatively, you may distribute this software under the terms of the
* PHP License, version 3.0 or later. A copy of this license should have
* been distributed with this file in the file LICENSE.PHP . If this is not
* the case, you can obtain a copy at http://www.php.net/license/3_0.txt.
*
* The latest version of DOMPDF might be available at:
* http://www.dompdf.com/
*
* @link http://www.dompdf.com/
* @copyright 2004 Benj Carson
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @package dompdf
*/
/* $Id: table_cell_frame_reflower.cls.php 358 2011-01-30 22:22:47Z fabien.menager $ */
/**
* Reflows table cells
*
* @access private
* @package dompdf
*/
class Table_Cell_Frame_Reflower extends Block_Frame_Reflower {
//........................................................................
function __construct(Frame $frame) {
parent::__construct($frame);
}
//........................................................................
function reflow(Frame_Decorator $block = null) {
$style = $this->_frame->get_style();
$table = Table_Frame_Decorator::find_parent_table($this->_frame);
$cellmap = $table->get_cellmap();
list($x, $y) = $cellmap->get_frame_position($this->_frame);
$this->_frame->set_position($x, $y);
$cells = $cellmap->get_spanned_cells($this->_frame);
$w = 0;
foreach ( $cells["columns"] as $i ) {
$col = $cellmap->get_column( $i );
$w += $col["used-width"];
}
//FIXME?
$h = $this->_frame->get_containing_block("h");
$left_space = $style->length_in_pt(array($style->margin_left,
$style->padding_left,
$style->border_left_width),
$w);
$right_space = $style->length_in_pt(array($style->padding_right,
$style->margin_right,
$style->border_right_width),
$w);
$top_space = $style->length_in_pt(array($style->margin_top,
$style->padding_top,
$style->border_top_width),
$h);
$bottom_space = $style->length_in_pt(array($style->margin_bottom,
$style->padding_bottom,
$style->border_bottom_width),
$h);
$style->width = $cb_w = $w - $left_space - $right_space;
$content_x = $x + $left_space;
$content_y = $line_y = $y + $top_space;
// Adjust the first line based on the text-indent property
$indent = $style->length_in_pt($style->text_indent, $w);
$this->_frame->increase_line_width($indent);
// Set the y position of the first line in the cell
$page = $this->_frame->get_root();
$this->_frame->set_current_line($line_y);
// Set the containing blocks and reflow each child
foreach ( $this->_frame->get_children() as $child ) {
if ( $page->is_full() )
break;
$child->set_containing_block($content_x, $content_y, $cb_w, $h);
$child->reflow($this->_frame);
}
// Determine our height
$style_height = $style->length_in_pt($style->height, $h);
$this->_frame->set_content_height($this->_calculate_content_height());
$height = max($style_height, $this->_frame->get_content_height());
// Let the cellmap know our height
$cell_height = $height / count($cells["rows"]);
if ($style_height <= $height)
$cell_height += $top_space + $bottom_space;
foreach ($cells["rows"] as $i)
$cellmap->set_row_height($i, $cell_height);
$style->height = $height;
$this->_text_align();
$this->vertical_align();
}
}

View File

@@ -0,0 +1,61 @@
<?php
/**
* DOMPDF - PHP5 HTML to PDF renderer
*
* File: $RCSfile: table_cell_positioner.cls.php,v $
* Created on: 2004-06-08
*
* Copyright (c) 2004 - Benj Carson <benjcarson@digitaljunkies.ca>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library in the file LICENSE.LGPL; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*
* Alternatively, you may distribute this software under the terms of the
* PHP License, version 3.0 or later. A copy of this license should have
* been distributed with this file in the file LICENSE.PHP . If this is not
* the case, you can obtain a copy at http://www.php.net/license/3_0.txt.
*
* The latest version of DOMPDF might be available at:
* http://www.dompdf.com/
*
* @link http://www.dompdf.com/
* @copyright 2004 Benj Carson
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @package dompdf
*/
/* $Id: table_cell_positioner.cls.php 216 2010-03-11 22:49:18Z ryan.masten $ */
/**
* Positions table cells
*
* @access private
* @package dompdf
*/
class Table_Cell_Positioner extends Positioner {
function __construct(Frame_Decorator $frame) { parent::__construct($frame); }
//........................................................................
function position() {
$table = Table_Frame_Decorator::find_parent_table($this->_frame);
$cellmap = $table->get_cellmap();
$this->_frame->set_position($cellmap->get_frame_position($this->_frame));
}
}

View File

@@ -0,0 +1,185 @@
<?php
/**
* DOMPDF - PHP5 HTML to PDF renderer
*
* File: $RCSfile: table_cell_renderer.cls.php,v $
* Created on: 2004-06-09
*
* Copyright (c) 2004 - Benj Carson <benjcarson@digitaljunkies.ca>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library in the file LICENSE.LGPL; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*
* Alternatively, you may distribute this software under the terms of the
* PHP License, version 3.0 or later. A copy of this license should have
* been distributed with this file in the file LICENSE.PHP . If this is not
* the case, you can obtain a copy at http://www.php.net/license/3_0.txt.
*
* The latest version of DOMPDF might be available at:
* http://www.dompdf.com/
*
* @link http://www.dompdf.com/
* @copyright 2004 Benj Carson
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @package dompdf
*/
/* $Id: table_cell_renderer.cls.php 311 2010-09-05 20:02:01Z fabien.menager $ */
/**
* Renders table cells
*
* @access private
* @package dompdf
*/
class Table_Cell_Renderer extends Block_Renderer {
//........................................................................
function render(Frame $frame) {
$style = $frame->get_style();
$this->_set_opacity( $frame->get_opacity( $style->opacity ) );
// Draw our background, border and content
if ( ($bg = $style->background_color) !== "transparent" ) {
list($x, $y, $w, $h) = $frame->get_padding_box();
$this->_canvas->filled_rectangle( $x, $y, $w, $h, $bg );
}
else {
list($x, $y, $w, $h) = $frame->get_padding_box();
}
if ( ($url = $style->background_image) && $url !== "none" ) {
$this->_background_image($url, $x, $y, $w, $h, $style);
}
if ( $style->border_collapse !== "collapse" ) {
$this->_render_border($frame, "bevel");
$this->_render_outline($frame, "bevel");
return;
}
// The collapsed case is slightly complicated...
// @todo Add support for outlines here
$cellmap = Table_Frame_Decorator::find_parent_table($frame)->get_cellmap();
$cells = $cellmap->get_spanned_cells($frame);
$num_rows = $cellmap->get_num_rows();
$num_cols = $cellmap->get_num_cols();
// Determine the top row spanned by this cell
$i = $cells["rows"][0];
$top_row = $cellmap->get_row($i);
// Determine if this cell borders on the bottom of the table. If so,
// then we draw its bottom border. Otherwise the next row down will
// draw its top border instead.
if (in_array( $num_rows - 1, $cells["rows"])) {
$draw_bottom = true;
$bottom_row = $cellmap->get_row($num_rows - 1);
} else
$draw_bottom = false;
// Draw the horizontal borders
foreach ( $cells["columns"] as $j ) {
$bp = $cellmap->get_border_properties($i, $j);
$y = $top_row["y"] - $bp["top"]["width"] / 2;
$col = $cellmap->get_column($j);
$x = $col["x"] - $bp["left"]["width"] / 2;
$w = $col["used-width"] + ($bp["left"]["width"] + $bp["right"]["width"] ) / 2;
if ( $bp["top"]["style"] !== "none" && $bp["top"]["width"] > 0 ) {
$widths = array($bp["top"]["width"],
$bp["right"]["width"],
$bp["bottom"]["width"],
$bp["left"]["width"]);
$method = "_border_". $bp["top"]["style"];
$this->$method($x, $y, $w, $bp["top"]["color"], $widths, "top", "square");
}
if ( $draw_bottom ) {
$bp = $cellmap->get_border_properties($num_rows - 1, $j);
if ( $bp["bottom"]["style"] === "none" || $bp["bottom"]["width"] <= 0 )
continue;
$y = $bottom_row["y"] + $bottom_row["height"] + $bp["bottom"]["width"] / 2;
$widths = array($bp["top"]["width"],
$bp["right"]["width"],
$bp["bottom"]["width"],
$bp["left"]["width"]);
$method = "_border_". $bp["bottom"]["style"];
$this->$method($x, $y, $w, $bp["bottom"]["color"], $widths, "bottom", "square");
}
}
$j = $cells["columns"][0];
$left_col = $cellmap->get_column($j);
if (in_array($num_cols - 1, $cells["columns"])) {
$draw_right = true;
$right_col = $cellmap->get_column($num_cols - 1);
} else
$draw_right = false;
// Draw the vertical borders
foreach ( $cells["rows"] as $i ) {
$bp = $cellmap->get_border_properties($i, $j);
$x = $left_col["x"] - $bp["left"]["width"] / 2;
$row = $cellmap->get_row($i);
$y = $row["y"] - $bp["top"]["width"] / 2;
$h = $row["height"] + ($bp["top"]["width"] + $bp["bottom"]["width"])/ 2;
if ( $bp["left"]["style"] !== "none" && $bp["left"]["width"] > 0 ) {
$widths = array($bp["top"]["width"],
$bp["right"]["width"],
$bp["bottom"]["width"],
$bp["left"]["width"]);
$method = "_border_" . $bp["left"]["style"];
$this->$method($x, $y, $h, $bp["left"]["color"], $widths, "left", "square");
}
if ( $draw_right ) {
$bp = $cellmap->get_border_properties($i, $num_cols - 1);
if ( $bp["right"]["style"] === "none" || $bp["right"]["width"] <= 0 )
continue;
$x = $right_col["x"] + $right_col["used-width"] + $bp["right"]["width"] / 2;
$widths = array($bp["top"]["width"],
$bp["right"]["width"],
$bp["bottom"]["width"],
$bp["left"]["width"]);
$method = "_border_" . $bp["right"]["style"];
$this->$method($x, $y, $h, $bp["right"]["color"], $widths, "right", "square");
}
}
}
}

View File

@@ -0,0 +1,342 @@
<?php
/**
* DOMPDF - PHP5 HTML to PDF renderer
*
* File: $RCSfile: table_frame_decorator.cls.php,v $
* Created on: 2004-06-04
*
* Copyright (c) 2004 - Benj Carson <benjcarson@digitaljunkies.ca>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library in the file LICENSE.LGPL; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*
* Alternatively, you may distribute this software under the terms of the
* PHP License, version 3.0 or later. A copy of this license should have
* been distributed with this file in the file LICENSE.PHP . If this is not
* the case, you can obtain a copy at http://www.php.net/license/3_0.txt.
*
* The latest version of DOMPDF might be available at:
* http://www.dompdf.com/
*
* @link http://www.dompdf.com/
* @copyright 2004 Benj Carson
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @package dompdf
*/
/* $Id: table_frame_decorator.cls.php 317 2010-10-06 13:06:57Z fabien.menager $ */
/**
* Decorates Frames for table layout
*
* @access private
* @package dompdf
*/
class Table_Frame_Decorator extends Frame_Decorator {
static $VALID_CHILDREN = array("table-row-group",
"table-row",
"table-header-group",
"table-footer-group",
"table-column",
"table-column-group",
"table-caption",
"table-cell");
static $ROW_GROUPS = array('table-row-group',
'table-header-group',
'table-footer-group');
/**
* The Cellmap object for this table. The cellmap maps table cells
* to rows and columns, and aids in calculating column widths.
*
* @var Cellmap
*/
protected $_cellmap;
/**
* The minimum width of the table, in pt
*
* @var float
*/
protected $_min_width;
/**
* The maximum width of the table, in pt
*
* @var float
*/
protected $_max_width;
/**
* Table header rows. Each table header is duplicated when a table
* spans pages.
*
* @var array
*/
protected $_headers;
/**
* Table footer rows. Each table footer is duplicated when a table
* spans pages.
*
* @var array
*/
protected $_footers;
/**
* Class constructor
*
* @param Frame $frame the frame to decorate
*/
function __construct(Frame $frame, DOMPDF $dompdf) {
parent::__construct($frame, $dompdf);
$this->_cellmap = new Cellmap($this);
$this->_min_width = null;
$this->_max_width = null;
$this->_headers = array();
$this->_footers = array();
}
function reset() {
parent::reset();
$this->_cellmap->reset();
$this->_min_width = null;
$this->_max_width = null;
$this->_headers = array();
$this->_footers = array();
$this->_reflower->reset();
}
//........................................................................
/**
* split the table at $row. $row and all subsequent rows will be
* added to the clone. This method is overidden in order to remove
* frames from the cellmap properly.
*
* @param Frame $row
*/
function split($child = null, $force_pagebreak = false) {
if ( is_null($child) ) {
parent::split();
return;
}
// If $child is a header or if it is the first non-header row, do
// not duplicate headers, simply move the table to the next page.
if ( count($this->_headers) && !in_array($child, $this->_headers, true) &&
!in_array($child->get_prev_sibling(), $this->_headers, true) ) {
$first_header = null;
// Insert copies of the table headers before $child
foreach ($this->_headers as $header) {
$new_header = $header->deep_copy();
if ( is_null($first_header) )
$first_header = $new_header;
$this->insert_child_before($new_header, $child);
}
parent::split($first_header);
} else if ( in_array($child->get_style()->display, self::$ROW_GROUPS) ) {
// Individual rows should have already been handled
parent::split($child);
} else {
$iter = $child;
while ($iter) {
$this->_cellmap->remove_row($iter);
$iter = $iter->get_next_sibling();
}
parent::split($child);
}
}
/**
* Static function to locate the parent table of a frame
*
* @param Frame $frame
* @return Table_Frame_Decorator the table that is an ancestor of $frame
*/
static function find_parent_table(Frame $frame) {
while ( $frame = $frame->get_parent() )
if ( in_array($frame->get_style()->display, Style::$TABLE_TYPES) )
break;
return $frame;
}
/**
* Return this table's Cellmap
*
* @return Cellmap
*/
function get_cellmap() { return $this->_cellmap; }
/**
* Return the minimum width of this table
*
* @return float
*/
function get_min_width() { return $this->_min_width; }
/**
* Return the maximum width of this table
*
* @return float
*/
function get_max_width() { return $this->_max_width; }
/**
* Set the minimum width of the table
*
* @param float $width the new minimum width
*/
function set_min_width($width) { $this->_min_width = $width; }
/**
* Set the maximum width of the table
*
* @param float $width the new maximum width
*/
function set_max_width($width) { $this->_max_width = $width; }
/**
* Restructure tree so that the table has the correct structure.
* Invalid children (i.e. all non-table-rows) are moved below the
* table.
*/
function normalise() {
// Store frames generated by invalid tags and move them outside the table
$erroneous_frames = array();
$anon_row = false;
$iter = $this->get_first_child();
while ( $iter ) {
$child = $iter;
$iter = $iter->get_next_sibling();
$display = $child->get_style()->display;
if ( $anon_row ) {
if ( $display === "table-row" ) {
// Add the previous anonymous row
$this->insert_child_before($table_row, $child);
$table_row->normalise();
$child->normalise();
$anon_row = false;
continue;
}
// add the child to the anonymous row
$table_row->append_child($child);
continue;
} else {
if ( $display === "table-row" ) {
$child->normalise();
continue;
}
if ( $display === "table-cell") {
// Create an anonymous table row
$tr = $this->get_node()->ownerDocument->createElement("tr");
$frame = new Frame($tr);
$css = $this->get_style()->get_stylesheet();
$style = $css->create_style();
$style->inherit($this->get_style());
// Lookup styles for tr tags. If the user wants styles to work
// better, they should make the tr explicit... I'm not going to
// try to guess what they intended.
if ( $tr_style = $css->lookup("tr") )
$style->merge($tr_style);
// Okay, I have absolutely no idea why I need this clone here, but
// if it's omitted, php (as of 2004-07-28) segfaults.
$frame->set_style(clone $style);
$table_row = Frame_Factory::decorate_frame($frame, $this->_dompdf);
$table_row->set_root($this->_root);
// Add the cell to the row
$table_row->append_child($child);
$anon_row = true;
continue;
}
if ( !in_array($display, self::$VALID_CHILDREN) ) {
$erroneous_frames[] = $child;
continue;
}
// Normalise other table parts (i.e. row groups)
foreach ($child->get_children() as $grandchild) {
if ( $grandchild->get_style()->display === "table-row" )
$grandchild->normalise();
}
// Add headers and footers
if ( $display === "table-header-group" )
$this->_headers[] = $child;
else if ( $display === "table-footer-group" )
$this->_footers[] = $child;
}
}
if ( $anon_row ) {
// Add the row to the table
$this->_frame->append_child($table_row);
$table_row->normalise();
$this->_cellmap->add_row();
}
foreach ($erroneous_frames as $frame)
$this->move_after($frame);
}
//........................................................................
/**
* Moves the specified frame and it's corresponding node outside of
* the table.
*
* @param Frame $frame the frame to move
*/
function move_after(Frame $frame) {
$this->get_parent()->insert_child_after($frame, $this);
}
}

View File

@@ -0,0 +1,574 @@
<?php
/**
* DOMPDF - PHP5 HTML to PDF renderer
*
* File: $RCSfile: table_frame_reflower.cls.php,v $
* Created on: 2004-06-17
*
* Copyright (c) 2004 - Benj Carson <benjcarson@digitaljunkies.ca>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library in the file LICENSE.LGPL; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*
* Alternatively, you may distribute this software under the terms of the
* PHP License, version 3.0 or later. A copy of this license should have
* been distributed with this file in the file LICENSE.PHP . If this is not
* the case, you can obtain a copy at http://www.php.net/license/3_0.txt.
*
* The latest version of DOMPDF might be available at:
* http://www.dompdf.com/
*
* @link http://www.dompdf.com/
* @copyright 2004 Benj Carson
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @package dompdf
*/
/* $Id: table_frame_reflower.cls.php 359 2011-02-05 12:15:06Z fabien.menager $ */
/**
* Reflows tables
*
* @access private
* @package dompdf
*/
class Table_Frame_Reflower extends Frame_Reflower {
/**
* Cache of results between call to get_min_max_width and assign_widths
*
* @var array
*/
protected $_state;
function __construct(Table_Frame_Decorator $frame) {
$this->_state = null;
parent::__construct($frame);
}
/**
* State is held here so it needs to be reset along with the decorator
*/
function reset() {
$this->_state = null;
$this->_min_max_cache = null;
}
//........................................................................
protected function _assign_widths() {
$style = $this->_frame->get_style();
// Find the min/max width of the table and sort the columns into
// absolute/percent/auto arrays
$min_width = $this->_state["min_width"];
$max_width = $this->_state["max_width"];
$percent_used = $this->_state["percent_used"];
$absolute_used = $this->_state["absolute_used"];
$auto_min = $this->_state["auto_min"];
$absolute =& $this->_state["absolute"];
$percent =& $this->_state["percent"];
$auto =& $this->_state["auto"];
// Determine the actual width of the table
$cb = $this->_frame->get_containing_block();
$columns =& $this->_frame->get_cellmap()->get_columns();
$width = $style->width;
// Calculate padding & border fudge factor
$left = $style->margin_left;
$right = $style->margin_right;
$left = $left === "auto" ? 0 : $style->length_in_pt($left, $cb["w"]);
$right = $right === "auto" ? 0 : $style->length_in_pt($right, $cb["w"]);
$delta = $left + $right + $style->length_in_pt(array($style->padding_left,
$style->border_left_width,
$style->border_right_width,
$style->padding_right), $cb["w"]);
$min_table_width = $style->length_in_pt( $style->min_width, $cb["w"] - $delta );
// min & max widths already include borders & padding
$min_width -= $delta;
$max_width -= $delta;
if ( $width !== "auto" ) {
$preferred_width = $style->length_in_pt($width, $cb["w"]) - $delta;
if ( $preferred_width < $min_table_width )
$preferred_width = $min_table_width;
if ( $preferred_width > $min_width )
$width = $preferred_width;
else
$width = $min_width;
} else {
if ( $max_width + $delta < $cb["w"] )
$width = $max_width;
else if ( $cb["w"] - $delta > $min_width )
$width = $cb["w"] - $delta;
else
$width = $min_width;
if ( $width < $min_table_width )
$width = $min_table_width;
}
// Store our resolved width
$style->width = $width;
$cellmap = $this->_frame->get_cellmap();
// If the whole table fits on the page, then assign each column it's max width
if ( $width == $max_width ) {
foreach (array_keys($columns) as $i)
$cellmap->set_column_width($i, $columns[$i]["max-width"]);
return;
}
// Determine leftover and assign it evenly to all columns
if ( $width > $min_width ) {
// We have four cases to deal with:
//
// 1. All columns are auto--no widths have been specified. In this
// case we distribute extra space across all columns weighted by max-width.
//
// 2. Only absolute widths have been specified. In this case we
// distribute any extra space equally among 'width: auto' columns, or all
// columns if no auto columns have been specified.
//
// 3. Only percentage widths have been specified. In this case we
// normalize the percentage values and distribute any remaining % to
// width: auto columns. We then proceed to assign widths as fractions
// of the table width.
//
// 4. Both absolute and percentage widths have been specified.
// Case 1:
if ( $absolute_used == 0 && $percent_used == 0 ) {
$increment = $width - $min_width;
foreach (array_keys($columns) as $i)
$cellmap->set_column_width($i, $columns[$i]["min-width"] + $increment * ($columns[$i]["max-width"] / $max_width));
return;
}
// Case 2
if ( $absolute_used > 0 && $percent_used == 0 ) {
if ( count($auto) > 0 )
$increment = ($width - $auto_min - $absolute_used) / count($auto);
// Use the absolutely specified width or the increment
foreach (array_keys($columns) as $i) {
if ( $columns[$i]["absolute"] > 0 && count($auto) )
$cellmap->set_column_width($i, $columns[$i]["min-width"]);
else if ( count($auto) )
$cellmap->set_column_width($i, $columns[$i]["min-width"] + $increment);
else {
// All absolute columns
$increment = ($width - $absolute_used) * $columns[$i]["absolute"] / $absolute_used;
$cellmap->set_column_width($i, $columns[$i]["min-width"] + $increment);
}
}
return;
}
// Case 3:
if ( $absolute_used == 0 && $percent_used > 0 ) {
$scale = null;
$remaining = null;
// Scale percent values if the total percentage is > 100, or if all
// values are specified as percentages.
if ( $percent_used > 100 || count($auto) == 0)
$scale = 100 / $percent_used;
else
$scale = 1;
// Account for the minimum space used by the unassigned auto columns
$used_width = $auto_min;
foreach ($percent as $i) {
$columns[$i]["percent"] *= $scale;
$slack = $width - $used_width;
$w = min($columns[$i]["percent"] * $width/100, $slack);
if ( $w < $columns[$i]["min-width"] )
$w = $columns[$i]["min-width"];
$cellmap->set_column_width($i, $w);
$used_width += $w;
}
// This works because $used_width includes the min-width of each
// unassigned column
if ( count($auto) > 0 ) {
$increment = ($width - $used_width) / count($auto);
foreach ($auto as $i)
$cellmap->set_column_width($i, $columns[$i]["min-width"] + $increment);
}
return;
}
// Case 4:
// First-come, first served
if ( $absolute_used > 0 && $percent_used > 0 ) {
$used_width = $auto_min;
foreach ($absolute as $i) {
$cellmap->set_column_width($i, $columns[$i]["min-width"]);
$used_width += $columns[$i]["min-width"];
}
// Scale percent values if the total percentage is > 100 or there
// are no auto values to take up slack
if ( $percent_used > 100 || count($auto) == 0 )
$scale = 100 / $percent_used;
else
$scale = 1;
$remaining_width = $width - $used_width;
foreach ($percent as $i) {
$slack = $remaining_width - $used_width;
$columns[$i]["percent"] *= $scale;
$w = min($columns[$i]["percent"] * $remaining_width / 100, $slack);
if ( $w < $columns[$i]["min-width"] )
$w = $columns[$i]["min-width"];
$columns[$i]["used-width"] = $w;
$used_width += $w;
}
if ( count($auto) > 0 ) {
$increment = ($width - $used_width) / count($auto);
foreach ($auto as $i)
$cellmap->set_column_width($i, $columns[$i]["min-width"] + $increment);
}
return;
}
} else { // we are over constrained
// Each column gets its minimum width
foreach (array_keys($columns) as $i)
$cellmap->set_column_width($i, $columns[$i]["min-width"]);
}
}
//........................................................................
// Determine the frame's height based on min/max height
protected function _calculate_height() {
$style = $this->_frame->get_style();
$height = $style->height;
$cellmap = $this->_frame->get_cellmap();
$cellmap->assign_frame_heights();
$rows = $cellmap->get_rows();
// Determine our content height
$content_height = 0;
foreach ( $rows as $r )
$content_height += $r["height"];
$cb = $this->_frame->get_containing_block();
if ( !($style->overflow === "visible" ||
($style->overflow === "hidden" && $height === "auto")) ) {
// Only handle min/max height if the height is independent of the frame's content
$min_height = $style->min_height;
$max_height = $style->max_height;
if ( isset($cb["h"]) ) {
$min_height = $style->length_in_pt($min_height, $cb["h"]);
$max_height = $style->length_in_pt($max_height, $cb["h"]);
} else if ( isset($cb["w"]) ) {
if ( mb_strpos($min_height, "%") !== false )
$min_height = 0;
else
$min_height = $style->length_in_pt($min_height, $cb["w"]);
if ( mb_strpos($max_height, "%") !== false )
$max_height = "none";
else
$max_height = $style->length_in_pt($max_height, $cb["w"]);
}
if ( $max_height !== "none" && $min_height > $max_height )
// Swap 'em
list($max_height, $min_height) = array($min_height, $max_height);
if ( $max_height !== "none" && $height > $max_height )
$height = $max_height;
if ( $height < $min_height )
$height = $min_height;
} else {
// Use the content height or the height value, whichever is greater
if ( $height !== "auto" ) {
$height = $style->length_in_pt($height, $cb["h"]);
if ( $height <= $content_height )
$height = $content_height;
else
$cellmap->set_frame_heights($height,$content_height);
} else
$height = $content_height;
}
return $height;
}
//........................................................................
function reflow(Frame_Decorator $block = null) {
$frame = $this->_frame;
// Check if a page break is forced
$page = $frame->get_root();
$page->check_forced_page_break($frame);
// Bail if the page is full
if ( $page->is_full() )
return;
// Let the page know that we're reflowing a table so that splits
// are suppressed (simply setting page-break-inside: avoid won't
// work because we may have an arbitrary number of block elements
// inside tds.)
$page->table_reflow_start();
// Collapse vertical margins, if required
$this->_collapse_margins();
$frame->position();
// Table layout algorithm:
// http://www.w3.org/TR/CSS21/tables.html#auto-table-layout
if ( is_null($this->_state) )
$this->get_min_max_width();
$cb = $frame->get_containing_block();
$style = $frame->get_style();
// This is slightly inexact, but should be okay. Add half the
// border-spacing to the table as padding. The other half is added to
// the cells themselves.
if ( $style->border_collapse === "separate" ) {
list($h, $v) = $style->border_spacing;
$v = $style->length_in_pt($v) / 2;
$h = $style->length_in_pt($h) / 2;
$style->padding_left = $style->length_in_pt($style->padding_left, $cb["w"]) + $h;
$style->padding_right = $style->length_in_pt($style->padding_right, $cb["w"]) + $h;
$style->padding_top = $style->length_in_pt($style->padding_top, $cb["h"]) + $v;
$style->padding_bottom = $style->length_in_pt($style->padding_bottom, $cb["h"]) + $v;
}
$this->_assign_widths();
// Adjust left & right margins, if they are auto
$width = $style->width;
$left = $style->margin_left;
$right = $style->margin_right;
$diff = $cb["w"] - $width;
if ( $left === "auto" && $right === "auto" && $diff > 0 ) {
$left = $right = $diff / 2;
$style->margin_left = "$left pt";
$style->margin_right = "$right pt";
} else {
if($left === "auto") {
$left = $style->length_in_pt($cb["w"] - $right - $width, $cb["w"]);
}
if($right === "auto") {
$left = $style->length_in_pt($left, $cb["w"]);
}
}
list($x, $y) = $frame->get_position();
// Determine the content edge
$content_x = $x + $left + $style->length_in_pt(array($style->padding_left,
$style->border_left_width), $cb["w"]);
$content_y = $y + $style->length_in_pt(array($style->margin_top,
$style->border_top_width,
$style->padding_top), $cb["h"]);
if ( isset($cb["h"]) )
$h = $cb["h"];
else
$h = null;
$cellmap = $frame->get_cellmap();
$col =& $cellmap->get_column(0);
$col["x"] = $content_x;
$row =& $cellmap->get_row(0);
$row["y"] = $content_y;
$cellmap->assign_x_positions();
// Set the containing block of each child & reflow
foreach ( $frame->get_children() as $child ) {
// Bail if the page is full
if ( !$page->in_nested_table() && $page->is_full() )
break;
$child->set_containing_block($content_x, $content_y, $width, $h);
$child->reflow();
if ( !$page->in_nested_table() )
// Check if a split has occured
$page->check_page_break($child);
}
// Assign heights to our cells:
$style->height = $this->_calculate_height();
if ( $style->border_collapse === "collapse" ) {
// Unset our borders because our cells are now using them
$style->border_style = "none";
}
$page->table_reflow_end();
// Debugging:
//echo ($this->_frame->get_cellmap());
if ( $block ) {
$block->add_frame_to_line($this->_frame);
}
}
//........................................................................
function get_min_max_width() {
if ( !is_null($this->_min_max_cache) )
return $this->_min_max_cache;
$style = $this->_frame->get_style();
$this->_frame->normalise();
// Add the cells to the cellmap (this will calcluate column widths as
// frames are added)
$this->_frame->get_cellmap()->add_frame($this->_frame);
// Find the min/max width of the table and sort the columns into
// absolute/percent/auto arrays
$this->_state = array();
$this->_state["min_width"] = 0;
$this->_state["max_width"] = 0;
$this->_state["percent_used"] = 0;
$this->_state["absolute_used"] = 0;
$this->_state["auto_min"] = 0;
$this->_state["absolute"] = array();
$this->_state["percent"] = array();
$this->_state["auto"] = array();
$columns =& $this->_frame->get_cellmap()->get_columns();
foreach (array_keys($columns) as $i) {
$this->_state["min_width"] += $columns[$i]["min-width"];
$this->_state["max_width"] += $columns[$i]["max-width"];
if ( $columns[$i]["absolute"] > 0 ) {
$this->_state["absolute"][] = $i;
$this->_state["absolute_used"] += $columns[$i]["absolute"];
} else if ( $columns[$i]["percent"] > 0 ) {
$this->_state["percent"][] = $i;
$this->_state["percent_used"] += $columns[$i]["percent"];
} else {
$this->_state["auto"][] = $i;
$this->_state["auto_min"] += $columns[$i]["min-width"];
}
}
// Account for margins & padding
$dims = array($style->border_left_width,
$style->border_right_width,
$style->padding_left,
$style->padding_right,
$style->margin_left,
$style->margin_right);
if ( $style->border_collapse !== "collapse" )
list($dims[]) = $style->border_spacing;
$delta = $style->length_in_pt($dims, $this->_frame->get_containing_block("w"));
$this->_state["min_width"] += $delta;
$this->_state["max_width"] += $delta;
return $this->_min_max_cache = array($this->_state["min_width"], $this->_state["max_width"],
"min" => $this->_state["min_width"], "max" => $this->_state["max_width"]);
}
}

View File

@@ -0,0 +1,81 @@
<?php
/**
* DOMPDF - PHP5 HTML to PDF renderer
*
* File: $RCSfile: table_row_frame_decorator.cls.php,v $
* Created on: 2004-06-07
*
* Copyright (c) 2004 - Benj Carson <benjcarson@digitaljunkies.ca>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library in the file LICENSE.LGPL; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*
* Alternatively, you may distribute this software under the terms of the
* PHP License, version 3.0 or later. A copy of this license should have
* been distributed with this file in the file LICENSE.PHP . If this is not
* the case, you can obtain a copy at http://www.php.net/license/3_0.txt.
*
* The latest version of DOMPDF might be available at:
* http://www.dompdf.com/
*
* @link http://www.dompdf.com/
* @copyright 2004 Benj Carson
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @package dompdf
*/
/* $Id: table_row_frame_decorator.cls.php 216 2010-03-11 22:49:18Z ryan.masten $ */
/**
* Decorates Frames for table row layout
*
* @access private
* @package dompdf
*/
class Table_Row_Frame_Decorator extends Frame_Decorator {
// protected members
function __construct(Frame $frame, DOMPDF $dompdf) {
parent::__construct($frame, $dompdf);
}
//........................................................................
/**
* Remove all non table-cell frames from this row and move them after
* the table.
*/
function normalise() {
// Find our table parent
$p = Table_Frame_Decorator::find_parent_table($this);
$erroneous_frames = array();
foreach ($this->get_children() as $child) {
$display = $child->get_style()->display;
if ( $display !== "table-cell" )
$erroneous_frames[] = $child;
}
// dump the extra nodes after the table.
foreach ($erroneous_frames as $frame)
$p->move_after($frame);
}
}

View File

@@ -0,0 +1,94 @@
<?php
/**
* DOMPDF - PHP5 HTML to PDF renderer
*
* File: $RCSfile: table_row_frame_reflower.cls.php,v $
* Created on: 2004-06-17
*
* Copyright (c) 2004 - Benj Carson <benjcarson@digitaljunkies.ca>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library in the file LICENSE.LGPL; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*
* Alternatively, you may distribute this software under the terms of the
* PHP License, version 3.0 or later. A copy of this license should have
* been distributed with this file in the file LICENSE.PHP . If this is not
* the case, you can obtain a copy at http://www.php.net/license/3_0.txt.
*
* The latest version of DOMPDF might be available at:
* http://www.dompdf.com/
*
* @link http://www.dompdf.com/
* @copyright 2004 Benj Carson
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @package dompdf
*/
/* $Id: table_row_frame_reflower.cls.php 357 2011-01-30 20:56:46Z fabien.menager $ */
/**
* Reflows table rows
*
* @access private
* @package dompdf
*/
class Table_Row_Frame_Reflower extends Frame_Reflower {
function __construct(Table_Row_Frame_Decorator $frame) {
parent::__construct($frame);
}
//........................................................................
function reflow(Frame_Decorator $block = null) {
$page = $this->_frame->get_root();
if ( $page->is_full() )
return;
$this->_frame->position();
$style = $this->_frame->get_style();
$cb = $this->_frame->get_containing_block();
foreach ($this->_frame->get_children() as $child) {
if ( $page->is_full() )
return;
$child->set_containing_block($cb);
$child->reflow();
}
if ( $page->is_full() )
return;
$table = Table_Frame_Decorator::find_parent_table($this->_frame);
$cellmap = $table->get_cellmap();
$style->width = $cellmap->get_frame_width($this->_frame);
$style->height = $cellmap->get_frame_height($this->_frame);
$this->_frame->set_position($cellmap->get_frame_position($this->_frame));
}
//........................................................................
function get_min_max_width() {
throw new DOMPDF_Exception("Min/max width is undefined for table rows");
}
}

View File

@@ -0,0 +1,96 @@
<?php
/**
* DOMPDF - PHP5 HTML to PDF renderer
*
* File: $RCSfile: table_row_group_frame_decorator.cls.php,v $
* Created on: 2004-06-02
*
* Copyright (c) 2004 - Benj Carson <benjcarson@digitaljunkies.ca>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library in the file LICENSE.LGPL; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*
* Alternatively, you may distribute this software under the terms of the
* PHP License, version 3.0 or later. A copy of this license should have
* been distributed with this file in the file LICENSE.PHP . If this is not
* the case, you can obtain a copy at http://www.php.net/license/3_0.txt.
*
* The latest version of DOMPDF might be available at:
* http://www.dompdf.com/
*
* @link http://www.dompdf.com/
* @copyright 2004-6 Benj Carson
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @package dompdf
*/
/* $Id: table_row_group_frame_decorator.cls.php 317 2010-10-06 13:06:57Z fabien.menager $ */
/**
* Table row group decorator
*
* Overrides split() method for tbody, thead & tfoot elements
*
* @access private
* @package dompdf
*/
class Table_Row_Group_Frame_Decorator extends Frame_Decorator {
/**
* Class constructor
*
* @param Frame $frame Frame to decorate
* @param DOMPDF $dompdf Current dompdf instance
*/
function __construct(Frame $frame, DOMPDF $dompdf) {
parent::__construct($frame, $dompdf);
}
/**
* Override split() to remove all child rows and this element from the cellmap
*
* @param Frame $child
*/
function split($child = null, $force_pagebreak = false) {
if ( is_null($child) ) {
parent::split();
return;
}
// Remove child & all subsequent rows from the cellmap
$cellmap = $this->get_parent()->get_cellmap();
$iter = $child;
while ( $iter ) {
$cellmap->remove_row($iter);
$iter = $iter->get_next_sibling();
}
// If we are splitting at the first child remove the
// table-row-group from the cellmap as well
if ( $child === $this->get_first_child() ) {
$cellmap->remove_row_group($this);
parent::split();
return;
}
$cellmap->update_row_group($this, $child->get_prev_sibling());
parent::split($child);
}
}

View File

@@ -0,0 +1,92 @@
<?php
/**
* DOMPDF - PHP5 HTML to PDF renderer
*
* File: $RCSfile: table_row_group_frame_reflower.cls.php,v $
* Created on: 2004-07-26
*
* Copyright (c) 2004 - Benj Carson <benjcarson@digitaljunkies.ca>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library in the file LICENSE.LGPL; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*
* Alternatively, you may distribute this software under the terms of the
* PHP License, version 3.0 or later. A copy of this license should have
* been distributed with this file in the file LICENSE.PHP . If this is not
* the case, you can obtain a copy at http://www.php.net/license/3_0.txt.
*
* The latest version of DOMPDF might be available at:
* http://www.dompdf.com/
*
* @link http://www.dompdf.com/
* @copyright 2004 Benj Carson
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @package dompdf
*/
/* $Id: table_row_group_frame_reflower.cls.php 357 2011-01-30 20:56:46Z fabien.menager $ */
/**
* Reflows table row groups (e.g. tbody tags)
*
* @access private
* @package dompdf
*/
class Table_Row_Group_Frame_Reflower extends Frame_Reflower {
function __construct($frame) {
parent::__construct($frame);
}
function reflow(Frame_Decorator $block = null) {
$page = $this->_frame->get_root();
$style = $this->_frame->get_style();
// Our width is equal to the width of our parent table
$table = Table_Frame_Decorator::find_parent_table($this->_frame);
$cb = $this->_frame->get_containing_block();
foreach ( $this->_frame->get_children() as $child) {
// Bail if the page is full
if ( $page->is_full() )
return;
$child->set_containing_block($cb["x"], $cb["y"], $cb["w"], $cb["h"]);
$child->reflow();
// Check if a split has occured
$page->check_page_break($child);
}
if ( $page->is_full() )
return;
$cellmap = $table->get_cellmap();
$style->width = $cellmap->get_frame_width($this->_frame);
$style->height = $cellmap->get_frame_height($this->_frame);
$this->_frame->set_position($cellmap->get_frame_position($this->_frame));
if ( $table->get_style()->border_collapse === "collapse" )
// Unset our borders because our cells are now using them
$style->border_style = "none";
}
}

View File

@@ -0,0 +1,68 @@
<?php
/**
* DOMPDF - PHP5 HTML to PDF renderer
*
* File: $RCSfile: table_row_positioner.cls.php,v $
* Created on: 2004-06-08
*
* Copyright (c) 2004 - Benj Carson <benjcarson@digitaljunkies.ca>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library in the file LICENSE.LGPL; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*
* Alternatively, you may distribute this software under the terms of the
* PHP License, version 3.0 or later. A copy of this license should have
* been distributed with this file in the file LICENSE.PHP . If this is not
* the case, you can obtain a copy at http://www.php.net/license/3_0.txt.
*
* The latest version of DOMPDF might be available at:
* http://www.dompdf.com/
*
* @link http://www.dompdf.com/
* @copyright 2004 Benj Carson
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @package dompdf
*/
/* $Id: table_row_positioner.cls.php 216 2010-03-11 22:49:18Z ryan.masten $ */
/**
* Positions table rows
*
* @access private
* @package dompdf
*/
class Table_Row_Positioner extends Positioner {
function __construct(Frame_Decorator $frame) { parent::__construct($frame); }
//........................................................................
function position() {
$cb = $this->_frame->get_containing_block();
$p = $this->_frame->get_prev_sibling();
if ( $p )
$y = $p->get_position("y") + $p->get_margin_height();
else
$y = $cb["y"];
$this->_frame->set_position($cb["x"], $y);
}
}

498
pdf/include/tcpdf_adapter.cls.php Executable file
View File

@@ -0,0 +1,498 @@
<?php
/**
* DOMPDF - PHP5 HTML to PDF renderer
*
* File: $RCSfile$
* Created on: 2004-08-04
*
* Copyright (c) 2004 - Benj Carson <benjcarson@digitaljunkies.ca>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library in the file LICENSE.LGPL; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*
* Alternatively, you may distribute this software under the terms of the
* PHP License, version 3.0 or later. A copy of this license should have
* been distributed with this file in the file LICENSE.PHP . If this is not
* the case, you can obtain a copy at http://www.php.net/license/3_0.txt.
*
* The latest version of DOMPDF might be available at:
* http://www.dompdf.com/
*
* @link http://www.dompdf.com/
* @copyright 2004 Benj Carson
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @package dompdf
*/
/* $Id: tcpdf_adapter.cls.php 311 2010-09-05 20:02:01Z fabien.menager $ */
require_once(DOMPDF_LIB_DIR . '/tcpdf/tcpdf.php');
/**
* TCPDF PDF Rendering interface
*
* TCPDF_Adapter provides a simple, stateless interface to TCPDF.
*
* Unless otherwise mentioned, all dimensions are in points (1/72 in).
* The coordinate origin is in the top left corner and y values
* increase downwards.
*
* See {@link http://tcpdf.sourceforge.net} for more information on
* the underlying TCPDF class.
*
* @package dompdf
*/
class TCPDF_Adapter implements Canvas {
/**
* Dimensions of paper sizes in points
*
* @var array;
*/
static public $PAPER_SIZES = array(); // Set to
// CPDF_Adapter::$PAPER_SIZES below.
/**
* Instance of the TCPDF class
*
* @var TCPDF
*/
private $_pdf;
/**
* PDF width in points
*
* @var float
*/
private $_width;
/**
* PDF height in points
*
* @var float
*/
private $_height;
/**
* Last fill colour used
*
* @var array
*/
private $_last_fill_color;
/**
* Last stroke colour used
*
* @var array
*/
private $_last_stroke_color;
/**
* Last line width used
*
* @var float
*/
private $_last_line_width;
/**
* Total number of pages
*
* @var int
*/
private $_page_count;
/**
* Text to display on every page
*
* @var array
*/
private $_page_text;
/**
* Array of pages for accessing after initial rendering is complete
*
* @var array
*/
private $_pages;
/**
* Class constructor
*
* @param mixed $paper The size of paper to use either a string (see {@link CPDF_Adapter::$PAPER_SIZES}) or
* an array(xmin,ymin,xmax,ymax)
* @param string $orientation The orientation of the document (either 'landscape' or 'portrait')
*/
function __construct($paper = "letter", $orientation = "portrait") {
if ( is_array($paper) )
$size = $paper;
else if ( isset(self::$PAPER_SIZES[mb_strtolower($paper)]) )
$size = self::$PAPER_SIZE[$paper];
else
$size = self::$PAPER_SIZE["letter"];
if ( mb_strtolower($orientation) === "landscape" ) {
list($size[2], $size[3]) = array($size[3], $size[2]);
}
$this->_width = $size[2] - $size[0];
$this->_height = $size[3] - $size[1];
$this->_pdf = new TCPDF("P", "pt", array($this->_width, $this->_height));
$this->_pdf->Setcreator("DOMPDF Converter");
$this->_pdf->AddPage();
$this->_page_number = $this->_page_count = 1;
$this->_page_text = array();
$this->_last_fill_color =
$this->_last_stroke_color =
$this->_last_line_width = null;
}
/**
* Remaps y coords from 4th to 1st quadrant
*
* @param float $y
* @return float
*/
protected function y($y) { return $this->_height - $y; }
/**
* Sets the stroke colour
*
* @param array $color
*/
protected function _set_stroke_colour($colour) {
$colour[0] = round(255 * $colour[0]);
$colour[1] = round(255 * $colour[1]);
$colour[2] = round(255 * $colour[2]);
if ( is_null($this->_last_stroke_color) || $color != $this->_last_stroke_color ) {
$this->_pdf->SetDrawColor($color[0],$color[1],$color[2]);
$this->_last_stroke_color = $color;
}
}
/**
* Sets the fill colour
*
* @param array $color
*/
protected function _set_fill_colour($colour) {
$colour[0] = round(255 * $colour[0]);
$colour[1] = round(255 * $colour[1]);
$colour[2] = round(255 * $colour[2]);
if ( is_null($this->_last_fill_color) || $color != $this->_last_fill_color ) {
$this->_pdf->SetDrawColor($color[0],$color[1],$color[2]);
$this->_last_fill_color = $color;
}
}
/**
* Return the TCPDF instance
*
* @return TCPDF
*/
function get_tcpdf() { return $this->_pdf; }
/**
* Returns the current page number
*
* @return int
*/
function get_page_number() {
return $this->_page_number;
}
/**
* Returns the total number of pages
*
* @return int
*/
function get_page_count() {
return $this->_page_count;
}
/**
* Sets the total number of pages
*
* @param int $count
*/
function set_page_count($count) {
$this->_page_count = (int)$count;
}
/**
* Draws a line from x1,y1 to x2,y2
*
* See {@link Style::munge_colour()} for the format of the colour array.
* See {@link Cpdf::setLineStyle()} for a description of the format of the
* $style parameter (aka dash).
*
* @param float $x1
* @param float $y1
* @param float $x2
* @param float $y2
* @param array $color
* @param float $width
* @param array $style
*/
function line($x1, $y1, $x2, $y2, $color, $width, $style = null) {
if ( is_null($this->_last_line_width) || $width != $this->_last_line_width ) {
$this->_pdf->SetLineWidth($width);
$this->_last_line_width = $width;
}
$this->_set_stroke_colour($color);
// FIXME: ugh, need to handle different styles here
$this->_pdf->line($x1, $y1, $x2, $y2);
}
/**
* Draws a rectangle at x1,y1 with width w and height h
*
* See {@link Style::munge_colour()} for the format of the colour array.
* See {@link Cpdf::setLineStyle()} for a description of the $style
* parameter (aka dash)
*
* @param float $x1
* @param float $y1
* @param float $w
* @param float $h
* @param array $color
* @param float $width
* @param array $style
*/
function rectangle($x1, $y1, $w, $h, $color, $width, $style = null) {
if ( is_null($this->_last_line_width) || $width != $this->_last_line_width ) {
$this->_pdf->SetLineWidth($width);
$this->_last_line_width = $width;
}
$this->_set_stroke_colour($color);
// FIXME: ugh, need to handle styles here
$this->_pdf->rect($x1, $y1, $w, $h);
}
/**
* Draws a filled rectangle at x1,y1 with width w and height h
*
* See {@link Style::munge_colour()} for the format of the colour array.
*
* @param float $x1
* @param float $y1
* @param float $w
* @param float $h
* @param array $color
*/
function filled_rectangle($x1, $y1, $w, $h, $color) {
$this->_set_fill_colour($color);
// FIXME: ugh, need to handle styles here
$this->_pdf->rect($x1, $y1, $w, $h, "F");
}
/**
* Draws a polygon
*
* The polygon is formed by joining all the points stored in the $points
* array. $points has the following structure:
* <code>
* array(0 => x1,
* 1 => y1,
* 2 => x2,
* 3 => y2,
* ...
* );
* </code>
*
* See {@link Style::munge_colour()} for the format of the colour array.
* See {@link Cpdf::setLineStyle()} for a description of the $style
* parameter (aka dash)
*
* @param array $points
* @param array $color
* @param float $width
* @param array $style
* @param bool $fill Fills the polygon if true
*/
function polygon($points, $color, $width = null, $style = null, $fill = false) {
// FIXME
}
/**
* Draws a circle at $x,$y with radius $r
*
* See {@link Style::munge_colour()} for the format of the colour array.
* See {@link Cpdf::setLineStyle()} for a description of the $style
* parameter (aka dash)
*
* @param float $x
* @param float $y
* @param float $r
* @param array $color
* @param float $width
* @param array $style
* @param bool $fill Fills the circle if true
*/
function circle($x, $y, $r, $color, $width = null, $style = null, $fill = false) {
// FIXME
}
/**
* Add an image to the pdf.
*
* The image is placed at the specified x and y coordinates with the
* given width and height.
*
* @param string $img_url the path to the image
* @param string $img_type the type (e.g. extension) of the image
* @param float $x x position
* @param float $y y position
* @param int $w width (in pixels)
* @param int $h height (in pixels)
*/
function image($img_url, $img_type, $x, $y, $w, $h) {
// FIXME
}
/**
* Writes text at the specified x and y coordinates
*
* See {@link Style::munge_colour()} for the format of the colour array.
*
* @param float $x
* @param float $y
* @param string $text the text to write
* @param string $font the font file to use
* @param float $size the font size, in points
* @param array $color
* @param float $adjust word spacing adjustment
*/
function text($x, $y, $text, $font, $size, $color = array(0,0,0), $adjust = 0) {
// FIXME
}
function javascript($code) {
// FIXME
}
/**
* Add a named destination (similar to <a name="foo">...</a> in html)
*
* @param string $anchorname The name of the named destination
*/
function add_named_dest($anchorname) {
// FIXME
}
/**
* Add a link to the pdf
*
* @param string $url The url to link to
* @param float $x The x position of the link
* @param float $y The y position of the link
* @param float $width The width of the link
* @param float $height The height of the link
*/
function add_link($url, $x, $y, $width, $height) {
// FIXME
}
/**
* Add meta information to the PDF
*
* @param string $label label of the value (Creator, Producer, etc.)
* @param string $value the text to set
*/
function add_info($label, $value) {
$method = "Set$label";
if ( in_array("Title", "Author", "Keywords", "Subject") && method_exists($this->_pdf, $method) ) {
$this->_pdf->$method($value);
}
}
/**
* Calculates text size, in points
*
* @param string $text the text to be sized
* @param string $font the desired font
* @param float $size the desired font size
* @param float $spacing word spacing, if any
* @return float
*/
function get_text_width($text, $font, $size, $spacing = 0) {
// FIXME
}
/**
* Calculates font height, in points
*
* @param string $font
* @param float $size
* @return float
*/
function get_font_height($font, $size) {
// FIXME
}
/**
* Starts a new page
*
* Subsequent drawing operations will appear on the new page.
*/
function new_page() {
// FIXME
}
/**
* Streams the PDF directly to the browser
*
* @param string $filename the name of the PDF file
* @param array $options associative array, 'Attachment' => 0 or 1, 'compress' => 1 or 0
*/
function stream($filename, $options = null) {
// FIXME
}
/**
* Returns the PDF as a string
*
* @param array $options associative array: 'compress' => 1 or 0
* @return string
*/
function output($options = null) {
// FIXME
}
}
// Workaround for idiotic limitation on statics...
PDFLib_Adapter::$PAPER_SIZES = CPDF_Adapter::$PAPER_SIZES;

View File

@@ -0,0 +1,204 @@
<?php
/**
* DOMPDF - PHP5 HTML to PDF renderer
*
* File: $RCSfile: text_frame_decorator.cls.php,v $
* Created on: 2004-06-04
*
* Copyright (c) 2004 - Benj Carson <benjcarson@digitaljunkies.ca>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library in the file LICENSE.LGPL; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*
* Alternatively, you may distribute this software under the terms of the
* PHP License, version 3.0 or later. A copy of this license should have
* been distributed with this file in the file LICENSE.PHP . If this is not
* the case, you can obtain a copy at http://www.php.net/license/3_0.txt.
*
* The latest version of DOMPDF might be available at:
* http://www.dompdf.com/
*
* @link http://www.dompdf.com/
* @copyright 2004 Benj Carson
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @package dompdf
*/
/* $Id: text_frame_decorator.cls.php 356 2011-01-28 08:56:10Z fabien.menager $ */
/**
* Decorates Frame objects for text layout
*
* @access private
* @package dompdf
*/
class Text_Frame_Decorator extends Frame_Decorator {
// protected members
protected $_text_spacing;
// buggy DOMText::splitText (PHP < 5.2.7)
public static $_buggy_splittext;
function __construct(Frame $frame, DOMPDF $dompdf) {
if ( $frame->get_node()->nodeName !== "#text" )
throw new DOMPDF_Exception("Text_Decorator can only be applied to #text nodes.");
parent::__construct($frame, $dompdf);
$this->_text_spacing = null;
}
//........................................................................
function reset() {
parent::reset();
$this->_text_spacing = null;
}
//........................................................................
// Accessor methods
function get_text_spacing() { return $this->_text_spacing; }
function get_text() {
// FIXME: this should be in a child class (and is incorrect)
// if ( $this->_frame->get_style()->content !== "normal" ) {
// $this->_frame->get_node()->data = $this->_frame->get_style()->content;
// $this->_frame->get_style()->content = "normal";
// }
// pre_r("---");
// $style = $this->_frame->get_style();
// var_dump($text = $this->_frame->get_node()->data);
// var_dump($asc = utf8_decode($text));
// for ($i = 0; $i < strlen($asc); $i++)
// pre_r("$i: " . $asc[$i] . " - " . ord($asc[$i]));
// pre_r("width: " . Font_Metrics::get_text_width($text, $style->font_family, $style->font_size));
return $this->_frame->get_node()->data;
}
//........................................................................
// Vertical margins & padding do not apply to text frames
// http://www.w3.org/TR/CSS21/visudet.html#inline-non-replaced:
//
// The vertical padding, border and margin of an inline, non-replaced box
// start at the top and bottom of the content area, not the
// 'line-height'. But only the 'line-height' is used to calculate the
// height of the line box.
function get_margin_height() {
// This function is called in add_frame_to_line() and is used to
// determine the line height, so we actually want to return the
// 'line-height' property, not the actual margin box
$style = $this->get_style();
$font = $style->font_family;
$size = $style->font_size;
/*
pre_r('-----');
pre_r($style->line_height);
pre_r($style->font_size);
pre_r(Font_Metrics::get_font_height($font, $size));
pre_r(($style->line_height / $size) * Font_Metrics::get_font_height($font, $size));
*/
return ($style->line_height / $size) * Font_Metrics::get_font_height($font, $size);
}
function get_padding_box() {
$pb = $this->_frame->get_padding_box();
$pb[3] = $pb["h"] = $this->_frame->get_style()->height;
return $pb;
}
//........................................................................
// Set method
function set_text_spacing($spacing) {
$style = $this->_frame->get_style();
$this->_text_spacing = $spacing;
$char_spacing = $style->length_in_pt($style->letter_spacing);
// Re-adjust our width to account for the change in spacing
$style->width = Font_Metrics::get_text_width($this->get_text(), $style->font_family, $style->font_size, $spacing, $char_spacing);
}
//........................................................................
// Recalculate the text width
function recalculate_width() {
$style = $this->get_style();
$text = $this->get_text();
$size = $style->font_size;
$font = $style->font_family;
$word_spacing = $style->length_in_pt($style->word_spacing);
$char_spacing = $style->length_in_pt($style->letter_spacing);
return $style->width = Font_Metrics::get_text_width($text, $font, $size, $word_spacing, $char_spacing);
}
//........................................................................
// Text manipulation methods
// split the text in this frame at the offset specified. The remaining
// text is added a sibling frame following this one and is returned.
function split_text($offset) {
if ( $offset == 0 )
return;
if ( self::$_buggy_splittext ) {
// workaround to solve DOMText::spliText() bug parsing multibyte strings
$node = $this->_frame->get_node();
$txt0 = $node->substringData(0, $offset);
$txt1 = $node->substringData($offset, mb_strlen($node->textContent)-1);
$node->replaceData(0, mb_strlen($node->textContent), $txt0);
$split = $node->parentNode->appendChild(new DOMText($txt1));
}
else {
$split = $this->_frame->get_node()->splitText($offset);
}
$deco = $this->copy($split);
$p = $this->get_parent();
$p->insert_child_after($deco, $this, false);
if ( $p instanceof Inline_Frame_Decorator )
$p->split($deco);
return $deco;
}
//........................................................................
function delete_text($offset, $count) {
$this->_frame->get_node()->deleteData($offset, $count);
}
//........................................................................
function set_text($text) {
$this->_frame->get_node()->data = $text;
}
}
Text_Frame_Decorator::$_buggy_splittext = version_compare(PHP_VERSION, '5.2.6', '<=');

View File

@@ -0,0 +1,440 @@
<?php
/**
* DOMPDF - PHP5 HTML to PDF renderer
*
* File: $RCSfile: text_frame_reflower.cls.php,v $
* Created on: 2004-06-17
*
* Copyright (c) 2004 - Benj Carson <benjcarson@digitaljunkies.ca>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library in the file LICENSE.LGPL; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*
* Alternatively, you may distribute this software under the terms of the
* PHP License, version 3.0 or later. A copy of this license should have
* been distributed with this file in the file LICENSE.PHP . If this is not
* the case, you can obtain a copy at http://www.php.net/license/3_0.txt.
*
* The latest version of DOMPDF might be available at:
* http://www.dompdf.com/
*
* @link http://www.dompdf.com/
* @copyright 2004 Benj Carson
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @package dompdf
*/
/* $Id: text_frame_reflower.cls.php 360 2011-02-15 19:33:52Z fabien.menager $ */
/**
* Reflows text frames.
*
* @access private
* @package dompdf
*/
class Text_Frame_Reflower extends Frame_Reflower {
/**
* @var Block_Frame_Decorator
*/
protected $_block_parent; // Nearest block-level ancestor
/**
* @var Text_Frame_Decorator
*/
protected $_frame;
public static $_whitespace_pattern = "/[ \t\r\n\f]+/u";
function __construct(Text_Frame_Decorator $frame) { parent::__construct($frame); }
//........................................................................
protected function _collapse_white_space($text) {
//$text = $this->_frame->get_text();
// if ( $this->_block_parent->get_current_line("w") == 0 )
// $text = ltrim($text, " \n\r\t");
return preg_replace(self::$_whitespace_pattern, " ", $text);
}
//........................................................................
protected function _line_break($text) {
$style = $this->_frame->get_style();
$size = $style->font_size;
$font = $style->font_family;
$current_line = $this->_block_parent->get_current_line();
// Determine the available width
$line_width = $this->_frame->get_containing_block("w");
$current_line_width = $current_line["left"] + $current_line["w"] + $current_line["right"];
$available_width = $line_width - $current_line_width;
// split the text into words
$words = preg_split('/([\s-]+)/u', $text, -1, PREG_SPLIT_DELIM_CAPTURE);
$wc = count($words);
// Account for word-spacing
$word_spacing = $style->length_in_pt($style->word_spacing);
$char_spacing = $style->length_in_pt($style->letter_spacing);
// Determine the frame width including margin, padding & border
$text_width = Font_Metrics::get_text_width($text, $font, $size, $word_spacing, $char_spacing);
$mbp_width =
$style->length_in_pt( array( $style->margin_left,
$style->border_left_width,
$style->padding_left,
$style->padding_right,
$style->border_right_width,
$style->margin_right), $line_width );
$frame_width = $text_width + $mbp_width;
// Debugging:
// pre_r("Text: '" . htmlspecialchars($text). "'");
// pre_r("width: " .$frame_width);
// pre_r("textwidth + delta: $text_width + $mbp_width");
// pre_r("font-size: $size");
// pre_r("cb[w]: " .$line_width);
// pre_r("available width: " . $available_width);
// pre_r("current line width: " . $current_line_width);
// pre_r($words);
if ( $frame_width <= $available_width )
return false;
// Determine the split point
$width = 0;
$str = "";
reset($words);
// @todo support <shy>, <wbr>
for ($i = 0; $i < $wc; $i += 2) {
$word = $words[$i] . (isset($words[$i+1]) ? $words[$i+1] : "");
$word_width = Font_Metrics::get_text_width($word, $font, $size, $word_spacing, $char_spacing);
if ( $width + $word_width + $mbp_width > $available_width )
break;
$width += $word_width;
$str .= $word;
}
// The first word has overflowed. Force it onto the line
if ( $current_line_width == 0 && $width == 0 ) {
$width += $word_width;
$str .= $word;
}
$offset = mb_strlen($str);
// More debugging:
// pre_var_dump($str);
// pre_r("Width: ". $width);
// pre_r("Offset: " . $offset);
return $offset;
}
//........................................................................
protected function _newline_break($text) {
if ( ($i = mb_strpos($text, "\n")) === false)
return false;
return $i+1;
}
//........................................................................
protected function _layout_line() {
$style = $this->_frame->get_style();
$text = $this->_frame->get_text();
$size = $style->font_size;
$font = $style->font_family;
$word_spacing = $style->length_in_pt($style->word_spacing);
$char_spacing = $style->length_in_pt($style->letter_spacing);
// Determine the text height
$style->height = Font_Metrics::get_font_height( $font, $size );
$split = false;
$add_line = false;
// Handle text transform:
// http://www.w3.org/TR/CSS21/text.html#propdef-text-transform
switch (strtolower($style->text_transform)) {
default: break;
case "capitalize": $text = mb_convert_case($text, MB_CASE_TITLE); break;
case "uppercase": $text = mb_convert_case($text, MB_CASE_UPPER); break;
case "lowercase": $text = mb_convert_case($text, MB_CASE_LOWER); break;
}
// Handle white-space property:
// http://www.w3.org/TR/CSS21/text.html#propdef-white-space
switch ($style->white_space) {
default:
case "normal":
$this->_frame->set_text( $text = $this->_collapse_white_space($text) );
if ( $text == "" )
break;
$split = $this->_line_break($text);
break;
case "pre":
$split = $this->_newline_break($text);
$add_line = $split !== false;
break;
case "nowrap":
$this->_frame->set_text( $text = $this->_collapse_white_space($text) );
break;
case "pre-wrap":
$split = $this->_newline_break($text);
if ( ($tmp = $this->_line_break($text)) !== false ) {
$add_line = $split < $tmp;
$split = min($tmp, $split);
} else
$add_line = true;
break;
case "pre-line":
// Collapse white-space except for \n
$this->_frame->set_text( $text = preg_replace( "/[ \t]+/u", " ", $text ) );
if ( $text == "" )
break;
$split = $this->_newline_break($text);
if ( ($tmp = $this->_line_break($text)) !== false ) {
$add_line = $split < $tmp;
$split = min($tmp, $split);
} else
$add_line = true;
break;
}
// Handle degenerate case
if ( $text === "" )
return;
if ( $split !== false) {
// Handle edge cases
if ( $split == 0 && $text === " " ) {
$this->_frame->set_text("");
return;
}
if ( $split == 0 ) {
// Trim newlines from the beginning of the line
//$this->_frame->set_text(ltrim($text, "\n\r"));
$this->_block_parent->add_line();
$this->_frame->position();
// Layout the new line
$this->_layout_line();
}
else if ( $split < mb_strlen($this->_frame->get_text()) ) {
// split the line if required
$this->_frame->split_text($split);
$t = $this->_frame->get_text();
// Remove any trailing newlines
if ( $split > 1 && $t[$split-1] === "\n" )
$this->_frame->set_text( mb_substr($t, 0, -1) );
// Do we need to trim spaces on wrapped lines? This might be desired, however, we
// can't trim the lines here or the layout will be affected if trimming the line
// leaves enough space to fit the next word in the text stream (because pdf layout
// is performed elsewhere).
/*if (!$this->_frame->get_prev_sibling() && !$this->_frame->get_next_sibling()) {
$t = $this->_frame->get_text();
$this->_frame->set_text( trim($t) );
}*/
}
if ( $add_line ) {
$this->_block_parent->add_line();
$this->_frame->position();
}
} else {
// Remove empty space from start and end of line, but only where there isn't an inline sibling
// and the parent node isn't an inline element with siblings
// FIXME: Include non-breaking spaces?
$t = $this->_frame->get_text();
$parent = $this->_frame->get_parent();
$is_inline_frame = get_class($parent) === 'Inline_Frame_Decorator';
if ((!$is_inline_frame && !$this->_frame->get_next_sibling()) ||
( $is_inline_frame && !$parent->get_next_sibling())) {
$t = rtrim($t);
}
if ((!$is_inline_frame && !$this->_frame->get_prev_sibling()) ||
( $is_inline_frame && !$parent->get_prev_sibling())) {
$t = ltrim($t);
}
$this->_frame->set_text( $t );
}
// Set our new width
$width = $this->_frame->recalculate_width();
}
//........................................................................
function reflow(Frame_Decorator $block = null) {
$page = $this->_frame->get_root();
$page->check_forced_page_break($this->_frame);
if ( $page->is_full() )
return;
$this->_block_parent = $this->_frame->find_block_parent();
// Left trim the text if this is the first text on the line and we're
// collapsing white space
// if ( $this->_block_parent->get_current_line("w") == 0 &&
// ($this->_frame->get_style()->white_space !== "pre" ||
// $this->_frame->get_style()->white_space !== "pre-wrap") ) {
// $this->_frame->set_text( ltrim( $this->_frame->get_text() ) );
// }
$this->_frame->position();
$this->_layout_line();
if ( $block ) {
$block->add_frame_to_line($this->_frame);
}
}
//........................................................................
// Returns an array(0 => min, 1 => max, "min" => min, "max" => max) of the
// minimum and maximum widths of this frame
function get_min_max_width() {
$style = $this->_frame->get_style();
$this->_block_parent = $this->_frame->find_block_parent();
$line_width = $this->_frame->get_containing_block("w");
$str = $text = $this->_frame->get_text();
$size = $style->font_size;
$font = $style->font_family;
$word_spacing = $style->length_in_pt($style->word_spacing);
$char_spacing = $style->length_in_pt($style->letter_spacing);
switch($style->white_space) {
default:
case "normal":
$str = preg_replace(self::$_whitespace_pattern," ", $str);
case "pre-wrap":
case "pre-line":
// Find the longest word (i.e. minimum length)
// This technique (using arrays & an anonymous function) is actually
// faster than doing a single-pass character by character scan. Heh,
// yes I took the time to bench it ;)
$words = array_flip(preg_split("/[\s-]+/u",$str, -1, PREG_SPLIT_DELIM_CAPTURE));
array_walk($words, create_function('&$val,$str',
'$val = Font_Metrics::get_text_width($str, "'.addslashes($font).'", '.$size.', '.$word_spacing.', '.$char_spacing.');'));
arsort($words);
$min = reset($words);
break;
case "pre":
$lines = array_flip(preg_split("/\n/u", $str));
array_walk($lines, create_function('&$val,$str',
'$val = Font_Metrics::get_text_width($str, "'.addslashes($font).'", '.$size.', '.$word_spacing.', '.$char_spacing.');'));
arsort($lines);
$min = reset($lines);
break;
case "nowrap":
$min = Font_Metrics::get_text_width($this->_collapse_white_space($str), $font, $size, $word_spacing, $char_spacing);
break;
}
switch ($style->white_space) {
default:
case "normal":
case "nowrap":
$str = preg_replace(self::$_whitespace_pattern," ", $text);
break;
case "pre-line":
//XXX: Is this correct?
$str = preg_replace( "/[ \t]+/u", " ", $text);
case "pre-wrap":
// Find the longest word (i.e. minimum length)
$lines = array_flip(preg_split("/\n/", $text));
array_walk($lines, create_function('&$val,$str',
'$val = Font_Metrics::get_text_width($str, "'.$font.'", '.$size.', '.$word_spacing.', '.$char_spacing.');'));
arsort($lines);
reset($lines);
$str = key($lines);
break;
}
$max = Font_Metrics::get_text_width($str, $font, $size, $word_spacing, $char_spacing);
$delta = $style->length_in_pt(array($style->margin_left,
$style->border_left_width,
$style->padding_left,
$style->padding_right,
$style->border_right_width,
$style->margin_right), $line_width);
$min += $delta;
$max += $delta;
return array($min, $max, "min" => $min, "max" => $max);
}
}

182
pdf/include/text_renderer.cls.php Executable file
View File

@@ -0,0 +1,182 @@
<?php
/**
* DOMPDF - PHP5 HTML to PDF renderer
*
* File: $RCSfile: text_renderer.cls.php,v $
* Created on: 2004-06-01
*
* Copyright (c) 2004 - Benj Carson <benjcarson@digitaljunkies.ca>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library in the file LICENSE.LGPL; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*
* Alternatively, you may distribute this software under the terms of the
* PHP License, version 3.0 or later. A copy of this license should have
* been distributed with this file in the file LICENSE.PHP . If this is not
* the case, you can obtain a copy at http://www.php.net/license/3_0.txt.
*
* The latest version of DOMPDF might be available at:
* http://www.dompdf.com/
*
* @link http://www.dompdf.com/
* @copyright 2004 Benj Carson
* @author Benj Carson <benjcarson@digitaljunkies.ca>
* @contributor Helmut Tischer <htischer@weihenstephan.org>
* @package dompdf
*
* Changes
* @contributor Helmut Tischer <htischer@weihenstephan.org>
* @version dompdf_trunk_with_helmut_mods.20090528
* - fix text decoration positions according to font metrics
* @version 20090610
* - better accuracy on using different renderer as cpdf, added comments
*/
/* $Id: text_renderer.cls.php 357 2011-01-30 20:56:46Z fabien.menager $ */
/**
* Renders text frames
*
* @access private
* @package dompdf
*/
class Text_Renderer extends Abstract_Renderer {
const DECO_THICKNESS = 0.02; // Thickness of underline. Screen: 0.08, print: better less, e.g. 0.04
//Tweaking if $base and $descent are not accurate.
//Check method_exists( $this->_canvas, "get_cpdf" )
//- For cpdf these can and must stay 0, because font metrics are used directly.
//- For other renderers, if different values are wanted, separate the parameter sets.
// But $size and $size-$height seem to be accurate enough
const UNDERLINE_OFFSET = 0.0; // Relative to bottom of text, as fraction of height.
const OVERLINE_OFFSET = 0.0; // Relative to top of text
const LINETHROUGH_OFFSET = 0.0; // Relative to centre of text.
const DECO_EXTENSION = 0.0; // How far to extend lines past either end, in pt
//........................................................................
function render(Frame $frame) {
$text = $frame->get_text();
if ( trim($text) === "" )
return;
$style = $frame->get_style();
list($x, $y) = $frame->get_position();
$cb = $frame->get_containing_block();
if ( ($ml = $style->margin_left) === "auto" || $ml === "none" )
$ml = 0;
if ( ($pl = $style->padding_left) === "auto" || $pl === "none" )
$pl = 0;
if ( ($bl = $style->border_left_width) === "auto" || $bl === "none" )
$bl = 0;
$x += $style->length_in_pt( array($ml, $pl, $bl), $cb["w"] );
$font = $style->font_family;
$size = $frame_font_size = $style->font_size;
$height = $style->height;
$word_spacing = $frame->get_text_spacing() + $style->length_in_pt($style->word_spacing);
$char_spacing = $style->length_in_pt($style->letter_spacing);
$width = $style->width;
/*$text = str_replace(
array("{PAGE_NUM}"),
array($this->_canvas->get_page_number()),
$text
);*/
$this->_canvas->text($x, $y, $text,
$font, $size,
$style->color, $word_spacing, $char_spacing);
$line = $frame->get_containing_line();
// FIXME Instead of using the tallest frame to position,
// the decoration, the text should be well placed
if ( false && $line["tallest_frame"] ) {
$base_frame = $line["tallest_frame"];
$style = $base_frame->get_style();
$size = $style->font_size;
$height = $line["h"] * ($size / $style->line_height);
}
if ( method_exists( $this->_canvas, "get_cpdf" ) ) {
$cpdf = $this->_canvas->get_cpdf();
$fontBBox = $cpdf->fonts[$style->font_family]['FontBBox'];
$base = (($fontBBox[3]*$size)/1000) * 0.90;
$descent = ($fontBBox[1]*$size)/1000;
//print '<pre>Text_Renderer cpdf:'.$base.' '.$descent.' '.$size.'</pre>';
} else {
//Descent is font part below baseline, typically negative. $height is about full height of font box.
//$descent = -$size/6; is less accurate, depends on font family.
// @todo Could we get font info for PDFlib adapter and others ?
$base = $size*1.08;
$descent = $size-$height;
//print '<pre>Text_Renderer other than cpdf:'.$base.' '.$descent.' '.$size.'</pre>';
}
// Handle text decoration:
// http://www.w3.org/TR/CSS21/text.html#propdef-text-decoration
// Draw all applicable text-decorations. Start with the root and work our way down.
$p = $frame;
$stack = array();
while ( $p = $p->get_parent() )
$stack[] = $p;
while ( count($stack) > 0 ) {
$f = array_pop($stack);
if ( ($text_deco = $f->get_style()->text_decoration) === "none" )
continue;
$deco_y = $y; //$line["y"];
$color = $f->get_style()->color;
switch ($text_deco) {
default:
continue;
case "underline":
$deco_y += $base - $descent + $size * (self::UNDERLINE_OFFSET - self::DECO_THICKNESS/2);
break;
case "overline":
$deco_y += $size * (self::OVERLINE_OFFSET + self::DECO_THICKNESS/2);
break;
case "line-through":
$deco_y += $base * 0.7 + $size * self::LINETHROUGH_OFFSET;
break;
}
$dx = 0;
$x1 = $x - self::DECO_EXTENSION;
$x2 = $x + $width + $dx + self::DECO_EXTENSION;
$this->_canvas->line($x1, $deco_y, $x2, $deco_y, $color, $size * self::DECO_THICKNESS);
}
if (DEBUG_LAYOUT && DEBUG_LAYOUT_LINES) {
$text_width = Font_Metrics::get_text_width($text, $font, $frame_font_size);
$this->_debug_layout(array($x, $y, $text_width+($line["wc"]-1)*$word_spacing, $frame_font_size), "orange", array(0.5, 0.5));
}
}
}

306
pdf/include/ttf_info.cls.php Executable file
View File

@@ -0,0 +1,306 @@
<?php
/**
* ttfInfo class
* Retrieve data stored in a TTF files 'name' table
*
* @original author Unknown
* found at http://www.phpclasses.org/browse/package/2144.html
*
* @ported for used on http://www.nufont.com
* @author Jason Arencibia
* @version 0.2
* @copyright (c) 2006 GrayTap Media
* @website http://www.graytap.com
* @license GPL 2.0
* @access public
*
* @todo: Make it Retrieve additional information from other tables
*
*/
class TTF_Info {
/**
* variable $_dirRestriction
* Restrict the resource pointer to this directory and above.
* Change to 1 for to allow the class to look outside of it current directory
* @protected
* @var int
*/
protected $_dirRestriction = true;
/**
* variable $_dirRestriction
* Restrict the resource pointer to this directory and above.
* Change to 1 for nested directories
* @protected
* @var int
*/
protected $_recursive = true;
/**
* variable $fontsdir
* This is to declare this variable as protected
* don't edit this!!!
* @protected
*/
protected $fontsdir;
/**
* variable $filename
* This is to declare this varable as protected
* don't edit this!!!
* @protected
*/
protected $filename;
/**
* function setFontFile()
* set the filename
* @public
* @param string $data the new value
* @return object reference to this
*/
public function setFontFile($data)
{
if ($this->_dirRestriction && preg_match('[\.\/|\.\.\/]', $data))
{
$this->exitClass('Error: Directory restriction is enforced!');
}
$this->filename = $data;
return $this;
} // public function setFontFile
/**
* function setFontsDir()
* set the Font Directory
* @public
* @param string $data the new value
* @return object referrence to this
*/
public function setFontsDir($data)
{
if ($this->_dirRestriction && preg_match('[\.\/|\.\.\/]', $data))
{
$this->exitClass('Error: Directory restriction is enforced!');
}
$this->fontsdir = $data;
return $this;
} // public function setFontsDir
/**
* function readFontsDir()
* @public
* @return information contained in the TTF 'name' table of all fonts in a directory.
*/
public function readFontsDir()
{
if (empty($this->fontsdir)) { $this->exitClass('Error: Fonts Directory has not been set with setFontsDir().'); }
if (empty($this->backupDir)){ $this->backupDir = $this->fontsdir; }
//$this->array = array();
$d = dir($this->fontsdir);
while (false !== ($e = $d->read()))
{
if($e != '.' && $e != '..')
{
$e = $this->fontsdir . $e;
if($this->_recursive && is_dir($e))
{
$this->setFontsDir($e);
$this->readFontsDir();
}
else if ($this->is_ttf($e) === true)
{
$this->setFontFile($e);
$this->array[] = $this->getFontInfo();
}
}
}
if (!empty($this->backupDir)){ $this->fontsdir = $this->backupDir; }
$d->close();
return $this;
} // public function readFontsDir
/**
* function setProtectedVar()
* @public
* @param string $var the new variable
* @param string $data the new value
* @return object reference to this
* DISABLED, NO REAL USE YET
public function setProtectedVar($var, $data)
{
if ($var == 'filename')
{
$this->setFontFile($data);
} else {
//if (isset($var) && !empty($data))
$this->$var = $data;
}
return $this;
}
*/
/**
* function getFontInfo()
* @public
* @return information contained in the TTF 'name' table.
*/
public function getFontInfo()
{
$fd = fopen ($this->filename, "r");
$this->text = fread ($fd, filesize($this->filename));
fclose ($fd);
$number_of_tables = hexdec($this->dec2ord($this->text[4]).$this->dec2ord($this->text[5]));
for ($i=0;$i<$number_of_tables;$i++)
{
$tag = $this->text[12+$i*16].$this->text[12+$i*16+1].$this->text[12+$i*16+2].$this->text[12+$i*16+3];
if ($tag == 'name')
{
$this->ntOffset = hexdec(
$this->dec2ord($this->text[12+$i*16+8]).$this->dec2ord($this->text[12+$i*16+8+1]).
$this->dec2ord($this->text[12+$i*16+8+2]).$this->dec2ord($this->text[12+$i*16+8+3]));
$offset_storage_dec = hexdec($this->dec2ord($this->text[$this->ntOffset+4]).$this->dec2ord($this->text[$this->ntOffset+5]));
$number_name_records_dec = hexdec($this->dec2ord($this->text[$this->ntOffset+2]).$this->dec2ord($this->text[$this->ntOffset+3]));
}
}
$storage_dec = $offset_storage_dec + $this->ntOffset;
$storage_hex = strtoupper(dechex($storage_dec));
for ($j=0;$j<$number_name_records_dec;$j++)
{
$platform_id_dec = hexdec($this->dec2ord($this->text[$this->ntOffset+6+$j*12+0]).$this->dec2ord($this->text[$this->ntOffset+6+$j*12+1]));
$name_id_dec = hexdec($this->dec2ord($this->text[$this->ntOffset+6+$j*12+6]).$this->dec2ord($this->text[$this->ntOffset+6+$j*12+7]));
$string_length_dec = hexdec($this->dec2ord($this->text[$this->ntOffset+6+$j*12+8]).$this->dec2ord($this->text[$this->ntOffset+6+$j*12+9]));
$string_offset_dec = hexdec($this->dec2ord($this->text[$this->ntOffset+6+$j*12+10]).$this->dec2ord($this->text[$this->ntOffset+6+$j*12+11]));
if (!empty($name_id_dec) and empty($font_tags[$name_id_dec]))
{
for($l=0;$l<$string_length_dec;$l++)
{
if (ord($this->text[$storage_dec+$string_offset_dec+$l]) == '0') { continue; }
else { $font_tags[$name_id_dec] .= ($this->text[$storage_dec+$string_offset_dec+$l]); }
}
}
}
return $font_tags;
} // public function getFontInfo
/**
* function getCopyright()
* @public
* @return 'Copyright notice' contained in the TTF 'name' table at index 0
*/
public function getCopyright()
{
$this->info = $this->getFontInfo();
return $this->info[0];
} // public function getCopyright
/**
* function getFontFamily()
* @public
* @return 'Font Family name' contained in the TTF 'name' table at index 1
*/
public function getFontFamily()
{
$this->info = $this->getFontInfo();
return $this->info[1];
} // public function getFontFamily
/**
* function getFontSubFamily()
* @public
* @return 'Font Subfamily name' contained in the TTF 'name' table at index 2
*/
public function getFontSubFamily()
{
$this->info = $this->getFontInfo();
return $this->info[2];
} // public function getFontSubFamily
/**
* function getFontId()
* @public
* @return 'Unique font identifier' contained in the TTF 'name' table at index 3
*/
public function getFontId()
{
$this->info = $this->getFontInfo();
return $this->info[3];
} // public function getFontId
/**
* function getFullFontName()
* @public
* @return 'Full font name' contained in the TTF 'name' table at index 4
*/
public function getFullFontName()
{
$this->info = $this->getFontInfo();
return $this->info[4];
} // public function getFullFontName
/**
* function dec2ord()
* Used to lessen redundant calls to multiple functions.
* @protected
* @return object
*/
protected function dec2ord($dec)
{
return $this->dec2hex(ord($dec));
} // protected function dec2ord
/**
* function dec2hex()
* private function to perform Hexadecimal to decimal with proper padding.
* @protected
* @return object
*/
protected function dec2hex($dec)
{
return str_repeat('0', 2-strlen(($hex=strtoupper(dechex($dec))))) . $hex;
} // protected function dec2hex
/**
* function dec2hex()
* private function to perform Hexadecimal to decimal with proper padding.
* @protected
* @return object
*/
protected function exitClass($message)
{
echo $message;
exit;
} // protected function dec2hex
/**
* function dec2hex()
* private helper function to test in the file in question is a ttf.
* @protected
* @return object
*/
protected function is_ttf($file)
{
$ext = explode('.', $file);
$ext = $ext[count($ext)-1];
return preg_match("/ttf$/i",$ext) ? true : false;
} // protected function is_ttf
} // class ttfInfo
function getFontInfo($resource)
{
$ttfInfo = new TTF_Info;
$ttfInfo->setFontFile($resource);
return $ttfInfo->getFontInfo();
}
?>