diff --git a/composer.json b/composer.json index 01135e5ec..ad6481850 100644 --- a/composer.json +++ b/composer.json @@ -26,7 +26,6 @@ }, "require": { "php": ">=5.4.0", - "ext-gd": "*", "ext-dom": "*", "ext-mbstring": "*", "phenx/php-font-lib": "0.5.*", @@ -36,6 +35,11 @@ "phpunit/phpunit": "^4.8|^5.5|^6.5", "squizlabs/php_codesniffer": "2.*" }, + "suggest": { + "ext-gd": "Needed to process images", + "ext-imagick": "Improves image processing performance", + "ext-gmagick": "Improves image processing performance" + }, "extra": { "branch-alias": { "dev-develop": "0.7-dev" diff --git a/lib/Cpdf.php b/lib/Cpdf.php index d47a4b27e..7748d4f15 100644 --- a/lib/Cpdf.php +++ b/lib/Cpdf.php @@ -464,28 +464,28 @@ protected function o_viewerPreferences($id, $action, $options = '') // Named with limited valid values case 'NonFullScreenPageMode': if (!in_array($v, array('UseNone', 'UseOutlines', 'UseThumbs', 'UseOC'))) { - continue; + break; } $o['info'][$k] = $v; break; case 'Direction': if (!in_array($v, array('L2R', 'R2L'))) { - continue; + break; } $o['info'][$k] = $v; break; case 'PrintScaling': if (!in_array($v, array('None', 'AppDefault'))) { - continue; + break; } $o['info'][$k] = $v; break; case 'Duplex': if (!in_array($v, array('None', 'AppDefault'))) { - continue; + break; } $o['info'][$k] = $v; break; diff --git a/lib/res/broken_image.svg b/lib/res/broken_image.svg new file mode 100644 index 000000000..09b97e6c3 --- /dev/null +++ b/lib/res/broken_image.svg @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/phpunit.xml.dist b/phpunit.xml.dist index d39712097..687755066 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -6,11 +6,10 @@ convertNoticesToExceptions="true" convertWarningsToExceptions="true" processIsolation="false" - stopOnFailure="false" - syntaxCheck="false"> + stopOnFailure="false"> ./tests/Dompdf/ - \ No newline at end of file + diff --git a/src/Adapter/CPDF.php b/src/Adapter/CPDF.php index 889eb4da2..d976627b8 100644 --- a/src/Adapter/CPDF.php +++ b/src/Adapter/CPDF.php @@ -152,7 +152,7 @@ class CPDF implements Canvas private $_page_text; /** - * Array of pages for accesing after rendering is initially complete + * Array of pages for accessing after rendering is initially complete * * @var array */ @@ -275,7 +275,7 @@ public function add_info($label, $value) /** * Opens a new 'object' * - * While an object is open, all drawing actions are recored in the object, + * While an object is open, all drawing actions are recorded 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. * diff --git a/src/Adapter/PDFLib.php b/src/Adapter/PDFLib.php index b07e91b1f..e999c580e 100644 --- a/src/Adapter/PDFLib.php +++ b/src/Adapter/PDFLib.php @@ -49,14 +49,46 @@ class PDFLib implements Canvas static $IN_MEMORY = true; /** - * @var Dompdf + * Saves the major version of PDFLib for compatibility requests + * + * @var null|int + */ + static private $MAJOR_VERSION = null; + + + /** + * Transforms the list of native fonts into PDFLib compatible names (casesensitive) + * + * @var array + */ + static public $nativeFontsTpPDFLib = array( + "courier" => "Courier", + "courier-bold" => "Courier-Bold", + "courier-oblique" => "Courier-Oblique", + "courier-boldoblique" => "Courier-BoldOblique", + "helvetica" => "Helvetica", + "helvetica-bold" => "Helvetica-Bold", + "helvetica-oblique" => "Helvetica-Oblique", + "helvetica-boldoblique" => "Helvetica-BoldOblique", + "times" => "Times-Roman", + "times-roman" => "Times-Roman", + "times-bold" => "Times-Bold", + "times-italic" => "Times-Italic", + "times-bolditalic" => "Times-BoldItalic", + "symbol" => "Symbol", + "zapfdinbats" => "ZapfDingbats", + "zapfdingbats" => "ZapfDingbats", + ); + + /** + * @var \Dompdf\Dompdf */ private $_dompdf; /** * Instance of PDFLib class * - * @var \PDFlib + * @var \PDFLib */ private $_pdf; @@ -116,6 +148,13 @@ class PDFLib implements Canvas */ private $_fonts; + /** + * Cache of fontFile checks + * + * @var array + */ + private $_fontsFiles; + /** * List of objects (templates) to add to multiple pages * @@ -161,7 +200,7 @@ class PDFLib implements Canvas /** * Class constructor * - * @param mixed $paper The size of paper to use either a string (see {@link Dompdf\Adapter\CPDF::$PAPER_SIZES}) or + * @param string|array $paper The size of paper to use either a string (see {@link Dompdf\Adapter\CPDF::$PAPER_SIZES}) or * an array(xmin,ymin,xmax,ymax) * @param string $orientation The orientation of the document (either 'landscape' or 'portrait') * @param Dompdf $dompdf @@ -170,7 +209,7 @@ public function __construct($paper = "letter", $orientation = "portrait", Dompdf { if (is_array($paper)) { $size = $paper; - } else if (isset(self::$PAPER_SIZES[mb_strtolower($paper)])) { + } elseif (isset(self::$PAPER_SIZES[mb_strtolower($paper)])) { $size = self::$PAPER_SIZES[mb_strtolower($paper)]; } else { $size = self::$PAPER_SIZES["letter"]; @@ -189,14 +228,25 @@ public function __construct($paper = "letter", $orientation = "portrait", Dompdf $license = $dompdf->getOptions()->getPdflibLicense(); if (strlen($license) > 0) { - $this->_pdf->set_parameter("license", $license); + $this->setPDFLibParameter("license", $license); + } + + $this->setPDFLibParameter("textformat", "utf8"); + if ($this->getPDFLibMajorVersion() >= 7) { + $this->setPDFLibParameter("errorpolicy", "return"); + // $this->_pdf->set_option('logging={filename=' . \APP_PATH . '/logs/pdflib.log classes={api=1 warning=2}}'); + // $this->_pdf->set_option('errorpolicy=exception'); + } else { + $this->setPDFLibParameter("fontwarning", "false"); } - $this->_pdf->set_parameter("textformat", "utf8"); - $this->_pdf->set_parameter("fontwarning", "false"); + $searchPath = $this->_dompdf->getOptions()->getFontDir(); + if (empty($searchPath) === false) { + $this->_pdf->set_option('searchpath={' . $searchPath . '}'); + } - // TODO: fetch PDFLib version information for the producer field - $this->_pdf->set_info("Producer Addendum", sprintf("%s + PDFLib", $dompdf->version)); + // fetch PDFLib version information for the producer field + $this->_pdf->set_info("Producer Addendum", sprintf("%s + PDFLib %s", $dompdf->version, $this->getPDFLibMajorVersion())); // Silence pedantic warnings about missing TZ settings $tz = @date_default_timezone_get(); @@ -291,6 +341,7 @@ public function open_object() $ret = $this->_pdf->begin_template($this->_width, $this->_height); $this->_pdf->save(); $this->_objs[$ret] = array("start_page" => $this->_page_number); + return $ret; } @@ -332,7 +383,7 @@ public function close_object() * - 'nextodd' add to all odd numbered pages from the next one * - 'nexteven' add to all even numbered pages from the next one * - * @param int $object the object handle returned by open_object() + * @param int $object the object handle returned by open_object() * @param string $where */ public function add_object($object, $where = 'all') @@ -453,10 +504,10 @@ public function set_page_count($count) /** * Sets the line style * - * @param float $width + * @param float $width * @param $cap * @param string $join - * @param array $dash + * @param array $dash * * @return void */ @@ -466,23 +517,43 @@ protected function _set_line_style($width, $cap, $join, $dash) $dash[] = $dash[0]; } - if (count($dash) > 1) { - $this->_pdf->setdashpattern("dasharray={" . implode(" ", $dash) . "}"); + if ($this->getPDFLibMajorVersion() >= 9) { + if (count($dash) > 1) { + $this->_pdf->set_graphics_option("dasharray={" . implode(" ", $dash) . "}"); + } else { + $this->_pdf->set_graphics_option("dasharray=none"); + } } else { - $this->_pdf->setdash(0, 0); + if (count($dash) > 1) { + $this->_pdf->setdashpattern("dasharray={" . implode(" ", $dash) . "}"); + } else { + $this->_pdf->setdash(0, 0); + } } switch ($join) { case "miter": - $this->_pdf->setlinejoin(0); + if ($this->getPDFLibMajorVersion() >= 9) { + $this->_pdf->set_graphics_option('linejoin=0'); + } else { + $this->_pdf->setlinejoin(0); + } break; case "round": - $this->_pdf->setlinejoin(1); + if ($this->getPDFLibMajorVersion() >= 9) { + $this->_pdf->set_graphics_option('linejoin=1'); + } else { + $this->_pdf->setlinejoin(1); + } break; case "bevel": - $this->_pdf->setlinejoin(2); + if ($this->getPDFLibMajorVersion() >= 9) { + $this->_pdf->set_graphics_option('linejoin=2'); + } else { + $this->_pdf->setlinejoin(2); + } break; default: @@ -491,15 +562,27 @@ protected function _set_line_style($width, $cap, $join, $dash) switch ($cap) { case "butt": - $this->_pdf->setlinecap(0); + if ($this->getPDFLibMajorVersion() >= 9) { + $this->_pdf->set_graphics_option('linecap=0'); + } else { + $this->_pdf->setlinecap(0); + } break; case "round": - $this->_pdf->setlinecap(1); + if ($this->getPDFLibMajorVersion() >= 9) { + $this->_pdf->set_graphics_option('linecap=1'); + } else { + $this->_pdf->setlinecap(1); + } break; case "square": - $this->_pdf->setlinecap(2); + if ($this->getPDFLibMajorVersion() >= 9) { + $this->_pdf->set_graphics_option('linecap=2'); + } else { + $this->_pdf->setlinecap(2); + } break; default: @@ -583,7 +666,7 @@ protected function _set_fill_color($color) */ public function _set_fill_opacity($opacity, $mode = "Normal") { - if ($mode === "Normal") { + if ($mode === "Normal" && is_null($opacity) === false) { $this->_set_gstate("opacityfill=$opacity"); } } @@ -596,7 +679,7 @@ public function _set_fill_opacity($opacity, $mode = "Normal") */ public function _set_stroke_opacity($opacity, $mode = "Normal") { - if ($mode === "Normal") { + if ($mode === "Normal" && is_null($opacity) === false) { $this->_set_gstate("opacitystroke=$opacity"); } } @@ -609,7 +692,7 @@ public function _set_stroke_opacity($opacity, $mode = "Normal") */ public function set_opacity($opacity, $mode = "Normal") { - if ($mode === "Normal") { + if ($mode === "Normal" && is_null($opacity) === false) { $this->_set_gstate("opacityfill=$opacity opacitystroke=$opacity"); $this->_current_opacity = $opacity; } @@ -627,6 +710,7 @@ public function _set_gstate($gstate_options) $gstate = $this->_pdf->create_gstate($gstate_options); $this->_gstates[$gstate] = $gstate_options; } + return $this->_pdf->set_gstate($gstate); } @@ -644,7 +728,7 @@ public function set_default_view($view, $options = array()) * fitwindow Fit the complete page to the window. * fixed */ - //$this->_pdf->set_parameter("openaction", $view); + //$this->setPDFLibParameter("openaction", $view); } /** @@ -658,28 +742,69 @@ public function set_default_view($view, $options = array()) */ protected function _load_font($font, $encoding = null, $options = "") { - // Set up font paths - if ($this->_pdf->get_parameter("FontOutline", 1) === "") { + // Fix for PDFLibs case-sensitive font names + $baseFont = basename($font); + $isNativeFont = false; + if (isset(self::$nativeFontsTpPDFLib[$baseFont])) { + $font = self::$nativeFontsTpPDFLib[$baseFont]; + $isNativeFont = true; + } + + // Check if the font is a native PDF font + // Embed non-native fonts + $test = strtolower($baseFont); + if (in_array($test, DOMPDF::$nativeFonts)) { + $font = basename($font); + } else { + // Embed non-native fonts + $options .= " embedding=true"; + } + + if (is_null($encoding)) { + // Unicode encoding is only available for the commerical + // version of PDFlib and not PDFlib-Lite + if (strlen($this->_dompdf->getOptions()->getPdflibLicense()) > 0) { + $encoding = "unicode"; + } else { + $encoding = "auto"; + } + } + + $key = "$font:$encoding:$options"; + if (isset($this->_fonts[$key])) { + return $this->_fonts[$key]; + } + + // Native fonts are build in, just load it + if ($isNativeFont) { + $this->_fonts[$key] = $this->_pdf->load_font($font, $encoding, $options); + + return $this->_fonts[$key]; + } + + $fontOutline = $this->getPDFLibParameter("FontOutline", 1); + if ($fontOutline === "" || $fontOutline <= 0) { $families = $this->_dompdf->getFontMetrics()->getFontFamilies(); foreach ($families as $files) { foreach ($files as $file) { $face = basename($file); $afm = null; + if (isset($this->_fontsFiles[$face])) { + continue; + } + // Prefer ttfs to afms if (file_exists("$file.ttf")) { $outline = "$file.ttf"; - - } else if (file_exists("$file.TTF")) { + } elseif (file_exists("$file.TTF")) { $outline = "$file.TTF"; - - } else if (file_exists("$file.pfb")) { + } elseif (file_exists("$file.pfb")) { $outline = "$file.pfb"; if (file_exists("$file.afm")) { $afm = "$file.afm"; } - - } else if (file_exists("$file.PFB")) { + } elseif (file_exists("$file.PFB")) { $outline = "$file.PFB"; if (file_exists("$file.AFM")) { $afm = "$file.AFM"; @@ -688,43 +813,29 @@ protected function _load_font($font, $encoding = null, $options = "") continue; } - $this->_pdf->set_parameter("FontOutline", "\{$face\}=\{$outline\}"); + $this->_fontsFiles[$face] = true; - if (!is_null($afm)) { - $this->_pdf->set_parameter("FontAFM", "\{$face\}=\{$afm\}"); + if ($this->getPDFLibMajorVersion() >= 9) { + $this->setPDFLibParameter("FontOutline", '{' . "$face=$outline" . '}'); + } else { + $this->setPDFLibParameter("FontOutline", "\{$face\}=\{$outline\}"); } - } - } - } - - // Check if the font is a native PDF font - // Embed non-native fonts - $test = strtolower(basename($font)); - if (in_array($test, DOMPDF::$nativeFonts)) { - $font = basename($font); - } else { - // Embed non-native fonts - $options .= " embedding=true"; - } - if (is_null($encoding)) { - // Unicode encoding is only available for the commerical - // version of PDFlib and not PDFlib-Lite - if (strlen($this->_dompdf->getOptions()->getPdflibLicense()) > 0) { - $encoding = "unicode"; - } else { - $encoding = "auto"; + if (is_null($afm)) { + continue; + } + if ($this->getPDFLibMajorVersion() >= 9) { + $this->setPDFLibParameter("FontAFM", '{' . "$face=$afm" . '}'); + } else { + $this->setPDFLibParameter("FontAFM", "\{$face\}=\{$afm\}"); + } + } } } - $key = "$font:$encoding:$options"; + $this->_fonts[$key] = $this->_pdf->load_font($font, $encoding, $options); - if (isset($this->_fonts[$key])) { - return $this->_fonts[$key]; - } else { - $this->_fonts[$key] = $this->_pdf->load_font($font, $encoding, $options); - return $this->_fonts[$key]; - } + return $this->_fonts[$key]; } /** @@ -759,7 +870,7 @@ public function line($x1, $y1, $x2, $y2, $color, $width, $style = null) $this->_pdf->lineto($x2, $y2); $this->_pdf->stroke(); - $this->_set_line_transparency("Normal", $this->_current_opacity); + $this->_set_stroke_opacity($this->_current_opacity, "Normal"); } /** @@ -802,7 +913,7 @@ public function arc($x1, $y1, $r1, $r2, $astart, $aend, $color, $width, $style = $this->_pdf->arc($x1, $y1, $r1, $astart, $aend); $this->_pdf->stroke(); - $this->_set_line_transparency("Normal", $this->_current_opacity); + $this->_set_stroke_opacity($this->_current_opacity, "Normal"); } /** @@ -812,7 +923,7 @@ public function arc($x1, $y1, $r1, $r2, $astart, $aend, $color, $width, $style = * @param float $h * @param array $color * @param float $width - * @param null $style + * @param null $style */ public function rectangle($x1, $y1, $w, $h, $color, $width, $style = null) { @@ -824,7 +935,7 @@ public function rectangle($x1, $y1, $w, $h, $color, $width, $style = null) $this->_pdf->rect($x1, $y1, $w, $h); $this->_pdf->stroke(); - $this->_set_line_transparency("Normal", $this->_current_opacity); + $this->_set_stroke_opacity($this->_current_opacity, "Normal"); } /** @@ -843,7 +954,7 @@ public function filled_rectangle($x1, $y1, $w, $h, $color) $this->_pdf->rect(floatval($x1), floatval($y1), floatval($w), floatval($h)); $this->_pdf->fill(); - $this->_set_fill_transparency("Normal", $this->_current_opacity); + $this->_set_fill_opacity($this->_current_opacity, "Normal"); } /** @@ -874,7 +985,6 @@ public function clipping_rectangle($x1, $y1, $w, $h) */ public function clipping_roundrectangle($x1, $y1, $w, $h, $rTL, $rTR, $rBR, $rBL) { - // @todo $this->clipping_rectangle($x1, $y1, $w, $h); } @@ -965,9 +1075,9 @@ public function transform($a, $b, $c, $d, $e, $f) /** * @param array $points * @param array $color - * @param null $width - * @param null $style - * @param bool $fill + * @param null $width + * @param null $style + * @param bool $fill */ public function polygon($points, $color, $width = null, $style = null, $fill = false) { @@ -994,8 +1104,8 @@ public function polygon($points, $color, $width = null, $style = null, $fill = f $this->_pdf->closepath_stroke(); } - $this->_set_fill_transparency("Normal", $this->_current_opacity); - $this->_set_line_transparency("Normal", $this->_current_opacity); + $this->_set_fill_opacity($this->_current_opacity, "Normal"); + $this->_set_stroke_opacity($this->_current_opacity, "Normal"); } /** @@ -1003,9 +1113,9 @@ public function polygon($points, $color, $width = null, $style = null, $fill = f * @param float $y * @param float $r * @param array $color - * @param null $width - * @param null $style - * @param bool $fill + * @param null $width + * @param null $style + * @param bool $fill */ public function circle($x, $y, $r, $color, $width = null, $style = null, $fill = false) { @@ -1026,16 +1136,16 @@ public function circle($x, $y, $r, $color, $width = null, $style = null, $fill = $this->_pdf->stroke(); } - $this->_set_fill_transparency("Normal", $this->_current_opacity); - $this->_set_line_transparency("Normal", $this->_current_opacity); + $this->_set_fill_opacity($this->_current_opacity, "Normal"); + $this->_set_stroke_opacity($this->_current_opacity, "Normal"); } /** * @param string $img_url - * @param float $x - * @param float $y - * @param int $w - * @param int $h + * @param float $x + * @param float $y + * @param int $w + * @param int $h * @param string $resolution */ public function image($img_url, $x, $y, $w, $h, $resolution = "normal") @@ -1056,15 +1166,15 @@ public function image($img_url, $x, $y, $w, $h, $resolution = "normal") } /** - * @param float $x - * @param float $y + * @param float $x + * @param float $y * @param string $text * @param string $font - * @param float $size - * @param array $color - * @param int $word_spacing - * @param int $char_spacing - * @param int $angle + * @param float $size + * @param array $color + * @param int $word_spacing + * @param int $char_spacing + * @param int $angle */ public function text($x, $y, $text, $font, $size, $color = array(0, 0, 0), $word_spacing = 0, $char_spacing = 0, $angle = 0) { @@ -1081,7 +1191,7 @@ public function text($x, $y, $text, $font, $size, $color = array(0, 0, 0), $word $this->_pdf->fit_textline($text, $x, $y, "rotate=$angle wordspacing=$word_spacing charspacing=$char_spacing "); - $this->_set_fill_transparency("Normal", $this->_current_opacity); + $this->_set_fill_opacity($this->_current_opacity, "Normal"); } /** @@ -1107,11 +1217,11 @@ public 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 + * @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 */ public function add_link($url, $x, $y, $width, $height) { @@ -1140,9 +1250,9 @@ public function add_link($url, $x, $y, $width, $height) /** * @param string $text * @param string $font - * @param float $size - * @param int $word_spacing - * @param int $letter_spacing + * @param float $size + * @param int $word_spacing + * @param int $letter_spacing * @return mixed */ public function get_text_width($text, $font, $size, $word_spacing = 0, $letter_spacing = 0) @@ -1163,7 +1273,7 @@ public function get_text_width($text, $font, $size, $word_spacing = 0, $letter_s /** * @param string $font - * @param float $size + * @param float $size * @return float */ public function get_font_height($font, $size) @@ -1177,17 +1287,19 @@ public function get_font_height($font, $size) // $desc is usually < 0, $ratio = $this->_dompdf->getOptions()->getFontHeightRatio(); + return $size * ($asc - $desc) * $ratio; } /** * @param string $font - * @param float $size + * @param float $size * @return float */ public function get_font_baseline($font, $size) { $ratio = $this->_dompdf->getOptions()->getFontHeightRatio(); + return $this->get_font_height($font, $size) / $ratio * 1.1; } @@ -1199,15 +1311,15 @@ public function get_font_baseline($font, $size) * * 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 $word_space word spacing adjustment - * @param float $char_space char spacing adjustment - * @param float $angle angle to write the text at, measured CW starting from the x-axis + * @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 char spacing adjustment + * @param float $angle angle to write the text at, measured CW starting from the x-axis */ public function page_text($x, $y, $text, $font, $size, $color = array(0, 0, 0), $word_space = 0.0, $char_space = 0.0, $angle = 0.0) { @@ -1296,7 +1408,7 @@ protected function _add_page_text() * Streams the PDF to the client. * * @param string $filename The filename to present to the client. - * @param array $options Associative array: 'compress' => 1 or 0 (default 1); 'Attachment' => 1 or 0 (default 1). + * @param array $options Associative array: 'compress' => 1 or 0 (default 1); 'Attachment' => 1 or 0 (default 1). * @throws Exception */ public function stream($filename = "document.pdf", $options = array()) @@ -1305,15 +1417,19 @@ public function stream($filename = "document.pdf", $options = array()) die("Unable to stream pdf: headers already sent"); } - if (!isset($options["compress"])) $options["compress"] = true; - if (!isset($options["Attachment"])) $options["Attachment"] = true; + if (!isset($options["compress"])) { + $options["compress"] = true; + } + if (!isset($options["Attachment"])) { + $options["Attachment"] = true; + } $this->_add_page_text(); if ($options["compress"]) { - $this->_pdf->set_value("compress", 6); + $this->setPDFLibValue("compress", 6); } else { - $this->_pdf->set_value("compress", 0); + $this->setPDFLibValue("compress", 0); } $this->_close(); @@ -1372,14 +1488,16 @@ public function stream($filename = "document.pdf", $options = array()) */ public function output($options = array()) { - if (!isset($options["compress"])) $options["compress"] = true; + if (!isset($options["compress"])) { + $options["compress"] = true; + } $this->_add_page_text(); if ($options["compress"]) { - $this->_pdf->set_value("compress", 6); + $this->setPDFLibValue("compress", 6); } else { - $this->_pdf->set_value("compress", 0); + $this->setPDFLibValue("compress", 0); } $this->_close(); @@ -1402,6 +1520,78 @@ public function output($options = array()) return $data; } + + /** + * @param string $keyword + * @param string $optlist + * @return mixed + */ + protected function getPDFLibParameter($keyword, $optlist = "") + { + if ($this->getPDFLibMajorVersion() >= 9) { + return $this->_pdf->get_option($keyword, ""); + } + + return $this->_pdf->get_parameter($keyword, $optlist); + } + + /** + * @param string $keyword + * @param string $value + * @return mixed + */ + protected function setPDFLibParameter($keyword, $value) + { + if ($this->getPDFLibMajorVersion() >= 9) { + return $this->_pdf->set_option($keyword . "=" . $value); + } + + return $this->_pdf->set_parameter($keyword, $value); + } + + /** + * @param string $keyword + * @param string $optlist + * @return mixed + */ + protected function getPDFLibValue($keyword, $optlist = "") + { + if ($this->getPDFLibMajorVersion() >= 9) { + return $this->getPDFLibParameter($keyword, $optlist); + } + + return $this->_pdf->get_value($keyword); + } + + /** + * @param string $keyword + * @param string $value + * @return mixed + */ + protected function setPDFLibValue($keyword, $value) + { + if ($this->getPDFLibMajorVersion() >= 9) { + return $this->setPDFLibParameter($keyword, $value); + } + + return $this->_pdf->set_value($keyword, $value); + } + + /** + * @return int + */ + private function getPDFLibMajorVersion() + { + if (is_null(self::$MAJOR_VERSION)) { + if (method_exists($this->_pdf, "get_option")) { + self::$MAJOR_VERSION = abs(intval($this->_pdf->get_option("major", ""))); + } else { + self::$MAJOR_VERSION = abs(intval($this->_pdf->get_value("major", ""))); + } + } + + return self::$MAJOR_VERSION; + } } // Workaround for idiotic limitation on statics... diff --git a/src/CanvasFactory.php b/src/CanvasFactory.php index d9d22bed0..b2bf1276a 100644 --- a/src/CanvasFactory.php +++ b/src/CanvasFactory.php @@ -46,7 +46,7 @@ class_exists("PDFLib", false) } else { - if ($backend === "gd") { + if ($backend === "gd" && extension_loaded('gd')) { $class = "Dompdf\\Adapter\\GD"; } else { $class = "Dompdf\\Adapter\\CPDF"; diff --git a/src/Css/Style.php b/src/Css/Style.php index 42327abbf..96ac5d42b 100644 --- a/src/Css/Style.php +++ b/src/Css/Style.php @@ -776,7 +776,7 @@ function important_get($prop) * For easier finding all assignments, attempted to allowing only explicite assignment: * Very many uses, e.g. AbstractFrameReflower.php -> for now leave as it is * function __set($prop, $val) { - * throw new Exception("Implicite replacement of assignment by __set. Not good."); + * throw new Exception("Implicit replacement of assignment by __set. Not good."); * } * function props_set($prop, $val) { ... } * @@ -1590,7 +1590,7 @@ function get_border_spacing() Only for combined attributes extra treatment needed. See below. div { border: 1px red; } - div { border: solid; } // Not combined! Only one occurence of same style per context + div { border: solid; } // Not combined! Only one occurrence of same style per context // div { border: 1px red; } div a { border: solid; } // Adding to border style ok by inheritance @@ -1608,13 +1608,13 @@ function get_border_spacing() At individual property like border-top-width need to check whether overriding value is also !important. Also store the !important condition for later overrides. Since not known who is initiating the override, need to get passed !important as parameter. - !important Paramter taken as in the original style in the css file. + !important Parameter taken as in the original style in the css file. When property border !important given, do not mark subsets like border_style as important. Only individual properties. Note: Setting individual property directly from css with e.g. set_border_top_style() is not needed, because - missing set funcions handled by a generic handler __set(), including the !important. + missing set functions handled by a generic handler __set(), including the !important. Setting individual property of as sub-property is handled below. Implementation see at _set_style_side_type() @@ -1969,7 +1969,7 @@ function set_font_size($size) * * Other than with border and list, existing partial attributes should * reset when starting here, even when not mentioned. - * If individual attribute is !important and explicite or implicite replacement is not, + * If individual attribute is !important and explicit or implicit replacement is not, * keep individual attribute * * require whitespace as delimiters for single value attributes @@ -1978,7 +1978,7 @@ function set_font_size($size) * font-style, font-variant, font-weight, font-size, line-height, font-family * * missing font-size and font-family might be not allowed, but accept it here and - * use default (medium size, enpty font name) + * use default (medium size, empty font name) * * @link http://www.w3.org/TR/CSS21/generate.html#propdef-list-style * @param $val @@ -2172,7 +2172,7 @@ protected function _set_border($side, $border_spec, $important) // FIXME: handle partial values - //For consistency of individal and combined properties, and with ie8 and firefox3 + //For consistency of individual and combined properties, and with ie8 and firefox3 //reset all attributes, even if only partially given $this->_set_style_side_type('border', $side, '_style', self::$_defaults['border_' . $side . '_style'], $important); $this->_set_style_side_type('border', $side, '_width', self::$_defaults['border_' . $side . '_width'], $important); @@ -2544,7 +2544,7 @@ function set_list_style($val) //On setting or merging or inheriting list_style_image as well as list_style_type, //and url exists, then url has precedence, otherwise fall back to list_style_type - //Firefox is wrong here (list_style_image gets overwritten on explicite list_style_type) + //Firefox is wrong here (list_style_image gets overwritten on explicit list_style_type) //Internet Explorer 7/8 and dompdf is right. if (mb_substr($value, 0, 3) === "url") { diff --git a/src/Css/Stylesheet.php b/src/Css/Stylesheet.php index 733d5b9b4..9d1a1ecee 100644 --- a/src/Css/Stylesheet.php +++ b/src/Css/Stylesheet.php @@ -172,8 +172,12 @@ function __construct(Dompdf $dompdf) $this->setFontMetrics($dompdf->getFontMetrics()); $this->_styles = array(); $this->_loaded_files = array(); - list($this->_protocol, $this->_base_host, $this->_base_path) = Helpers::explode_url($_SERVER["SCRIPT_FILENAME"]); - $this->_page_styles = array("base" => null); + $script = __FILE__; + if(isset($_SERVER["SCRIPT_FILENAME"])){ + $script = $_SERVER["SCRIPT_FILENAME"]; + } + list($this->_protocol, $this->_base_host, $this->_base_path) = Helpers::explode_url($script); + $this->_page_styles = array("base" => new Style($this)); } /** @@ -281,7 +285,7 @@ function add_style($key, Style $style) } /** - * lookup a specifc Style collection + * lookup a specific Style collection * * lookup() returns the Style collection specified by $key, or null if the Style is * not found. @@ -441,7 +445,7 @@ private function _specificity($selector, $origin = self::ORIG_AUTHOR) * @param bool $first_pass * * @throws Exception - * @return string + * @return array */ private function _css_selector_to_xpath($selector, $first_pass = false) { @@ -566,7 +570,7 @@ private function _css_selector_to_xpath($selector, $first_pass = false) break; case "+": - // All sibling elements that folow the current token + // All sibling elements that follow the current token if (mb_substr($query, -1, 1) !== "/") { $query .= "/"; } @@ -1261,7 +1265,7 @@ private function _parse_css($str) throw new Exception("Error parsing css file: preg_match_all() failed."); } - // After matching, the array indicies are set as follows: + // After matching, the array indices are set as follows: // // [0] => complete text of match // [1] => contains '@import ...;' or '@media {' if applicable @@ -1357,7 +1361,7 @@ private function _parse_css($str) $key = $page_selector; default: - continue; + break 2; } // Store the style for later... @@ -1556,7 +1560,7 @@ private function _parse_properties($str) foreach ($properties as $prop) { // If the $prop contains an url, the regex may be wrong - // @todo: fix the regex so that it works everytime + // @todo: fix the regex so that it works every time /*if (strpos($prop, "url(") === false) { if (preg_match("/([a-z-]+)\s*:\s*[^:]+$/i", $prop, $m)) $prop = $m[0]; @@ -1726,4 +1730,4 @@ function __toString() return $str; } -} \ No newline at end of file +} diff --git a/src/Dompdf.php b/src/Dompdf.php index 853c58c87..90d26ae22 100644 --- a/src/Dompdf.php +++ b/src/Dompdf.php @@ -107,7 +107,7 @@ class Dompdf /** * Desired paper size ('letter', 'legal', 'A4', etc.) * - * @var string + * @var string|array */ private $paperSize; @@ -273,6 +273,11 @@ public function __construct($options = null) { mb_internal_encoding('UTF-8'); + if (version_compare(PHP_VERSION, '7.0.0') >= 0) + { + ini_set('pcre.jit', 0); + } + if (isset($options) && $options instanceof Options) { $this->setOptions($options); } elseif (is_array($options)) { @@ -397,8 +402,8 @@ public function loadHtmlFile($file) } /** - * @param $str - * @param null $encoding + * @param string $str + * @param string $encoding * @deprecated */ public function load_html($str, $encoding = 'UTF-8') @@ -607,7 +612,7 @@ private function processHtml() if (!$accept) { //found at least one mediatype, but none of the accepted ones //Skip this css file. - continue; + break; } } @@ -628,7 +633,7 @@ private function processHtml() ($media = $tag->getAttribute("media")) && !in_array($media, $acceptedmedia) ) { - continue; + break; } $css = ""; @@ -1030,7 +1035,7 @@ public function set_paper($size, $orientation = "portrait") /** * Sets the paper size & orientation * - * @param string $size 'letter', 'legal', 'A4', etc. {@link Dompdf\Adapter\CPDF::$PAPER_SIZES} + * @param string|array $size 'letter', 'legal', 'A4', etc. {@link Dompdf\Adapter\CPDF::$PAPER_SIZES} * @param string $orientation 'portrait' or 'landscape' * @return $this */ diff --git a/src/FontMetrics.php b/src/FontMetrics.php index 0266e589e..712bd0acc 100644 --- a/src/FontMetrics.php +++ b/src/FontMetrics.php @@ -125,7 +125,7 @@ public function loadFontFamilies() $fontDir = $this->getOptions()->getFontDir(); $rootDir = $this->getOptions()->getRootDir(); - // FIXME: tempoarary define constants for cache files <= v0.6.2 + // FIXME: temporarily define constants for cache files <= v0.6.2 if (!defined("DOMPDF_DIR")) { define("DOMPDF_DIR", $rootDir); } if (!defined("DOMPDF_FONT_DIR")) { define("DOMPDF_FONT_DIR", $fontDir); } @@ -186,7 +186,6 @@ public function registerFont($style, $remoteFile, $context = null) $fontDir = $this->getOptions()->getFontDir(); $remoteHash = md5($remoteFile); $localFile = $fontDir . DIRECTORY_SEPARATOR . $remoteHash; - $localTempFile = @tempnam($this->options->get("tempDir"), "dompdf-font-"); $cacheEntry = $localFile; $localFile .= ".".strtolower(pathinfo(parse_url($remoteFile, PHP_URL_PATH),PATHINFO_EXTENSION)); @@ -198,6 +197,8 @@ public function registerFont($style, $remoteFile, $context = null) if (false === $remoteFileContent) { return false; } + + $localTempFile = @tempnam($this->options->get("tempDir"), "dompdf-font-"); file_put_contents($localTempFile, $remoteFileContent); $font = Font::load($localTempFile); diff --git a/src/Frame.php b/src/Frame.php index 0a3061b4a..31ea00a1f 100644 --- a/src/Frame.php +++ b/src/Frame.php @@ -148,7 +148,7 @@ class Frame protected $_is_cache = array(); /** - * Tells wether the frame was already pushed to the next page + * Tells whether the frame was already pushed to the next page * * @var bool */ @@ -160,7 +160,7 @@ class Frame public $_float_next_line = false; /** - * Tells wether the frame was split + * Tells whether the frame was split * * @var bool */ diff --git a/src/Frame/FrameTree.php b/src/Frame/FrameTree.php index 5d53b1678..1d2585439 100644 --- a/src/Frame/FrameTree.php +++ b/src/Frame/FrameTree.php @@ -22,7 +22,7 @@ * * The FrameTree consists of {@link Frame} objects each tied to specific * DOMNode objects in a specific DomDocument. The FrameTree has the same - * structure as the DomDocument, but adds additional capabalities for + * structure as the DomDocument, but adds additional capabilities for * styling and layout. * * @package dompdf @@ -90,7 +90,7 @@ public function __construct(DomDocument $dom) } /** - * Returns the DOMDocument object representing the curent html document + * Returns the DOMDocument object representing the current html document * * @return DOMDocument */ diff --git a/src/FrameDecorator/AbstractFrameDecorator.php b/src/FrameDecorator/AbstractFrameDecorator.php index b3c4db534..777fc3ce8 100644 --- a/src/FrameDecorator/AbstractFrameDecorator.php +++ b/src/FrameDecorator/AbstractFrameDecorator.php @@ -83,7 +83,7 @@ abstract class AbstractFrameDecorator extends Frame private $_positionned_parent; /** - * Cache for the get_parent wehile loop results + * Cache for the get_parent while loop results * * @var Frame */ @@ -438,6 +438,7 @@ function remove_child(Frame $child, $update_node = true) } /** + * @param bool $use_cache * @return AbstractFrameDecorator */ function get_parent($use_cache = true) diff --git a/src/FrameDecorator/ListBullet.php b/src/FrameDecorator/ListBullet.php index 42ef7b96a..5b2311886 100644 --- a/src/FrameDecorator/ListBullet.php +++ b/src/FrameDecorator/ListBullet.php @@ -48,7 +48,7 @@ function get_margin_width() return 0; } - return $style->get_font_size() * self::BULLET_SIZE + 2 * self::BULLET_PADDING; + return $style->font_size * self::BULLET_SIZE + 2 * self::BULLET_PADDING; } /** @@ -64,7 +64,7 @@ function get_margin_height() return 0; } - return $style->get_font_size() * self::BULLET_SIZE + 2 * self::BULLET_PADDING; + return $style->font_size * self::BULLET_SIZE + 2 * self::BULLET_PADDING; } /** diff --git a/src/FrameDecorator/Page.php b/src/FrameDecorator/Page.php index 8f4139075..39921aba8 100644 --- a/src/FrameDecorator/Page.php +++ b/src/FrameDecorator/Page.php @@ -169,7 +169,6 @@ function in_nested_table() */ function check_forced_page_break(Frame $frame) { - // Skip check if page is already split if ($this->_page_full) { return null; @@ -195,9 +194,10 @@ function check_forced_page_break(Frame $frame) // 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->style to the frame's original style. $frame->get_style()->page_break_before = "auto"; $this->_page_full = true; + $frame->_already_pushed = true; return true; } @@ -207,6 +207,7 @@ function check_forced_page_break(Frame $frame) $frame->split(null, true); $prev->get_style()->page_break_after = "auto"; $this->_page_full = true; + $frame->_already_pushed = true; return true; } @@ -217,6 +218,7 @@ function check_forced_page_break(Frame $frame) $frame->split(null, true); $prev_last_child->get_style()->page_break_after = "auto"; $this->_page_full = true; + $frame->_already_pushed = true; return true; } @@ -235,6 +237,10 @@ function check_forced_page_break(Frame $frame) * 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. + * 3. Between the content edge of a block container box and the + * outer edges of its child content (margin edges of block-level + * children or line box edges for inline-level children) if there + * is a (non-zero) gap between them. * * These breaks are subject to the following rules: * @@ -284,7 +290,7 @@ protected function _page_break_allowed(Frame $frame) if (in_array($display, $block_types)) { // Avoid breaks within table-cells - if ($this->_in_table) { + if ($this->_in_table > ($display === "table" ? 1 : 0)) { Helpers::dompdf_debug("page-break", "In table: " . $this->_in_table); return false; @@ -330,10 +336,14 @@ protected function _page_break_allowed(Frame $frame) return false; } - // If the frame is the first block-level frame, use the value from - // $frame's parent instead. + // If the frame is the first block-level frame, only allow a page + // break if there is a (non-zero) gap between the frame and its + // parent if (!$prev && $parent) { - return $this->_page_break_allowed($parent); + Helpers::dompdf_debug("page-break", "First block level frame, checking gap"); + + return $frame->get_style()->length_in_pt($frame->get_style()->margin_top) != 0 + || $parent->get_style()->length_in_pt($parent->get_style()->padding_top) != 0; } Helpers::dompdf_debug("page-break", "block: break allowed"); @@ -414,7 +424,7 @@ protected function _page_break_allowed(Frame $frame) $p = $p->find_block_parent(); } - // Avoid breaking after the first row of a table + // Avoid breaking before the first row of a table if ($table && $table->get_first_child() === $frame || $table->get_first_child()->get_first_child() === $frame) { Helpers::dompdf_debug("page-break", "table: first-row"); @@ -459,27 +469,10 @@ protected function _page_break_allowed(Frame $frame) */ function check_page_break(Frame $frame) { - //FIXME: should not need to do this since we're tracking table status in `$this->_in_table` - $p = $frame; - $in_table = false; - while ($p) { - if ($p->is_table()) { $in_table = true; break; } - $p = $p->get_parent(); - } - // Do not split if we have already or if the frame was already - // pushed to the next page (prevents infinite loops) - if ($in_table) { - if ($this->_page_full && $frame->_already_pushed) { - return false; - } - } elseif ($this->_page_full || $frame->_already_pushed) { + if ($this->_page_full || $frame->_already_pushed) { return false; } - //FIXME: work-around for infinite loop due to tables - if ($in_table && $frame->_already_pushed) { - return false; - } $p = $frame; do { $display = $p->get_style()->display; @@ -488,22 +481,12 @@ function check_page_break(Frame $frame) } } while ($p = $p->get_parent()); - // If the frame is absolute of fixed it shouldn't break + // If the frame is absolute or fixed it shouldn't break $p = $frame; do { if ($p->is_absolute()) { return false; } - - // FIXME If the row is taller than the page and - // if it the first of the page, we don't break - $display = $p->get_style()->display; - if ($display === "table-row" - && !$p->get_prev_sibling() - && $p->get_margin_height() > $this->get_margin_height() - ) { - return false; - } } while ($p = $p->get_parent()); $margin_height = $frame->get_margin_height(); @@ -519,7 +502,6 @@ function check_page_break(Frame $frame) $p = $p->get_parent(); } - // Check if $frame flows off the page if ($max_y <= $this->_bottom_page_margin) { // no: do nothing @@ -532,6 +514,7 @@ function check_page_break(Frame $frame) // yes: determine page break location $iter = $frame; $flg = false; + $pushed_flg = false; $in_table = $this->_in_table; @@ -544,11 +527,14 @@ function check_page_break(Frame $frame) break; } - if ($this->_page_break_allowed($iter)) { + if ($iter->_already_pushed) { + $pushed_flg = true; + } elseif ($this->_page_break_allowed($iter)) { Helpers::dompdf_debug("page-break", "break allowed, splitting."); $iter->split(null, true); $this->_page_full = true; $this->_in_table = $in_table; + $iter->_already_pushed = true; $frame->_already_pushed = true; return true; @@ -562,9 +548,15 @@ function check_page_break(Frame $frame) } $iter = $next; + $pushed_flg = false; continue; } + if ($pushed_flg) { + // The frame was already pushed, avoid breaking on a previous page + break; + } + if ($next = $iter->get_prev_sibling()) { Helpers::dompdf_debug("page-break", "following prev sibling."); @@ -608,6 +600,7 @@ function check_page_break(Frame $frame) if ($iter) { $iter->split(null, true); + $iter->_already_pushed = true; } else { return false; } diff --git a/src/FrameReflower/AbstractFrameReflower.php b/src/FrameReflower/AbstractFrameReflower.php index 48e66a74c..946d0969e 100644 --- a/src/FrameReflower/AbstractFrameReflower.php +++ b/src/FrameReflower/AbstractFrameReflower.php @@ -116,7 +116,7 @@ protected function _collapse_margins() } // Collapse our first child's margin, if there is no border or padding - if ($style->get_border_top_width() == 0 && $style->length_in_pt($style->padding_top) == 0) { + if ($style->border_top_width == 0 && $style->length_in_pt($style->padding_top) == 0) { $f = $this->_frame->get_first_child(); if ( $f && !$f->is_block() && !$f->is_table() ) { while ( $f = $f->get_next_sibling() ) { @@ -143,7 +143,7 @@ protected function _collapse_margins() } // Collapse our last child's margin, if there is no border or padding - if ($style->get_border_bottom_width() == 0 && $style->length_in_pt($style->padding_bottom) == 0) { + if ($style->border_bottom_width == 0 && $style->length_in_pt($style->padding_bottom) == 0) { $l = $this->_frame->get_last_child(); if ( $l && !$l->is_block() && !$l->is_table() ) { while ( $l = $l->get_prev_sibling() ) { @@ -398,7 +398,7 @@ protected function _parse_content() continue; } - preg_match('/(counters?)(^\()*?\(\s*([^\s,]+)\s*(,\s*["\']?([^"\'\)]+)["\']?\s*(,\s*([^\s)]+)\s*)?)?\)/i', $match[1], $args); + preg_match('/(counters?)(^\()*?\(\s*([^\s,]+)\s*(,\s*["\']?([^"\'\)]*)["\']?\s*(,\s*([^\s)]+)\s*)?)?\)/i', $match[1], $args); $counter_id = $args[3]; if (strtolower($args[1]) == 'counter') { // counter(name [,style]) diff --git a/src/FrameReflower/Block.php b/src/FrameReflower/Block.php index ef77be7cd..c6b75eb01 100644 --- a/src/FrameReflower/Block.php +++ b/src/FrameReflower/Block.php @@ -604,7 +604,7 @@ function vertical_align() break; case "text-top": // FIXME: this should be the height of the frame minus the height of the text - $y_offset = $height - (float)$style->length_in_pt($style->get_line_height(), $style->font_size); + $y_offset = $height - (float)$style->length_in_pt($style->line_height, $style->font_size); break; case "top": diff --git a/src/FrameReflower/Table.php b/src/FrameReflower/Table.php index 3ff262b2d..1f560c053 100644 --- a/src/FrameReflower/Table.php +++ b/src/FrameReflower/Table.php @@ -447,7 +447,7 @@ function reflow(BlockFrameDecorator $block = null) $style->margin_right = sprintf("%Fpt", $right);; } else { if ($left === "auto") { - $left = (float)$style->length_in_pt($cb["w"] - $right - $width, $cb["w"]); + $left = (float)$style->length_in_pt($cb["w"], $cb["w"]) - (float)$style->length_in_pt($right, $cb["w"]) - (float)$style->length_in_pt($width, $cb["w"]); } if ($right === "auto") { $left = (float)$style->length_in_pt($left, $cb["w"]); diff --git a/src/FrameReflower/Text.php b/src/FrameReflower/Text.php index 7ae13971b..321efeba5 100644 --- a/src/FrameReflower/Text.php +++ b/src/FrameReflower/Text.php @@ -467,9 +467,18 @@ function get_min_max_width() $style->border_right_width, $style->margin_right), $line_width); $min += $delta; + $min_word = $min; $max += $delta; - return $this->_min_max_cache = array($min, $max, "min" => $min, "max" => $max); + if ($style->word_wrap === 'break-word') { + // If it is allowed to break words, the min width is the widest character. + // But for performance reasons, we only check the first character. + $char = mb_substr($str, 0, 1); + $min_char = $this->getFontMetrics()->getTextWidth($char, $font, $size, $word_spacing, $char_spacing); + $min = $delta + $min_char; + } + + return $this->_min_max_cache = array($min, $max, $min_word, "min" => $min, "max" => $max, 'min_word' => $min_word); } /** diff --git a/src/Helpers.php b/src/Helpers.php index fc9db640e..ab05b6a74 100644 --- a/src/Helpers.php +++ b/src/Helpers.php @@ -63,7 +63,7 @@ public static function build_url($protocol, $host, $base_path, $url) } // Is the url already fully qualified, a Data URI, or a reference to a named anchor? - if (mb_strpos($url, "://") !== false || mb_substr($url, 0, 1) === "#" || mb_strpos($url, "data:") === 0 || mb_strpos($url, "mailto:") === 0) { + if (mb_strpos($url, "://") !== false || mb_substr($url, 0, 1) === "#" || mb_strpos($url, "data:") === 0 || mb_strpos($url, "mailto:") === 0 || mb_strpos($url, "tel:") === 0) { return $url; } @@ -583,6 +583,7 @@ public static function cmyk_to_rgb($c, $m = null, $y = null, $k = null) * getimagesize doesn't give a good size for 32bit BMP image v5 * * @param string $filename + * @param resource $context * @return array The same format as getimagesize($filename) */ public static function dompdf_getimagesize($filename, $context = null) diff --git a/src/Image/Cache.php b/src/Image/Cache.php index 479d2e679..e1139b11d 100644 --- a/src/Image/Cache.php +++ b/src/Image/Cache.php @@ -34,7 +34,7 @@ class Cache * * @var string */ - public static $broken_image = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABABAMAAABYR2ztAAAAA3NCSVQICAjb4U/gAAAAHlBMVEWZmZn////g4OCkpKS1tbXv7++9vb2tra3m5ub5+fkFnN6oAAAACXBIWXMAAAsSAAALEgHS3X78AAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M0BrLToAAAABZ0RVh0Q3JlYXRpb24gVGltZQAwNC8xMi8xMRPnI58AAAGZSURBVEiJhZbPasMwDMbTw2DHKhDQcbDQPsEge4BAjg0Mxh5gkBcY7Niwkpx32PvOjv9JspX60It/+fxJsqxW1b11gN11rA7N3v6vAd5nfR9fDYCTDiyzAeA6qgKd9QDNoAtsAKyKCxzAAfhdBuyHGwC3oovNvQOaxxJwnSNg3ZQFAlBy4ax7AG6ZBLrgA5Cn038SAPgREiaJHJASwXYEhEQQIACyikTTCWCBJJoANBfpPAKQdBLHFMBYkctcBKIE9lAGggt6gRjgA2GV44CL7m1WgS08fAAdsPHxyyMAIyHujgRwEldHArCKy5cBz90+gNOyf8TTyKOUQN2LPEmgnWWPcKD+sr+rnuqTK1avAcHfRSv3afTgVAbqmCPiggLtGM8aSkBNOidVjADrmIDYebT1PoGsWJEE8Oc0b96aZoe4iMBZPiADB6RAzEUA2vwRmyiAL3Lfv6MBSEmUEg7ALt/3LhxwLgj4QNw4UCbKEsaBNpPsyRbgVRASFig78BIGyJNIJQyQTwIi0RvgT98H+Mi6W67j3X8H/427u5bfpQGVAAAAAElFTkSuQmCC"; + public static $broken_image = "data:image/svg+xml;charset=utf8,%3C?xml version='1.0'?%3E%3Csvg width='64' height='64' xmlns='http://www.w3.org/2000/svg'%3E%3Cg%3E%3Crect stroke='%23666666' id='svg_1' height='60.499994' width='60.166667' y='1.666669' x='1.999998' stroke-width='1.5' fill='none'/%3E%3Cline stroke-linecap='null' stroke-linejoin='null' id='svg_3' y2='59.333253' x2='59.749916' y1='4.333415' x1='4.250079' stroke-width='1.5' stroke='%23999999' fill='none'/%3E%3Cline stroke-linecap='null' stroke-linejoin='null' id='svg_4' y2='59.999665' x2='4.062838' y1='3.750342' x1='60.062164' stroke-width='1.5' stroke='%23999999' fill='none'/%3E%3C/g%3E%3C/svg%3E"; public static $error_message = "Image not found or type unknown"; @@ -181,6 +181,6 @@ static function is_broken($url) } } -if (file_exists(realpath(__DIR__ . "/../../lib/res/broken_image.png"))) { - Cache::$broken_image = realpath(__DIR__ . "/../../lib/res/broken_image.png"); +if (file_exists(realpath(__DIR__ . "/../../lib/res/broken_image.svg"))) { + Cache::$broken_image = realpath(__DIR__ . "/../../lib/res/broken_image.svg"); } \ No newline at end of file diff --git a/src/Options.php b/src/Options.php index ec30b0e2b..b6c7c362c 100644 --- a/src/Options.php +++ b/src/Options.php @@ -13,7 +13,7 @@ class Options /** * The location of a temporary directory. * - * The directory specified must be writeable by the webserver process. + * The directory specified must be writable by the webserver process. * The temporary directory is required to download remote images and when * using the PFDLib back end. * @@ -112,7 +112,7 @@ class Options * Image DPI setting * * This setting determines the default DPI setting for images and fonts. The - * DPI may be overridden for inline images by explictly setting the + * DPI may be overridden for inline images by explicitly setting the * image's width & height style attributes (i.e. if the image's native * width is 600 pixels and you specify the image's width as 72 points, * the image will have a DPI of 600 in the rendered PDF. The DPI of @@ -267,7 +267,7 @@ class Options * * @link http://www.pdflib.com * - * If pdflib present in web server and auto or selected explicitely above, + * If pdflib present in web server and auto or selected explicitly above, * a real license code must exist! * * @var string diff --git a/src/Positioner/ListBullet.php b/src/Positioner/ListBullet.php index 29da406e5..36691f232 100644 --- a/src/Positioner/ListBullet.php +++ b/src/Positioner/ListBullet.php @@ -41,7 +41,7 @@ function position(AbstractFrameDecorator $frame) $n = $frame->get_next_sibling(); if ($n) { $style = $n->get_style(); - $line_height = $style->length_in_pt($style->line_height, $style->get_font_size()); + $line_height = $style->length_in_pt($style->line_height, $style->font_size); $offset = (float)$style->length_in_pt($line_height, $n->get_containing_block("h")) - $frame->get_height(); $y += $offset / 2; } @@ -66,7 +66,7 @@ function position(AbstractFrameDecorator $frame) // 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(); + $font_size = $style->font_size; $line_height = (float)$style->length_in_pt($style->line_height, $font_size); $y += ($line_height - $font_size) / 2; */ diff --git a/src/Renderer/ListBullet.php b/src/Renderer/ListBullet.php index c220426cc..fa0bc3731 100644 --- a/src/Renderer/ListBullet.php +++ b/src/Renderer/ListBullet.php @@ -134,7 +134,7 @@ private function make_counter($n, $type, $pad = null) function render(Frame $frame) { $style = $frame->get_style(); - $font_size = $style->get_font_size(); + $font_size = $style->font_size; $line_height = (float)$style->length_in_pt($style->line_height, $frame->get_containing_block("h")); $this->_set_opacity($frame->get_opacity($style->opacity)); diff --git a/src/Renderer/Text.php b/src/Renderer/Text.php index 149b3b683..51e6e6259 100644 --- a/src/Renderer/Text.php +++ b/src/Renderer/Text.php @@ -69,7 +69,8 @@ function render(Frame $frame) $x += (float)$style->length_in_pt(array($ml, $pl, $bl), $cb["w"]); $font = $style->font_family; - $size = $frame_font_size = $style->font_size; + $size = $style->font_size; + $frame_font_size = $frame->get_dompdf()->getFontMetrics()->getFontHeight($font, $size); $word_spacing = $frame->get_text_spacing() + (float)$style->length_in_pt($style->word_spacing); $char_spacing = (float)$style->length_in_pt($style->letter_spacing); $width = $style->width; @@ -113,7 +114,7 @@ function render(Frame $frame) } $descent = $size * $underline_position; - $base = $size; + $base = $frame_font_size; // Handle text decoration: // http://www.w3.org/TR/CSS21/text.html#propdef-text-decoration @@ -137,7 +138,7 @@ function render(Frame $frame) switch ($text_deco) { default: - continue; + continue 2; case "underline": $deco_y += $base - $descent + $underline_offset + $line_thickness / 2; @@ -159,7 +160,7 @@ function render(Frame $frame) } if ($this->_dompdf->getOptions()->getDebugLayout() && $this->_dompdf->getOptions()->getDebugLayoutLines()) { - $text_width = $this->_dompdf->getFontMetrics()->getTextWidth($text, $font, $frame_font_size); + $text_width = $this->_dompdf->getFontMetrics()->getTextWidth($text, $font, $size); $this->_debug_layout(array($x, $y, $text_width + ($line->wc - 1) * $word_spacing, $frame_font_size), "orange", array(0.5, 0.5)); } }