first
This commit is contained in:
944
system/vendor/pancakeapp/dompdf/src/Adapter/CPDF.php
vendored
Executable file
944
system/vendor/pancakeapp/dompdf/src/Adapter/CPDF.php
vendored
Executable file
@@ -0,0 +1,944 @@
|
||||
<?php
|
||||
/**
|
||||
* @package dompdf
|
||||
* @link https://github.com/dompdf/dompdf
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
*/
|
||||
|
||||
// FIXME: Need to sanity check inputs to this class
|
||||
namespace Dompdf\Adapter;
|
||||
|
||||
use Dompdf\Canvas;
|
||||
use Dompdf\Dompdf;
|
||||
use Dompdf\Exception;
|
||||
use Dompdf\FontMetrics;
|
||||
use Dompdf\Helpers;
|
||||
use Dompdf\Image\Cache;
|
||||
use FontLib\Exception\FontNotFoundException;
|
||||
|
||||
/**
|
||||
* PDF rendering interface
|
||||
*
|
||||
* Dompdf\Adapter\CPDF 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 implements Canvas
|
||||
{
|
||||
|
||||
/**
|
||||
* Dimensions of paper sizes in points
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
static $PAPER_SIZES = [
|
||||
"4a0" => [0.0, 0.0, 4767.87, 6740.79],
|
||||
"2a0" => [0.0, 0.0, 3370.39, 4767.87],
|
||||
"a0" => [0.0, 0.0, 2383.94, 3370.39],
|
||||
"a1" => [0.0, 0.0, 1683.78, 2383.94],
|
||||
"a2" => [0.0, 0.0, 1190.55, 1683.78],
|
||||
"a3" => [0.0, 0.0, 841.89, 1190.55],
|
||||
"a4" => [0.0, 0.0, 595.28, 841.89],
|
||||
"a5" => [0.0, 0.0, 419.53, 595.28],
|
||||
"a6" => [0.0, 0.0, 297.64, 419.53],
|
||||
"a7" => [0.0, 0.0, 209.76, 297.64],
|
||||
"a8" => [0.0, 0.0, 147.40, 209.76],
|
||||
"a9" => [0.0, 0.0, 104.88, 147.40],
|
||||
"a10" => [0.0, 0.0, 73.70, 104.88],
|
||||
"b0" => [0.0, 0.0, 2834.65, 4008.19],
|
||||
"b1" => [0.0, 0.0, 2004.09, 2834.65],
|
||||
"b2" => [0.0, 0.0, 1417.32, 2004.09],
|
||||
"b3" => [0.0, 0.0, 1000.63, 1417.32],
|
||||
"b4" => [0.0, 0.0, 708.66, 1000.63],
|
||||
"b5" => [0.0, 0.0, 498.90, 708.66],
|
||||
"b6" => [0.0, 0.0, 354.33, 498.90],
|
||||
"b7" => [0.0, 0.0, 249.45, 354.33],
|
||||
"b8" => [0.0, 0.0, 175.75, 249.45],
|
||||
"b9" => [0.0, 0.0, 124.72, 175.75],
|
||||
"b10" => [0.0, 0.0, 87.87, 124.72],
|
||||
"c0" => [0.0, 0.0, 2599.37, 3676.54],
|
||||
"c1" => [0.0, 0.0, 1836.85, 2599.37],
|
||||
"c2" => [0.0, 0.0, 1298.27, 1836.85],
|
||||
"c3" => [0.0, 0.0, 918.43, 1298.27],
|
||||
"c4" => [0.0, 0.0, 649.13, 918.43],
|
||||
"c5" => [0.0, 0.0, 459.21, 649.13],
|
||||
"c6" => [0.0, 0.0, 323.15, 459.21],
|
||||
"c7" => [0.0, 0.0, 229.61, 323.15],
|
||||
"c8" => [0.0, 0.0, 161.57, 229.61],
|
||||
"c9" => [0.0, 0.0, 113.39, 161.57],
|
||||
"c10" => [0.0, 0.0, 79.37, 113.39],
|
||||
"ra0" => [0.0, 0.0, 2437.80, 3458.27],
|
||||
"ra1" => [0.0, 0.0, 1729.13, 2437.80],
|
||||
"ra2" => [0.0, 0.0, 1218.90, 1729.13],
|
||||
"ra3" => [0.0, 0.0, 864.57, 1218.90],
|
||||
"ra4" => [0.0, 0.0, 609.45, 864.57],
|
||||
"sra0" => [0.0, 0.0, 2551.18, 3628.35],
|
||||
"sra1" => [0.0, 0.0, 1814.17, 2551.18],
|
||||
"sra2" => [0.0, 0.0, 1275.59, 1814.17],
|
||||
"sra3" => [0.0, 0.0, 907.09, 1275.59],
|
||||
"sra4" => [0.0, 0.0, 637.80, 907.09],
|
||||
"letter" => [0.0, 0.0, 612.00, 792.00],
|
||||
"half-letter" => [0.0, 0.0, 396.00, 612.00],
|
||||
"legal" => [0.0, 0.0, 612.00, 1008.00],
|
||||
"ledger" => [0.0, 0.0, 1224.00, 792.00],
|
||||
"tabloid" => [0.0, 0.0, 792.00, 1224.00],
|
||||
"executive" => [0.0, 0.0, 521.86, 756.00],
|
||||
"folio" => [0.0, 0.0, 612.00, 936.00],
|
||||
"commercial #10 envelope" => [0.0, 0.0, 684.00, 297.00],
|
||||
"catalog #10 1/2 envelope" => [0.0, 0.0, 648.00, 864.00],
|
||||
"8.5x11" => [0.0, 0.0, 612.00, 792.00],
|
||||
"8.5x14" => [0.0, 0.0, 612.00, 1008.00],
|
||||
"11x17" => [0.0, 0.0, 792.00, 1224.00],
|
||||
];
|
||||
|
||||
/**
|
||||
* The Dompdf object
|
||||
*
|
||||
* @var Dompdf
|
||||
*/
|
||||
protected $_dompdf;
|
||||
|
||||
/**
|
||||
* Instance of Cpdf class
|
||||
*
|
||||
* @var \Dompdf\Cpdf
|
||||
*/
|
||||
protected $_pdf;
|
||||
|
||||
/**
|
||||
* PDF width, in points
|
||||
*
|
||||
* @var float
|
||||
*/
|
||||
protected $_width;
|
||||
|
||||
/**
|
||||
* PDF height, in points
|
||||
*
|
||||
* @var float
|
||||
*/
|
||||
protected $_height;
|
||||
|
||||
/**
|
||||
* Current page number
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $_page_number;
|
||||
|
||||
/**
|
||||
* Total number of pages
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $_page_count;
|
||||
|
||||
/**
|
||||
* Array of pages for accessing after rendering is initially complete
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $_pages;
|
||||
|
||||
/**
|
||||
* Currently-applied opacity level (0 - 1)
|
||||
*
|
||||
* @var float
|
||||
*/
|
||||
protected $_current_opacity = 1;
|
||||
|
||||
public function __construct($paper = "letter", string $orientation = "portrait", ?Dompdf $dompdf = null)
|
||||
{
|
||||
if (is_array($paper)) {
|
||||
$size = array_map("floatval", $paper);
|
||||
} else {
|
||||
$paper = strtolower($paper);
|
||||
$size = self::$PAPER_SIZES[$paper] ?? self::$PAPER_SIZES["letter"];
|
||||
}
|
||||
|
||||
if (strtolower($orientation) === "landscape") {
|
||||
[$size[2], $size[3]] = [$size[3], $size[2]];
|
||||
}
|
||||
|
||||
if ($dompdf === null) {
|
||||
$this->_dompdf = new Dompdf();
|
||||
} else {
|
||||
$this->_dompdf = $dompdf;
|
||||
}
|
||||
|
||||
$this->_pdf = new \Dompdf\Cpdf(
|
||||
$size,
|
||||
true,
|
||||
$this->_dompdf->getOptions()->getFontCache(),
|
||||
$this->_dompdf->getOptions()->getTempDir()
|
||||
);
|
||||
|
||||
$this->_pdf->addInfo("Producer", sprintf("%s + CPDF", $this->_dompdf->version));
|
||||
$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->_page_number = $this->_page_count = 1;
|
||||
|
||||
$this->_pages = [$this->_pdf->getFirstPageId()];
|
||||
}
|
||||
|
||||
public function get_dompdf()
|
||||
{
|
||||
return $this->_dompdf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Cpdf instance
|
||||
*
|
||||
* @return \Dompdf\Cpdf
|
||||
*/
|
||||
public function get_cpdf()
|
||||
{
|
||||
return $this->_pdf;
|
||||
}
|
||||
|
||||
public function add_info(string $label, string $value): void
|
||||
{
|
||||
$this->_pdf->addInfo($label, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a new '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.
|
||||
*
|
||||
* The return value is an integer ID for the new object.
|
||||
*
|
||||
* @see CPDF::close_object()
|
||||
* @see CPDF::add_object()
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function open_object()
|
||||
{
|
||||
$ret = $this->_pdf->openObject();
|
||||
$this->_pdf->saveState();
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reopens an existing 'object'
|
||||
*
|
||||
* @see CPDF::open_object()
|
||||
* @param int $object the ID of a previously opened object
|
||||
*/
|
||||
public function reopen_object($object)
|
||||
{
|
||||
$this->_pdf->reopenObject($object);
|
||||
$this->_pdf->saveState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the current 'object'
|
||||
*
|
||||
* @see CPDF::open_object()
|
||||
*/
|
||||
public 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::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
|
||||
*/
|
||||
public 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
|
||||
*/
|
||||
public function stop_object($object)
|
||||
{
|
||||
$this->_pdf->stopObject($object);
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize the pdf object's current state for retrieval later
|
||||
*/
|
||||
public function serialize_object($id)
|
||||
{
|
||||
return $this->_pdf->serializeObject($id);
|
||||
}
|
||||
|
||||
public function reopen_serialized_object($obj)
|
||||
{
|
||||
return $this->_pdf->restoreSerializedObject($obj);
|
||||
}
|
||||
|
||||
//........................................................................
|
||||
|
||||
public function get_width()
|
||||
{
|
||||
return $this->_width;
|
||||
}
|
||||
|
||||
public function get_height()
|
||||
{
|
||||
return $this->_height;
|
||||
}
|
||||
|
||||
public function get_page_number()
|
||||
{
|
||||
return $this->_page_number;
|
||||
}
|
||||
|
||||
public function get_page_count()
|
||||
{
|
||||
return $this->_page_count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current page number
|
||||
*
|
||||
* @param int $num
|
||||
*/
|
||||
public function set_page_number($num)
|
||||
{
|
||||
$this->_page_number = $num;
|
||||
}
|
||||
|
||||
public function set_page_count($count)
|
||||
{
|
||||
$this->_page_count = $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the stroke color
|
||||
*
|
||||
* See {@link Style::set_color()} for the format of the color array.
|
||||
*
|
||||
* @param array $color
|
||||
*/
|
||||
protected function _set_stroke_color($color)
|
||||
{
|
||||
$this->_pdf->setStrokeColor($color);
|
||||
$alpha = isset($color["alpha"]) ? $color["alpha"] : 1;
|
||||
$alpha *= $this->_current_opacity;
|
||||
$this->_set_line_transparency("Normal", $alpha);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the fill colour
|
||||
*
|
||||
* See {@link Style::set_color()} for the format of the colour array.
|
||||
*
|
||||
* @param array $color
|
||||
*/
|
||||
protected function _set_fill_color($color)
|
||||
{
|
||||
$this->_pdf->setColor($color);
|
||||
$alpha = isset($color["alpha"]) ? $color["alpha"] : 1;
|
||||
$alpha *= $this->_current_opacity;
|
||||
$this->_set_fill_transparency("Normal", $alpha);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
public function set_opacity(float $opacity, string $mode = "Normal"): void
|
||||
{
|
||||
$this->_set_line_transparency($mode, $opacity);
|
||||
$this->_set_fill_transparency($mode, $opacity);
|
||||
$this->_current_opacity = $opacity;
|
||||
}
|
||||
|
||||
public function set_default_view($view, $options = [])
|
||||
{
|
||||
array_unshift($options, $view);
|
||||
call_user_func_array([$this->_pdf, "openHere"], $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remaps y coords from 4th to 1st quadrant
|
||||
*
|
||||
* @param float $y
|
||||
* @return float
|
||||
*/
|
||||
protected function y($y)
|
||||
{
|
||||
return $this->_height - $y;
|
||||
}
|
||||
|
||||
public function line($x1, $y1, $x2, $y2, $color, $width, $style = [], $cap = "butt")
|
||||
{
|
||||
$this->_set_stroke_color($color);
|
||||
$this->_set_line_style($width, $cap, "", $style);
|
||||
|
||||
$this->_pdf->line($x1, $this->y($y1),
|
||||
$x2, $this->y($y2));
|
||||
$this->_set_line_transparency("Normal", $this->_current_opacity);
|
||||
}
|
||||
|
||||
public function arc($x, $y, $r1, $r2, $astart, $aend, $color, $width, $style = [], $cap = "butt")
|
||||
{
|
||||
$this->_set_stroke_color($color);
|
||||
$this->_set_line_style($width, $cap, "", $style);
|
||||
|
||||
$this->_pdf->ellipse($x, $this->y($y), $r1, $r2, 0, 8, $astart, $aend, false, false, true, false);
|
||||
$this->_set_line_transparency("Normal", $this->_current_opacity);
|
||||
}
|
||||
|
||||
public function rectangle($x1, $y1, $w, $h, $color, $width, $style = [], $cap = "butt")
|
||||
{
|
||||
$this->_set_stroke_color($color);
|
||||
$this->_set_line_style($width, $cap, "", $style);
|
||||
$this->_pdf->rectangle($x1, $this->y($y1) - $h, $w, $h);
|
||||
$this->_set_line_transparency("Normal", $this->_current_opacity);
|
||||
}
|
||||
|
||||
public function filled_rectangle($x1, $y1, $w, $h, $color)
|
||||
{
|
||||
$this->_set_fill_color($color);
|
||||
$this->_pdf->filledRectangle($x1, $this->y($y1) - $h, $w, $h);
|
||||
$this->_set_fill_transparency("Normal", $this->_current_opacity);
|
||||
}
|
||||
|
||||
public function clipping_rectangle($x1, $y1, $w, $h)
|
||||
{
|
||||
$this->_pdf->clippingRectangle($x1, $this->y($y1) - $h, $w, $h);
|
||||
}
|
||||
|
||||
public function clipping_roundrectangle($x1, $y1, $w, $h, $rTL, $rTR, $rBR, $rBL)
|
||||
{
|
||||
$this->_pdf->clippingRectangleRounded($x1, $this->y($y1) - $h, $w, $h, $rTL, $rTR, $rBR, $rBL);
|
||||
}
|
||||
|
||||
public function clipping_polygon(array $points): void
|
||||
{
|
||||
// Adjust y values
|
||||
for ($i = 1; $i < count($points); $i += 2) {
|
||||
$points[$i] = $this->y($points[$i]);
|
||||
}
|
||||
|
||||
$this->_pdf->clippingPolygon($points);
|
||||
}
|
||||
|
||||
public function clipping_end()
|
||||
{
|
||||
$this->_pdf->clippingEnd();
|
||||
}
|
||||
|
||||
public function save()
|
||||
{
|
||||
$this->_pdf->saveState();
|
||||
}
|
||||
|
||||
public function restore()
|
||||
{
|
||||
$this->_pdf->restoreState();
|
||||
}
|
||||
|
||||
public function rotate($angle, $x, $y)
|
||||
{
|
||||
$this->_pdf->rotate($angle, $x, $y);
|
||||
}
|
||||
|
||||
public function skew($angle_x, $angle_y, $x, $y)
|
||||
{
|
||||
$this->_pdf->skew($angle_x, $angle_y, $x, $y);
|
||||
}
|
||||
|
||||
public function scale($s_x, $s_y, $x, $y)
|
||||
{
|
||||
$this->_pdf->scale($s_x, $s_y, $x, $y);
|
||||
}
|
||||
|
||||
public function translate($t_x, $t_y)
|
||||
{
|
||||
$this->_pdf->translate($t_x, $t_y);
|
||||
}
|
||||
|
||||
public function transform($a, $b, $c, $d, $e, $f)
|
||||
{
|
||||
$this->_pdf->transform([$a, $b, $c, $d, $e, $f]);
|
||||
}
|
||||
|
||||
public function polygon($points, $color, $width = null, $style = [], $fill = false)
|
||||
{
|
||||
$this->_set_fill_color($color);
|
||||
$this->_set_stroke_color($color);
|
||||
|
||||
if (!$fill && isset($width)) {
|
||||
$this->_set_line_style($width, "square", "miter", $style);
|
||||
}
|
||||
|
||||
// Adjust y values
|
||||
for ($i = 1; $i < count($points); $i += 2) {
|
||||
$points[$i] = $this->y($points[$i]);
|
||||
}
|
||||
|
||||
$this->_pdf->polygon($points, $fill);
|
||||
|
||||
$this->_set_fill_transparency("Normal", $this->_current_opacity);
|
||||
$this->_set_line_transparency("Normal", $this->_current_opacity);
|
||||
}
|
||||
|
||||
public function circle($x, $y, $r, $color, $width = null, $style = [], $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), $r, 0, 0, 8, 0, 360, 1, $fill);
|
||||
|
||||
$this->_set_fill_transparency("Normal", $this->_current_opacity);
|
||||
$this->_set_line_transparency("Normal", $this->_current_opacity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert image to a PNG image
|
||||
*
|
||||
* @param string $image_url
|
||||
* @param string $type
|
||||
*
|
||||
* @return string|null The url of the newly converted image
|
||||
*/
|
||||
protected function _convert_to_png($image_url, $type)
|
||||
{
|
||||
$filename = Cache::getTempImage($image_url);
|
||||
|
||||
if ($filename !== null && file_exists($filename)) {
|
||||
return $filename;
|
||||
}
|
||||
|
||||
$func_name = "imagecreatefrom$type";
|
||||
|
||||
set_error_handler([Helpers::class, "record_warnings"]);
|
||||
|
||||
if (!function_exists($func_name)) {
|
||||
if (!method_exists(Helpers::class, $func_name)) {
|
||||
throw new Exception("Function $func_name() not found. Cannot convert $type image: $image_url. Please install the image PHP extension.");
|
||||
}
|
||||
$func_name = [Helpers::class, $func_name];
|
||||
}
|
||||
|
||||
try {
|
||||
$im = call_user_func($func_name, $image_url);
|
||||
|
||||
if ($im) {
|
||||
imageinterlace($im, false);
|
||||
|
||||
$tmp_dir = $this->_dompdf->getOptions()->getTempDir();
|
||||
$tmp_name = @tempnam($tmp_dir, "{$type}_dompdf_img_");
|
||||
@unlink($tmp_name);
|
||||
$filename = "$tmp_name.png";
|
||||
|
||||
imagepng($im, $filename);
|
||||
imagedestroy($im);
|
||||
} else {
|
||||
$filename = null;
|
||||
}
|
||||
} finally {
|
||||
restore_error_handler();
|
||||
}
|
||||
|
||||
if ($filename !== null) {
|
||||
Cache::addTempImage($image_url, $filename);
|
||||
}
|
||||
|
||||
return $filename;
|
||||
}
|
||||
|
||||
public function image($img, $x, $y, $w, $h, $resolution = "normal")
|
||||
{
|
||||
[$width, $height, $type] = Helpers::dompdf_getimagesize($img, $this->get_dompdf()->getHttpContext());
|
||||
|
||||
$debug_png = $this->_dompdf->getOptions()->getDebugPng();
|
||||
|
||||
if ($debug_png) {
|
||||
print "[image:$img|$width|$height|$type]";
|
||||
}
|
||||
|
||||
switch ($type) {
|
||||
case "jpeg":
|
||||
if ($debug_png) {
|
||||
print '!!!jpg!!!';
|
||||
}
|
||||
$this->_pdf->addJpegFromFile($img, $x, $this->y($y) - $h, $w, $h);
|
||||
break;
|
||||
|
||||
case "webp":
|
||||
/** @noinspection PhpMissingBreakStatementInspection */
|
||||
case "gif":
|
||||
/** @noinspection PhpMissingBreakStatementInspection */
|
||||
case "bmp":
|
||||
if ($debug_png) print "!!!{$type}!!!";
|
||||
$img = $this->_convert_to_png($img, $type);
|
||||
if ($img === null) {
|
||||
if ($debug_png) print '!!!conversion to PDF failed!!!';
|
||||
$this->image(Cache::$broken_image, $x, $y, $w, $h, $resolution);
|
||||
break;
|
||||
}
|
||||
|
||||
case "png":
|
||||
if ($debug_png) print '!!!png!!!';
|
||||
|
||||
$this->_pdf->addPngFromFile($img, $x, $this->y($y) - $h, $w, $h);
|
||||
break;
|
||||
|
||||
case "svg":
|
||||
if ($debug_png) print '!!!SVG!!!';
|
||||
|
||||
$this->_pdf->addSvgFromFile($img, $x, $this->y($y) - $h, $w, $h);
|
||||
break;
|
||||
|
||||
default:
|
||||
if ($debug_png) print '!!!unknown!!!';
|
||||
}
|
||||
}
|
||||
|
||||
public function select($x, $y, $w, $h, $font, $size, $color = [0, 0, 0], $opts = [])
|
||||
{
|
||||
$pdf = $this->_pdf;
|
||||
|
||||
$font .= ".afm";
|
||||
$pdf->selectFont($font);
|
||||
|
||||
if (!isset($pdf->acroFormId)) {
|
||||
$pdf->addForm();
|
||||
}
|
||||
|
||||
$ft = \Dompdf\Cpdf::ACROFORM_FIELD_CHOICE;
|
||||
$ff = \Dompdf\Cpdf::ACROFORM_FIELD_CHOICE_COMBO;
|
||||
|
||||
$id = $pdf->addFormField($ft, rand(), $x, $this->y($y) - $h, $x + $w, $this->y($y), $ff, $size, $color);
|
||||
$pdf->setFormFieldOpt($id, $opts);
|
||||
}
|
||||
|
||||
public function textarea($x, $y, $w, $h, $font, $size, $color = [0, 0, 0])
|
||||
{
|
||||
$pdf = $this->_pdf;
|
||||
|
||||
$font .= ".afm";
|
||||
$pdf->selectFont($font);
|
||||
|
||||
if (!isset($pdf->acroFormId)) {
|
||||
$pdf->addForm();
|
||||
}
|
||||
|
||||
$ft = \Dompdf\Cpdf::ACROFORM_FIELD_TEXT;
|
||||
$ff = \Dompdf\Cpdf::ACROFORM_FIELD_TEXT_MULTILINE;
|
||||
|
||||
$pdf->addFormField($ft, rand(), $x, $this->y($y) - $h, $x + $w, $this->y($y), $ff, $size, $color);
|
||||
}
|
||||
|
||||
public function input($x, $y, $w, $h, $type, $font, $size, $color = [0, 0, 0])
|
||||
{
|
||||
$pdf = $this->_pdf;
|
||||
|
||||
$font .= ".afm";
|
||||
$pdf->selectFont($font);
|
||||
|
||||
if (!isset($pdf->acroFormId)) {
|
||||
$pdf->addForm();
|
||||
}
|
||||
|
||||
$ft = \Dompdf\Cpdf::ACROFORM_FIELD_TEXT;
|
||||
$ff = 0;
|
||||
|
||||
switch ($type) {
|
||||
case 'text':
|
||||
$ft = \Dompdf\Cpdf::ACROFORM_FIELD_TEXT;
|
||||
break;
|
||||
case 'password':
|
||||
$ft = \Dompdf\Cpdf::ACROFORM_FIELD_TEXT;
|
||||
$ff = \Dompdf\Cpdf::ACROFORM_FIELD_TEXT_PASSWORD;
|
||||
break;
|
||||
case 'submit':
|
||||
$ft = \Dompdf\Cpdf::ACROFORM_FIELD_BUTTON;
|
||||
break;
|
||||
}
|
||||
|
||||
$pdf->addFormField($ft, rand(), $x, $this->y($y) - $h, $x + $w, $this->y($y), $ff, $size, $color);
|
||||
}
|
||||
|
||||
public function text($x, $y, $text, $font, $size, $color = [0, 0, 0], $word_space = 0.0, $char_space = 0.0, $angle = 0.0)
|
||||
{
|
||||
$pdf = $this->_pdf;
|
||||
|
||||
$this->_set_fill_color($color);
|
||||
|
||||
$is_font_subsetting = $this->_dompdf->getOptions()->getIsFontSubsettingEnabled();
|
||||
$pdf->selectFont($font . '.afm', '', true, $is_font_subsetting);
|
||||
|
||||
$pdf->addText($x, $this->y($y) - $pdf->getFontHeight($size), $size, $text, $angle, $word_space, $char_space);
|
||||
|
||||
$this->_set_fill_transparency("Normal", $this->_current_opacity);
|
||||
}
|
||||
|
||||
public function javascript($code)
|
||||
{
|
||||
$this->_pdf->addJavascript($code);
|
||||
}
|
||||
|
||||
//........................................................................
|
||||
|
||||
public function add_named_dest($anchorname)
|
||||
{
|
||||
$this->_pdf->addDestination($anchorname, "Fit");
|
||||
}
|
||||
|
||||
public 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($url, $x, $y, $x + $width, $y + $height);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FontNotFoundException
|
||||
*/
|
||||
public function get_text_width($text, $font, $size, $word_spacing = 0.0, $char_spacing = 0.0)
|
||||
{
|
||||
$this->_pdf->selectFont($font, '', true, $this->_dompdf->getOptions()->getIsFontSubsettingEnabled());
|
||||
return $this->_pdf->getTextWidth($size, $text, $word_spacing, $char_spacing);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FontNotFoundException
|
||||
*/
|
||||
public function get_font_height($font, $size)
|
||||
{
|
||||
$options = $this->_dompdf->getOptions();
|
||||
$this->_pdf->selectFont($font, '', true, $options->getIsFontSubsettingEnabled());
|
||||
|
||||
return $this->_pdf->getFontHeight($size) * $options->getFontHeightRatio();
|
||||
}
|
||||
|
||||
/*function get_font_x_height($font, $size) {
|
||||
$this->_pdf->selectFont($font);
|
||||
$ratio = $this->_dompdf->getOptions()->getFontHeightRatio();
|
||||
return $this->_pdf->getFontXHeight($size) * $ratio;
|
||||
}*/
|
||||
|
||||
/**
|
||||
* @throws FontNotFoundException
|
||||
*/
|
||||
public function get_font_baseline($font, $size)
|
||||
{
|
||||
$ratio = $this->_dompdf->getOptions()->getFontHeightRatio();
|
||||
return $this->get_font_height($font, $size) / $ratio;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a callback or script on every page.
|
||||
*
|
||||
* The callback function receives the four parameters `int $pageNumber`,
|
||||
* `int $pageCount`, `Canvas $canvas`, and `FontMetrics $fontMetrics`, in
|
||||
* that order. If a script is passed as string, the variables `$PAGE_NUM`,
|
||||
* `$PAGE_COUNT`, `$pdf`, and `$fontMetrics` are available instead. Passing
|
||||
* a script as string is deprecated and will be removed in a future version.
|
||||
*
|
||||
* This function can be used to add page numbers to all pages after the
|
||||
* first one, for example.
|
||||
*
|
||||
* @param callable|string $callback The callback function or PHP script to process on every page
|
||||
*/
|
||||
public function page_script($callback): void
|
||||
{
|
||||
if (is_string($callback)) {
|
||||
$this->processPageScript(function (
|
||||
int $PAGE_NUM,
|
||||
int $PAGE_COUNT,
|
||||
self $pdf,
|
||||
FontMetrics $fontMetrics
|
||||
) use ($callback) {
|
||||
eval($callback);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
$this->processPageScript($callback);
|
||||
}
|
||||
|
||||
public function page_text($x, $y, $text, $font, $size, $color = [0, 0, 0], $word_space = 0.0, $char_space = 0.0, $angle = 0.0)
|
||||
{
|
||||
$this->processPageScript(function (int $pageNumber, int $pageCount) use ($x, $y, $text, $font, $size, $color, $word_space, $char_space, $angle) {
|
||||
$text = str_replace(
|
||||
["{PAGE_NUM}", "{PAGE_COUNT}"],
|
||||
[$pageNumber, $pageCount],
|
||||
$text
|
||||
);
|
||||
$this->text($x, $y, $text, $font, $size, $color, $word_space, $char_space, $angle);
|
||||
});
|
||||
}
|
||||
|
||||
public function page_line($x1, $y1, $x2, $y2, $color, $width, $style = [])
|
||||
{
|
||||
$this->processPageScript(function () use ($x1, $y1, $x2, $y2, $color, $width, $style) {
|
||||
$this->line($x1, $y1, $x2, $y2, $color, $width, $style);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function new_page()
|
||||
{
|
||||
$this->_page_number++;
|
||||
$this->_page_count++;
|
||||
|
||||
$ret = $this->_pdf->newPage();
|
||||
$this->_pages[] = $ret;
|
||||
return $ret;
|
||||
}
|
||||
|
||||
protected function processPageScript(callable $callback): void
|
||||
{
|
||||
$pageNumber = 1;
|
||||
|
||||
foreach ($this->_pages as $pid) {
|
||||
$this->reopen_object($pid);
|
||||
|
||||
$fontMetrics = $this->_dompdf->getFontMetrics();
|
||||
$callback($pageNumber, $this->_page_count, $this, $fontMetrics);
|
||||
|
||||
$this->close_object();
|
||||
$pageNumber++;
|
||||
}
|
||||
}
|
||||
|
||||
public function stream($filename = "document.pdf", $options = [])
|
||||
{
|
||||
if (headers_sent()) {
|
||||
die("Unable to stream pdf: headers already sent");
|
||||
}
|
||||
|
||||
if (!isset($options["compress"])) $options["compress"] = true;
|
||||
if (!isset($options["Attachment"])) $options["Attachment"] = true;
|
||||
|
||||
$debug = !$options['compress'];
|
||||
$tmp = ltrim($this->_pdf->output($debug));
|
||||
|
||||
header("Cache-Control: private");
|
||||
header("Content-Type: application/pdf");
|
||||
header("Content-Length: " . mb_strlen($tmp, "8bit"));
|
||||
|
||||
$filename = str_replace(["\n", "'"], "", basename($filename, ".pdf")) . ".pdf";
|
||||
$attachment = $options["Attachment"] ? "attachment" : "inline";
|
||||
header(Helpers::buildContentDispositionHeader($attachment, $filename));
|
||||
|
||||
echo $tmp;
|
||||
flush();
|
||||
}
|
||||
|
||||
public function output($options = [])
|
||||
{
|
||||
if (!isset($options["compress"])) $options["compress"] = true;
|
||||
|
||||
$debug = !$options['compress'];
|
||||
|
||||
return $this->_pdf->output($debug);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns logging messages generated by the Cpdf class
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_messages()
|
||||
{
|
||||
return $this->_pdf->messages;
|
||||
}
|
||||
}
|
||||
929
system/vendor/pancakeapp/dompdf/src/Adapter/GD.php
vendored
Executable file
929
system/vendor/pancakeapp/dompdf/src/Adapter/GD.php
vendored
Executable file
@@ -0,0 +1,929 @@
|
||||
<?php
|
||||
/**
|
||||
* @package dompdf
|
||||
* @link https://github.com/dompdf/dompdf
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
*/
|
||||
namespace Dompdf\Adapter;
|
||||
|
||||
use Dompdf\Canvas;
|
||||
use Dompdf\Dompdf;
|
||||
use Dompdf\Helpers;
|
||||
use Dompdf\Image\Cache;
|
||||
|
||||
/**
|
||||
* 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 implements Canvas
|
||||
{
|
||||
/**
|
||||
* @var Dompdf
|
||||
*/
|
||||
protected $_dompdf;
|
||||
|
||||
/**
|
||||
* Resource handle for the image
|
||||
*
|
||||
* @var \GdImage|resource
|
||||
*/
|
||||
protected $_img;
|
||||
|
||||
/**
|
||||
* Resource handle for the image
|
||||
*
|
||||
* @var \GdImage[]|resource[]
|
||||
*/
|
||||
protected $_imgs;
|
||||
|
||||
/**
|
||||
* Apparent canvas width in pixels
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $_width;
|
||||
|
||||
/**
|
||||
* Apparent canvas height in pixels
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $_height;
|
||||
|
||||
/**
|
||||
* Actual image width in pixels
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $_actual_width;
|
||||
|
||||
/**
|
||||
* Actual image height in pixels
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $_actual_height;
|
||||
|
||||
/**
|
||||
* Current page number
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $_page_number;
|
||||
|
||||
/**
|
||||
* Total number of pages
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $_page_count;
|
||||
|
||||
/**
|
||||
* Image antialias factor
|
||||
*
|
||||
* @var float
|
||||
*/
|
||||
protected $_aa_factor;
|
||||
|
||||
/**
|
||||
* Allocated colors
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $_colors;
|
||||
|
||||
/**
|
||||
* Background color
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $_bg_color;
|
||||
|
||||
/**
|
||||
* Background color array
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $_bg_color_array;
|
||||
|
||||
/**
|
||||
* Actual DPI
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $dpi;
|
||||
|
||||
/**
|
||||
* Amount to scale font sizes
|
||||
*
|
||||
* Font sizes are 72 DPI, GD internally uses 96. Scale them proportionally.
|
||||
* 72 / 96 = 0.75.
|
||||
*
|
||||
* @var float
|
||||
*/
|
||||
const FONT_SCALE = 0.75;
|
||||
|
||||
/**
|
||||
* @param string|float[] $paper The paper size to use as either a standard paper size (see {@link CPDF::$PAPER_SIZES}) or
|
||||
* an array of the form `[x1, y1, x2, y2]` (typically `[0, 0, width, height]`).
|
||||
* @param string $orientation The paper orientation, either `portrait` or `landscape`.
|
||||
* @param Dompdf|null $dompdf The Dompdf instance.
|
||||
* @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
|
||||
*/
|
||||
public function __construct($paper = "letter", string $orientation = "portrait", ?Dompdf $dompdf = null, float $aa_factor = 1.0, array $bg_color = [1, 1, 1, 0])
|
||||
{
|
||||
if (is_array($paper)) {
|
||||
$size = array_map("floatval", $paper);
|
||||
} else {
|
||||
$paper = strtolower($paper);
|
||||
$size = CPDF::$PAPER_SIZES[$paper] ?? CPDF::$PAPER_SIZES["letter"];
|
||||
}
|
||||
|
||||
if (strtolower($orientation) === "landscape") {
|
||||
[$size[2], $size[3]] = [$size[3], $size[2]];
|
||||
}
|
||||
|
||||
if ($dompdf === null) {
|
||||
$this->_dompdf = new Dompdf();
|
||||
} else {
|
||||
$this->_dompdf = $dompdf;
|
||||
}
|
||||
|
||||
$this->dpi = $this->get_dompdf()->getOptions()->getDpi();
|
||||
|
||||
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->_actual_width = $this->_upscale($this->_width);
|
||||
$this->_actual_height = $this->_upscale($this->_height);
|
||||
|
||||
$this->_page_number = $this->_page_count = 0;
|
||||
|
||||
if (is_null($bg_color) || !is_array($bg_color)) {
|
||||
// Pure white bg
|
||||
$bg_color = [1, 1, 1, 0];
|
||||
}
|
||||
|
||||
$this->_bg_color_array = $bg_color;
|
||||
|
||||
$this->new_page();
|
||||
}
|
||||
|
||||
public function get_dompdf()
|
||||
{
|
||||
return $this->_dompdf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the GD image resource
|
||||
*
|
||||
* @return \GdImage|resource
|
||||
*/
|
||||
public function get_image()
|
||||
{
|
||||
return $this->_img;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the image's width in pixels
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function get_width()
|
||||
{
|
||||
return round($this->_width / $this->_aa_factor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the image's height in pixels
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function get_height()
|
||||
{
|
||||
return round($this->_height / $this->_aa_factor);
|
||||
}
|
||||
|
||||
public function get_page_number()
|
||||
{
|
||||
return $this->_page_number;
|
||||
}
|
||||
|
||||
public function get_page_count()
|
||||
{
|
||||
return $this->_page_count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current page number
|
||||
*
|
||||
* @param int $num
|
||||
*/
|
||||
public function set_page_number($num)
|
||||
{
|
||||
$this->_page_number = $num;
|
||||
}
|
||||
|
||||
public function set_page_count($count)
|
||||
{
|
||||
$this->_page_count = $count;
|
||||
}
|
||||
|
||||
public function set_opacity(float $opacity, string $mode = "Normal"): void
|
||||
{
|
||||
// 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
|
||||
*/
|
||||
protected function _allocate_color($color)
|
||||
{
|
||||
$a = isset($color["alpha"]) ? $color["alpha"] : 1;
|
||||
|
||||
if (isset($color["c"])) {
|
||||
$color = Helpers::cmyk_to_rgb($color);
|
||||
}
|
||||
|
||||
list($r, $g, $b) = $color;
|
||||
|
||||
$r = round($r * 255);
|
||||
$g = round($g * 255);
|
||||
$b = round($b * 255);
|
||||
$a = round(127 - ($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->get_image(), $r, $g, $b, $a);
|
||||
} else {
|
||||
$this->_colors[$key] = imagecolorallocate($this->get_image(), $r, $g, $b);
|
||||
}
|
||||
|
||||
return $this->_colors[$key];
|
||||
}
|
||||
|
||||
/**
|
||||
* Scales value up to the current canvas DPI from 72 DPI
|
||||
*
|
||||
* @param float $length
|
||||
* @return int
|
||||
*/
|
||||
protected function _upscale($length)
|
||||
{
|
||||
return round(($length * $this->dpi) / 72 * $this->_aa_factor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scales value down from the current canvas DPI to 72 DPI
|
||||
*
|
||||
* @param float $length
|
||||
* @return float
|
||||
*/
|
||||
protected function _downscale($length)
|
||||
{
|
||||
return round(($length / $this->dpi * 72) / $this->_aa_factor);
|
||||
}
|
||||
|
||||
protected function convertStyle(array $style, int $color, int $width): array
|
||||
{
|
||||
$gdStyle = [];
|
||||
|
||||
if (count($style) === 1) {
|
||||
$style[] = $style[0];
|
||||
}
|
||||
|
||||
foreach ($style as $index => $s) {
|
||||
$d = $this->_upscale($s);
|
||||
|
||||
for ($i = 0; $i < $d; $i++) {
|
||||
for ($j = 0; $j < $width; $j++) {
|
||||
$gdStyle[] = $index % 2 === 0
|
||||
? $color
|
||||
: IMG_COLOR_TRANSPARENT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $gdStyle;
|
||||
}
|
||||
|
||||
public function line($x1, $y1, $x2, $y2, $color, $width, $style = [], $cap = "butt")
|
||||
{
|
||||
// Account for the fact that round and square caps are expected to
|
||||
// extend outwards
|
||||
if ($cap === "round" || $cap === "square") {
|
||||
// Shift line by half width
|
||||
$w = $width / 2;
|
||||
$a = $x2 - $x1;
|
||||
$b = $y2 - $y1;
|
||||
$c = sqrt($a ** 2 + $b ** 2);
|
||||
$dx = $a * $w / $c;
|
||||
$dy = $b * $w / $c;
|
||||
|
||||
$x1 -= $dx;
|
||||
$x2 -= $dx;
|
||||
$y1 -= $dy;
|
||||
$y2 -= $dy;
|
||||
|
||||
// Adapt dash pattern
|
||||
if (is_array($style)) {
|
||||
foreach ($style as $index => &$s) {
|
||||
$s = $index % 2 === 0 ? $s + $width : $s - $width;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Scale by the AA factor and DPI
|
||||
$x1 = $this->_upscale($x1);
|
||||
$y1 = $this->_upscale($y1);
|
||||
$x2 = $this->_upscale($x2);
|
||||
$y2 = $this->_upscale($y2);
|
||||
$width = $this->_upscale($width);
|
||||
|
||||
$c = $this->_allocate_color($color);
|
||||
|
||||
// Convert the style array if required
|
||||
if (is_array($style) && count($style) > 0) {
|
||||
$gd_style = $this->convertStyle($style, $c, $width);
|
||||
|
||||
if (!empty($gd_style)) {
|
||||
imagesetstyle($this->get_image(), $gd_style);
|
||||
$c = IMG_COLOR_STYLED;
|
||||
}
|
||||
}
|
||||
|
||||
imagesetthickness($this->get_image(), $width);
|
||||
|
||||
imageline($this->get_image(), $x1, $y1, $x2, $y2, $c);
|
||||
}
|
||||
|
||||
public function arc($x, $y, $r1, $r2, $astart, $aend, $color, $width, $style = [], $cap = "butt")
|
||||
{
|
||||
// Account for the fact that round and square caps are expected to
|
||||
// extend outwards
|
||||
if ($cap === "round" || $cap === "square") {
|
||||
// Adapt dash pattern
|
||||
if (is_array($style)) {
|
||||
foreach ($style as $index => &$s) {
|
||||
$s = $index % 2 === 0 ? $s + $width : $s - $width;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Scale by the AA factor and DPI
|
||||
$x = $this->_upscale($x);
|
||||
$y = $this->_upscale($y);
|
||||
$w = $this->_upscale($r1 * 2);
|
||||
$h = $this->_upscale($r2 * 2);
|
||||
$width = $this->_upscale($width);
|
||||
|
||||
// Adapt angles as imagearc counts clockwise
|
||||
$start = 360 - $aend;
|
||||
$end = 360 - $astart;
|
||||
|
||||
$c = $this->_allocate_color($color);
|
||||
|
||||
// Convert the style array if required
|
||||
if (is_array($style) && count($style) > 0) {
|
||||
$gd_style = $this->convertStyle($style, $c, $width);
|
||||
|
||||
if (!empty($gd_style)) {
|
||||
imagesetstyle($this->get_image(), $gd_style);
|
||||
$c = IMG_COLOR_STYLED;
|
||||
}
|
||||
}
|
||||
|
||||
imagesetthickness($this->get_image(), $width);
|
||||
|
||||
imagearc($this->get_image(), $x, $y, $w, $h, $start, $end, $c);
|
||||
}
|
||||
|
||||
public function rectangle($x1, $y1, $w, $h, $color, $width, $style = [], $cap = "butt")
|
||||
{
|
||||
// Account for the fact that round and square caps are expected to
|
||||
// extend outwards
|
||||
if ($cap === "round" || $cap === "square") {
|
||||
// Adapt dash pattern
|
||||
if (is_array($style)) {
|
||||
foreach ($style as $index => &$s) {
|
||||
$s = $index % 2 === 0 ? $s + $width : $s - $width;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Scale by the AA factor and DPI
|
||||
$x1 = $this->_upscale($x1);
|
||||
$y1 = $this->_upscale($y1);
|
||||
$w = $this->_upscale($w);
|
||||
$h = $this->_upscale($h);
|
||||
$width = $this->_upscale($width);
|
||||
|
||||
$c = $this->_allocate_color($color);
|
||||
|
||||
// Convert the style array if required
|
||||
if (is_array($style) && count($style) > 0) {
|
||||
$gd_style = $this->convertStyle($style, $c, $width);
|
||||
|
||||
if (!empty($gd_style)) {
|
||||
imagesetstyle($this->get_image(), $gd_style);
|
||||
$c = IMG_COLOR_STYLED;
|
||||
}
|
||||
}
|
||||
|
||||
imagesetthickness($this->get_image(), $width);
|
||||
|
||||
if ($c === IMG_COLOR_STYLED) {
|
||||
imagepolygon($this->get_image(), [
|
||||
$x1, $y1,
|
||||
$x1 + $w, $y1,
|
||||
$x1 + $w, $y1 + $h,
|
||||
$x1, $y1 + $h
|
||||
], $c);
|
||||
} else {
|
||||
imagerectangle($this->get_image(), $x1, $y1, $x1 + $w, $y1 + $h, $c);
|
||||
}
|
||||
}
|
||||
|
||||
public function filled_rectangle($x1, $y1, $w, $h, $color)
|
||||
{
|
||||
// Scale by the AA factor and DPI
|
||||
$x1 = $this->_upscale($x1);
|
||||
$y1 = $this->_upscale($y1);
|
||||
$w = $this->_upscale($w);
|
||||
$h = $this->_upscale($h);
|
||||
|
||||
$c = $this->_allocate_color($color);
|
||||
|
||||
imagefilledrectangle($this->get_image(), $x1, $y1, $x1 + $w, $y1 + $h, $c);
|
||||
}
|
||||
|
||||
public function clipping_rectangle($x1, $y1, $w, $h)
|
||||
{
|
||||
// @todo
|
||||
}
|
||||
|
||||
public function clipping_roundrectangle($x1, $y1, $w, $h, $rTL, $rTR, $rBR, $rBL)
|
||||
{
|
||||
// @todo
|
||||
}
|
||||
|
||||
public function clipping_polygon(array $points): void
|
||||
{
|
||||
// @todo
|
||||
}
|
||||
|
||||
public function clipping_end()
|
||||
{
|
||||
// @todo
|
||||
}
|
||||
|
||||
public function save()
|
||||
{
|
||||
$this->get_dompdf()->getOptions()->setDpi(72);
|
||||
}
|
||||
|
||||
public function restore()
|
||||
{
|
||||
$this->get_dompdf()->getOptions()->setDpi($this->dpi);
|
||||
}
|
||||
|
||||
public function rotate($angle, $x, $y)
|
||||
{
|
||||
// @todo
|
||||
}
|
||||
|
||||
public function skew($angle_x, $angle_y, $x, $y)
|
||||
{
|
||||
// @todo
|
||||
}
|
||||
|
||||
public function scale($s_x, $s_y, $x, $y)
|
||||
{
|
||||
// @todo
|
||||
}
|
||||
|
||||
public function translate($t_x, $t_y)
|
||||
{
|
||||
// @todo
|
||||
}
|
||||
|
||||
public function transform($a, $b, $c, $d, $e, $f)
|
||||
{
|
||||
// @todo
|
||||
}
|
||||
|
||||
public function polygon($points, $color, $width = null, $style = [], $fill = false)
|
||||
{
|
||||
// Scale each point by the AA factor and DPI
|
||||
foreach (array_keys($points) as $i) {
|
||||
$points[$i] = $this->_upscale($points[$i]);
|
||||
}
|
||||
|
||||
$width = isset($width) ? $this->_upscale($width) : null;
|
||||
|
||||
$c = $this->_allocate_color($color);
|
||||
|
||||
// Convert the style array if required
|
||||
if (is_array($style) && count($style) > 0 && isset($width) && !$fill) {
|
||||
$gd_style = $this->convertStyle($style, $c, $width);
|
||||
|
||||
if (!empty($gd_style)) {
|
||||
imagesetstyle($this->get_image(), $gd_style);
|
||||
$c = IMG_COLOR_STYLED;
|
||||
}
|
||||
}
|
||||
|
||||
imagesetthickness($this->get_image(), isset($width) ? $width : 0);
|
||||
|
||||
if ($fill) {
|
||||
imagefilledpolygon($this->get_image(), $points, $c);
|
||||
} else {
|
||||
imagepolygon($this->get_image(), $points, $c);
|
||||
}
|
||||
}
|
||||
|
||||
public function circle($x, $y, $r, $color, $width = null, $style = [], $fill = false)
|
||||
{
|
||||
// Scale by the AA factor and DPI
|
||||
$x = $this->_upscale($x);
|
||||
$y = $this->_upscale($y);
|
||||
$d = $this->_upscale(2 * $r);
|
||||
$width = isset($width) ? $this->_upscale($width) : null;
|
||||
|
||||
$c = $this->_allocate_color($color);
|
||||
|
||||
// Convert the style array if required
|
||||
if (is_array($style) && count($style) > 0 && isset($width) && !$fill) {
|
||||
$gd_style = $this->convertStyle($style, $c, $width);
|
||||
|
||||
if (!empty($gd_style)) {
|
||||
imagesetstyle($this->get_image(), $gd_style);
|
||||
$c = IMG_COLOR_STYLED;
|
||||
}
|
||||
}
|
||||
|
||||
imagesetthickness($this->get_image(), isset($width) ? $width : 0);
|
||||
|
||||
if ($fill) {
|
||||
imagefilledellipse($this->get_image(), $x, $y, $d, $d, $c);
|
||||
} else {
|
||||
imageellipse($this->get_image(), $x, $y, $d, $d, $c);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function image($img, $x, $y, $w, $h, $resolution = "normal")
|
||||
{
|
||||
$img_type = Cache::detect_type($img, $this->get_dompdf()->getHttpContext());
|
||||
|
||||
if (!$img_type) {
|
||||
return;
|
||||
}
|
||||
|
||||
$func_name = "imagecreatefrom$img_type";
|
||||
if (!function_exists($func_name)) {
|
||||
if (!method_exists(Helpers::class, $func_name)) {
|
||||
throw new \Exception("Function $func_name() not found. Cannot convert $img_type image: $img. Please install the image PHP extension.");
|
||||
}
|
||||
$func_name = [Helpers::class, $func_name];
|
||||
}
|
||||
$src = @call_user_func($func_name, $img);
|
||||
|
||||
if (!$src) {
|
||||
return; // Probably should add to $_dompdf_errors or whatever here
|
||||
}
|
||||
|
||||
// Scale by the AA factor and DPI
|
||||
$x = $this->_upscale($x);
|
||||
$y = $this->_upscale($y);
|
||||
|
||||
$w = $this->_upscale($w);
|
||||
$h = $this->_upscale($h);
|
||||
|
||||
$img_w = imagesx($src);
|
||||
$img_h = imagesy($src);
|
||||
|
||||
imagecopyresampled($this->get_image(), $src, $x, $y, 0, 0, $w, $h, $img_w, $img_h);
|
||||
}
|
||||
|
||||
public function text($x, $y, $text, $font, $size, $color = [0, 0, 0], $word_spacing = 0.0, $char_spacing = 0.0, $angle = 0.0)
|
||||
{
|
||||
// Scale by the AA factor and DPI
|
||||
$x = $this->_upscale($x);
|
||||
$y = $this->_upscale($y);
|
||||
$size = $this->_upscale($size) * self::FONT_SCALE;
|
||||
|
||||
$h = round($this->get_font_height_actual($font, $size));
|
||||
$c = $this->_allocate_color($color);
|
||||
|
||||
// imagettftext() converts numeric entities to their respective
|
||||
// character. Preserve any originally double encoded entities to be
|
||||
// represented as is.
|
||||
// eg: &#160; will render   rather than its character.
|
||||
$text = preg_replace('/&(#(?:x[a-fA-F0-9]+|[0-9]+);)/', '&\1', $text);
|
||||
|
||||
$text = mb_encode_numericentity($text, [0x0080, 0xff, 0, 0xff], 'UTF-8');
|
||||
|
||||
$font = $this->get_ttf_file($font);
|
||||
|
||||
// FIXME: word spacing
|
||||
imagettftext($this->get_image(), $size, $angle, $x, $y + $h, $c, $font, $text);
|
||||
}
|
||||
|
||||
public function javascript($code)
|
||||
{
|
||||
// Not implemented
|
||||
}
|
||||
|
||||
public function add_named_dest($anchorname)
|
||||
{
|
||||
// Not implemented
|
||||
}
|
||||
|
||||
public function add_link($url, $x, $y, $width, $height)
|
||||
{
|
||||
// Not implemented
|
||||
}
|
||||
|
||||
public function add_info(string $label, string $value): void
|
||||
{
|
||||
// N/A
|
||||
}
|
||||
|
||||
public function set_default_view($view, $options = [])
|
||||
{
|
||||
// N/A
|
||||
}
|
||||
|
||||
public function get_text_width($text, $font, $size, $word_spacing = 0.0, $char_spacing = 0.0)
|
||||
{
|
||||
$font = $this->get_ttf_file($font);
|
||||
$size = $this->_upscale($size) * self::FONT_SCALE;
|
||||
|
||||
// imagettfbbox() converts numeric entities to their respective
|
||||
// character. Preserve any originally double encoded entities to be
|
||||
// represented as is.
|
||||
// eg: &#160; will render   rather than its character.
|
||||
$text = preg_replace('/&(#(?:x[a-fA-F0-9]+|[0-9]+);)/', '&\1', $text);
|
||||
|
||||
$text = mb_encode_numericentity($text, [0x0080, 0xffff, 0, 0xffff], 'UTF-8');
|
||||
|
||||
// FIXME: word spacing
|
||||
list($x1, , $x2) = imagettfbbox($size, 0, $font, $text);
|
||||
|
||||
// Add additional 1pt to prevent text overflow issues
|
||||
return $this->_downscale($x2 - $x1) + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $font
|
||||
* @return string
|
||||
*/
|
||||
public function get_ttf_file($font)
|
||||
{
|
||||
if ($font === null) {
|
||||
$font = "";
|
||||
}
|
||||
|
||||
if ( stripos($font, ".ttf") === false ) {
|
||||
$font .= ".ttf";
|
||||
}
|
||||
|
||||
if (!file_exists($font)) {
|
||||
$font_metrics = $this->_dompdf->getFontMetrics();
|
||||
$font = $font_metrics->getFont($this->_dompdf->getOptions()->getDefaultFont()) . ".ttf";
|
||||
if (!file_exists($font)) {
|
||||
if (strpos($font, "mono")) {
|
||||
$font = $font_metrics->getFont("DejaVu Mono") . ".ttf";
|
||||
} elseif (strpos($font, "sans") !== false) {
|
||||
$font = $font_metrics->getFont("DejaVu Sans") . ".ttf";
|
||||
} elseif (strpos($font, "serif")) {
|
||||
$font = $font_metrics->getFont("DejaVu Serif") . ".ttf";
|
||||
} else {
|
||||
$font = $font_metrics->getFont("DejaVu Sans") . ".ttf";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $font;
|
||||
}
|
||||
|
||||
public function get_font_height($font, $size)
|
||||
{
|
||||
$size = $this->_upscale($size) * self::FONT_SCALE;
|
||||
|
||||
$height = $this->get_font_height_actual($font, $size);
|
||||
|
||||
return $this->_downscale($height);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $font
|
||||
* @param float $size
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
protected function get_font_height_actual($font, $size)
|
||||
{
|
||||
$font = $this->get_ttf_file($font);
|
||||
$ratio = $this->_dompdf->getOptions()->getFontHeightRatio();
|
||||
|
||||
// FIXME: word spacing
|
||||
list(, $y2, , , , $y1) = imagettfbbox($size, 0, $font, "MXjpqytfhl"); // Test string with ascenders, descenders and caps
|
||||
return ($y2 - $y1) * $ratio;
|
||||
}
|
||||
|
||||
public function get_font_baseline($font, $size)
|
||||
{
|
||||
$ratio = $this->_dompdf->getOptions()->getFontHeightRatio();
|
||||
return $this->get_font_height($font, $size) / $ratio;
|
||||
}
|
||||
|
||||
public function new_page()
|
||||
{
|
||||
$this->_page_number++;
|
||||
$this->_page_count++;
|
||||
|
||||
$this->_img = imagecreatetruecolor($this->_actual_width, $this->_actual_height);
|
||||
|
||||
$this->_bg_color = $this->_allocate_color($this->_bg_color_array);
|
||||
imagealphablending($this->_img, true);
|
||||
imagesavealpha($this->_img, true);
|
||||
imagefill($this->_img, 0, 0, $this->_bg_color);
|
||||
|
||||
$this->_imgs[] = $this->_img;
|
||||
}
|
||||
|
||||
public function open_object()
|
||||
{
|
||||
// N/A
|
||||
}
|
||||
|
||||
public function close_object()
|
||||
{
|
||||
// N/A
|
||||
}
|
||||
|
||||
public function add_object()
|
||||
{
|
||||
// N/A
|
||||
}
|
||||
|
||||
public function page_script($callback): void
|
||||
{
|
||||
// N/A
|
||||
}
|
||||
|
||||
public function page_text($x, $y, $text, $font, $size, $color = [0, 0, 0], $word_space = 0.0, $char_space = 0.0, $angle = 0.0)
|
||||
{
|
||||
// N/A
|
||||
}
|
||||
|
||||
public function page_line($x1, $y1, $x2, $y2, $color, $width, $style = [])
|
||||
{
|
||||
// N/A
|
||||
}
|
||||
|
||||
/**
|
||||
* Streams the image to the client.
|
||||
*
|
||||
* @param string $filename The filename to present to the client.
|
||||
* @param array $options Associative array: 'type' => jpeg|jpg|png; 'quality' => 0 - 100 (JPEG only);
|
||||
* 'page' => Number of the page to output (defaults to the first); 'Attachment': 1 or 0 (default 1).
|
||||
*/
|
||||
public function stream($filename, $options = [])
|
||||
{
|
||||
if (headers_sent()) {
|
||||
die("Unable to stream image: headers already sent");
|
||||
}
|
||||
|
||||
if (!isset($options["type"])) $options["type"] = "png";
|
||||
if (!isset($options["Attachment"])) $options["Attachment"] = true;
|
||||
$type = strtolower($options["type"]);
|
||||
|
||||
switch ($type) {
|
||||
case "jpg":
|
||||
case "jpeg":
|
||||
$contentType = "image/jpeg";
|
||||
$extension = ".jpg";
|
||||
break;
|
||||
case "png":
|
||||
default:
|
||||
$contentType = "image/png";
|
||||
$extension = ".png";
|
||||
break;
|
||||
}
|
||||
|
||||
header("Cache-Control: private");
|
||||
header("Content-Type: $contentType");
|
||||
|
||||
$filename = str_replace(["\n", "'"], "", basename($filename, ".$type")) . $extension;
|
||||
$attachment = $options["Attachment"] ? "attachment" : "inline";
|
||||
header(Helpers::buildContentDispositionHeader($attachment, $filename));
|
||||
|
||||
$this->_output($options);
|
||||
flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the image as a string.
|
||||
*
|
||||
* @param array $options Associative array: 'type' => jpeg|jpg|png; 'quality' => 0 - 100 (JPEG only);
|
||||
* 'page' => Number of the page to output (defaults to the first).
|
||||
* @return string
|
||||
*/
|
||||
public function output($options = [])
|
||||
{
|
||||
ob_start();
|
||||
|
||||
$this->_output($options);
|
||||
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs the image stream directly.
|
||||
*
|
||||
* @param array $options Associative array: 'type' => jpeg|jpg|png; 'quality' => 0 - 100 (JPEG only);
|
||||
* 'page' => Number of the page to output (defaults to the first).
|
||||
*/
|
||||
protected function _output($options = [])
|
||||
{
|
||||
if (!isset($options["type"])) $options["type"] = "png";
|
||||
if (!isset($options["page"])) $options["page"] = 1;
|
||||
$type = strtolower($options["type"]);
|
||||
|
||||
if (isset($this->_imgs[$options["page"] - 1])) {
|
||||
$img = $this->_imgs[$options["page"] - 1];
|
||||
} else {
|
||||
$img = $this->_imgs[0];
|
||||
}
|
||||
|
||||
// Perform any antialiasing
|
||||
if ($this->_aa_factor != 1) {
|
||||
$dst_w = round($this->_actual_width / $this->_aa_factor);
|
||||
$dst_h = round($this->_actual_height / $this->_aa_factor);
|
||||
$dst = imagecreatetruecolor($dst_w, $dst_h);
|
||||
imagecopyresampled($dst, $img, 0, 0, 0, 0,
|
||||
$dst_w, $dst_h,
|
||||
$this->_actual_width, $this->_actual_height);
|
||||
} else {
|
||||
$dst = $img;
|
||||
}
|
||||
|
||||
switch ($type) {
|
||||
case "jpg":
|
||||
case "jpeg":
|
||||
if (!isset($options["quality"])) {
|
||||
$options["quality"] = 75;
|
||||
}
|
||||
|
||||
imagejpeg($dst, null, $options["quality"]);
|
||||
break;
|
||||
case "png":
|
||||
default:
|
||||
imagepng($dst);
|
||||
break;
|
||||
}
|
||||
|
||||
if ($this->_aa_factor != 1) {
|
||||
imagedestroy($dst);
|
||||
}
|
||||
}
|
||||
}
|
||||
1450
system/vendor/pancakeapp/dompdf/src/Adapter/PDFLib.php
vendored
Executable file
1450
system/vendor/pancakeapp/dompdf/src/Adapter/PDFLib.php
vendored
Executable file
File diff suppressed because it is too large
Load Diff
477
system/vendor/pancakeapp/dompdf/src/Canvas.php
vendored
Executable file
477
system/vendor/pancakeapp/dompdf/src/Canvas.php
vendored
Executable file
@@ -0,0 +1,477 @@
|
||||
<?php
|
||||
/**
|
||||
* @package dompdf
|
||||
* @link https://github.com/dompdf/dompdf
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
*/
|
||||
namespace Dompdf;
|
||||
|
||||
/**
|
||||
* Main rendering interface
|
||||
*
|
||||
* Currently {@link Dompdf\Adapter\CPDF}, {@link Dompdf\Adapter\PDFLib}, and {@link Dompdf\Adapter\GD}
|
||||
* 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
|
||||
{
|
||||
/**
|
||||
* @param string|float[] $paper The paper size to use as either a standard paper size (see {@link Dompdf\Adapter\CPDF::$PAPER_SIZES})
|
||||
* or an array of the form `[x1, y1, x2, y2]` (typically `[0, 0, width, height]`).
|
||||
* @param string $orientation The paper orientation, either `portrait` or `landscape`.
|
||||
* @param Dompdf|null $dompdf The Dompdf instance.
|
||||
*/
|
||||
public function __construct($paper = "letter", string $orientation = "portrait", ?Dompdf $dompdf = null);
|
||||
|
||||
/**
|
||||
* @return Dompdf
|
||||
*/
|
||||
function get_dompdf();
|
||||
|
||||
/**
|
||||
* Returns the current page number
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
function get_page_number();
|
||||
|
||||
/**
|
||||
* Returns the total number of pages in the document
|
||||
*
|
||||
* @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 Cpdf::setLineStyle()} for a description of the format of the
|
||||
* $style and $cap parameters (aka dash and cap).
|
||||
*
|
||||
* @param float $x1
|
||||
* @param float $y1
|
||||
* @param float $x2
|
||||
* @param float $y2
|
||||
* @param array $color Color array in the format `[r, g, b, "alpha" => alpha]`
|
||||
* where r, g, b, and alpha are float values between 0 and 1
|
||||
* @param float $width
|
||||
* @param array $style
|
||||
* @param string $cap `butt`, `round`, or `square`
|
||||
*/
|
||||
function line($x1, $y1, $x2, $y2, $color, $width, $style = [], $cap = "butt");
|
||||
|
||||
/**
|
||||
* Draws an arc
|
||||
*
|
||||
* See {@link Cpdf::setLineStyle()} for a description of the format of the
|
||||
* $style and $cap parameters (aka dash and cap).
|
||||
*
|
||||
* @param float $x X coordinate of the arc
|
||||
* @param float $y Y coordinate of the arc
|
||||
* @param float $r1 Radius 1
|
||||
* @param float $r2 Radius 2
|
||||
* @param float $astart Start angle in degrees
|
||||
* @param float $aend End angle in degrees
|
||||
* @param array $color Color array in the format `[r, g, b, "alpha" => alpha]`
|
||||
* where r, g, b, and alpha are float values between 0 and 1
|
||||
* @param float $width
|
||||
* @param array $style
|
||||
* @param string $cap `butt`, `round`, or `square`
|
||||
*/
|
||||
function arc($x, $y, $r1, $r2, $astart, $aend, $color, $width, $style = [], $cap = "butt");
|
||||
|
||||
/**
|
||||
* Draws a rectangle at x1,y1 with width w and height h
|
||||
*
|
||||
* See {@link Cpdf::setLineStyle()} for a description of the format of the
|
||||
* $style and $cap parameters (aka dash and cap).
|
||||
*
|
||||
* @param float $x1
|
||||
* @param float $y1
|
||||
* @param float $w
|
||||
* @param float $h
|
||||
* @param array $color Color array in the format `[r, g, b, "alpha" => alpha]`
|
||||
* where r, g, b, and alpha are float values between 0 and 1
|
||||
* @param float $width
|
||||
* @param array $style
|
||||
* @param string $cap `butt`, `round`, or `square`
|
||||
*/
|
||||
function rectangle($x1, $y1, $w, $h, $color, $width, $style = [], $cap = "butt");
|
||||
|
||||
/**
|
||||
* Draws a filled rectangle at x1,y1 with width w and height h
|
||||
*
|
||||
* @param float $x1
|
||||
* @param float $y1
|
||||
* @param float $w
|
||||
* @param float $h
|
||||
* @param array $color Color array in the format `[r, g, b, "alpha" => alpha]`
|
||||
* where r, g, b, and alpha are float values between 0 and 1
|
||||
*/
|
||||
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);
|
||||
|
||||
/**
|
||||
* Starts a rounded clipping rectangle at x1,y1 with width w and height h
|
||||
*
|
||||
* @param float $x1
|
||||
* @param float $y1
|
||||
* @param float $w
|
||||
* @param float $h
|
||||
* @param float $tl
|
||||
* @param float $tr
|
||||
* @param float $br
|
||||
* @param float $bl
|
||||
*/
|
||||
function clipping_roundrectangle($x1, $y1, $w, $h, $tl, $tr, $br, $bl);
|
||||
|
||||
/**
|
||||
* Starts a clipping polygon
|
||||
*
|
||||
* @param float[] $points
|
||||
*/
|
||||
public function clipping_polygon(array $points): void;
|
||||
|
||||
/**
|
||||
* Ends the last clipping shape
|
||||
*/
|
||||
function clipping_end();
|
||||
|
||||
/**
|
||||
* Processes a callback on every page.
|
||||
*
|
||||
* The callback function receives the four parameters `int $pageNumber`,
|
||||
* `int $pageCount`, `Canvas $canvas`, and `FontMetrics $fontMetrics`, in
|
||||
* that order.
|
||||
*
|
||||
* This function can be used to add page numbers to all pages after the
|
||||
* first one, for example.
|
||||
*
|
||||
* @param callable $callback The callback function to process on every page
|
||||
*/
|
||||
public function page_script($callback): void;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @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 Color array in the format `[r, g, b, "alpha" => alpha]`
|
||||
* where r, g, b, and alpha are float values between 0 and 1
|
||||
* @param float $word_space Word spacing adjustment
|
||||
* @param float $char_space Char spacing adjustment
|
||||
* @param float $angle Angle to write the text at, measured clockwise starting from the x-axis
|
||||
*/
|
||||
public function page_text($x, $y, $text, $font, $size, $color = [0, 0, 0], $word_space = 0.0, $char_space = 0.0, $angle = 0.0);
|
||||
|
||||
/**
|
||||
* Draws a line at the specified coordinates on every page.
|
||||
*
|
||||
* @param float $x1
|
||||
* @param float $y1
|
||||
* @param float $x2
|
||||
* @param float $y2
|
||||
* @param array $color Color array in the format `[r, g, b, "alpha" => alpha]`
|
||||
* where r, g, b, and alpha are float values between 0 and 1
|
||||
* @param float $width
|
||||
* @param array $style
|
||||
*/
|
||||
public function page_line($x1, $y1, $x2, $y2, $color, $width, $style = []);
|
||||
|
||||
/**
|
||||
* Save current state
|
||||
*/
|
||||
function save();
|
||||
|
||||
/**
|
||||
* Restore last state
|
||||
*/
|
||||
function restore();
|
||||
|
||||
/**
|
||||
* Rotate
|
||||
*
|
||||
* @param float $angle angle in degrees for counter-clockwise rotation
|
||||
* @param float $x Origin abscissa
|
||||
* @param float $y Origin ordinate
|
||||
*/
|
||||
function rotate($angle, $x, $y);
|
||||
|
||||
/**
|
||||
* Skew
|
||||
*
|
||||
* @param float $angle_x
|
||||
* @param float $angle_y
|
||||
* @param float $x Origin abscissa
|
||||
* @param float $y Origin ordinate
|
||||
*/
|
||||
function skew($angle_x, $angle_y, $x, $y);
|
||||
|
||||
/**
|
||||
* Scale
|
||||
*
|
||||
* @param float $s_x scaling factor for width as percent
|
||||
* @param float $s_y scaling factor for height as percent
|
||||
* @param float $x Origin abscissa
|
||||
* @param float $y Origin ordinate
|
||||
*/
|
||||
function scale($s_x, $s_y, $x, $y);
|
||||
|
||||
/**
|
||||
* Translate
|
||||
*
|
||||
* @param float $t_x movement to the right
|
||||
* @param float $t_y movement to the bottom
|
||||
*/
|
||||
function translate($t_x, $t_y);
|
||||
|
||||
/**
|
||||
* Transform
|
||||
*
|
||||
* @param float $a
|
||||
* @param float $b
|
||||
* @param float $c
|
||||
* @param float $d
|
||||
* @param float $e
|
||||
* @param float $f
|
||||
*/
|
||||
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:
|
||||
* ```
|
||||
* array(0 => x1,
|
||||
* 1 => y1,
|
||||
* 2 => x2,
|
||||
* 3 => y2,
|
||||
* ...
|
||||
* );
|
||||
* ```
|
||||
*
|
||||
* See {@link Cpdf::setLineStyle()} for a description of the format of the
|
||||
* $style parameter (aka dash).
|
||||
*
|
||||
* @param array $points
|
||||
* @param array $color Color array in the format `[r, g, b, "alpha" => alpha]`
|
||||
* where r, g, b, and alpha are float values between 0 and 1
|
||||
* @param float $width
|
||||
* @param array $style
|
||||
* @param bool $fill Fills the polygon if true
|
||||
*/
|
||||
function polygon($points, $color, $width = null, $style = [], $fill = false);
|
||||
|
||||
/**
|
||||
* Draws a circle at $x,$y with radius $r
|
||||
*
|
||||
* See {@link Cpdf::setLineStyle()} for a description of the format of the
|
||||
* $style parameter (aka dash).
|
||||
*
|
||||
* @param float $x
|
||||
* @param float $y
|
||||
* @param float $r
|
||||
* @param array $color Color array in the format `[r, g, b, "alpha" => alpha]`
|
||||
* where r, g, b, and alpha are float values between 0 and 1
|
||||
* @param float $width
|
||||
* @param array $style
|
||||
* @param bool $fill Fills the circle if true
|
||||
*/
|
||||
function circle($x, $y, $r, $color, $width = null, $style = [], $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 The path to the image
|
||||
* @param float $x X position
|
||||
* @param float $y Y position
|
||||
* @param float $w Width
|
||||
* @param float $h Height
|
||||
* @param string $resolution The resolution of the image
|
||||
*/
|
||||
function image($img, $x, $y, $w, $h, $resolution = "normal");
|
||||
|
||||
/**
|
||||
* Writes text at the specified x and y coordinates
|
||||
*
|
||||
* @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 Color array in the format `[r, g, b, "alpha" => alpha]`
|
||||
* where r, g, b, and alpha are float values between 0 and 1
|
||||
* @param float $word_space Word spacing adjustment
|
||||
* @param float $char_space Char spacing adjustment
|
||||
* @param float $angle Angle to write the text at, measured clockwise starting from the x-axis
|
||||
*/
|
||||
function text($x, $y, $text, $font, $size, $color = [0, 0, 0], $word_space = 0.0, $char_space = 0.0, $angle = 0.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
|
||||
*/
|
||||
public function add_info(string $label, string $value): void;
|
||||
|
||||
/**
|
||||
* Calculates text size, in points
|
||||
*
|
||||
* @param string $text The text to be sized
|
||||
* @param string $font The font file to use
|
||||
* @param float $size The font size, in points
|
||||
* @param float $word_spacing Word spacing, if any
|
||||
* @param float $char_spacing Char spacing, if any
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
function get_text_width($text, $font, $size, $word_spacing = 0.0, $char_spacing = 0.0);
|
||||
|
||||
/**
|
||||
* Calculates font height, in points
|
||||
*
|
||||
* @param string $font The font file to use
|
||||
* @param float $size The font size, in points
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
function get_font_height($font, $size);
|
||||
|
||||
/**
|
||||
* Returns the font x-height, in points
|
||||
*
|
||||
* @param string $font The font file to use
|
||||
* @param float $size The font size, in points
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
//function get_font_x_height($font, $size);
|
||||
|
||||
/**
|
||||
* Calculates font baseline, in points
|
||||
*
|
||||
* @param string $font The font file to use
|
||||
* @param float $size The font size, in points
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
function get_font_baseline($font, $size);
|
||||
|
||||
/**
|
||||
* Returns the PDF's width in points
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
function get_width();
|
||||
|
||||
/**
|
||||
* Returns the PDF's height in points
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
function get_height();
|
||||
|
||||
/**
|
||||
* Sets the opacity
|
||||
*
|
||||
* @param float $opacity
|
||||
* @param string $mode
|
||||
*/
|
||||
public function set_opacity(float $opacity, string $mode = "Normal"): void;
|
||||
|
||||
/**
|
||||
* Sets the default view
|
||||
*
|
||||
* @param string $view
|
||||
* 'XYZ' left, top, zoom
|
||||
* 'Fit'
|
||||
* 'FitH' top
|
||||
* 'FitV' left
|
||||
* 'FitR' left,bottom,right
|
||||
* 'FitB'
|
||||
* 'FitBH' top
|
||||
* 'FitBV' left
|
||||
* @param array $options
|
||||
*/
|
||||
function set_default_view($view, $options = []);
|
||||
|
||||
/**
|
||||
* @param string $code
|
||||
*/
|
||||
function javascript($code);
|
||||
|
||||
/**
|
||||
* Starts a new page
|
||||
*
|
||||
* Subsequent drawing operations will appear on the new page.
|
||||
*/
|
||||
function new_page();
|
||||
|
||||
/**
|
||||
* 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).
|
||||
*/
|
||||
function stream($filename, $options = []);
|
||||
|
||||
/**
|
||||
* Returns the PDF as a string.
|
||||
*
|
||||
* @param array $options Associative array: 'compress' => 1 or 0 (default 1).
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function output($options = []);
|
||||
}
|
||||
58
system/vendor/pancakeapp/dompdf/src/CanvasFactory.php
vendored
Executable file
58
system/vendor/pancakeapp/dompdf/src/CanvasFactory.php
vendored
Executable file
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
/**
|
||||
* @package dompdf
|
||||
* @link https://github.com/dompdf/dompdf
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
*/
|
||||
namespace Dompdf;
|
||||
|
||||
/**
|
||||
* Create canvas instances
|
||||
*
|
||||
* The canvas factory creates canvas instances based on the
|
||||
* availability of rendering backends and config options.
|
||||
*
|
||||
* @package dompdf
|
||||
*/
|
||||
class CanvasFactory
|
||||
{
|
||||
/**
|
||||
* Constructor is private: this is a static class
|
||||
*/
|
||||
private function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Dompdf $dompdf
|
||||
* @param string|float[] $paper
|
||||
* @param string $orientation
|
||||
* @param string|null $class
|
||||
*
|
||||
* @return Canvas
|
||||
*/
|
||||
static function get_instance(Dompdf $dompdf, $paper, string $orientation, ?string $class = null)
|
||||
{
|
||||
$backend = strtolower($dompdf->getOptions()->getPdfBackend());
|
||||
|
||||
if (isset($class) && class_exists($class, false)) {
|
||||
$class .= "_Adapter";
|
||||
} else {
|
||||
if (($backend === "auto" || $backend === "pdflib") &&
|
||||
class_exists("PDFLib", false)
|
||||
) {
|
||||
$class = "Dompdf\\Adapter\\PDFLib";
|
||||
}
|
||||
|
||||
else {
|
||||
if ($backend === "gd" && extension_loaded('gd')) {
|
||||
$class = "Dompdf\\Adapter\\GD";
|
||||
} else {
|
||||
$class = "Dompdf\\Adapter\\CPDF";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new $class($paper, $orientation, $dompdf);
|
||||
}
|
||||
}
|
||||
999
system/vendor/pancakeapp/dompdf/src/Cellmap.php
vendored
Executable file
999
system/vendor/pancakeapp/dompdf/src/Cellmap.php
vendored
Executable file
@@ -0,0 +1,999 @@
|
||||
<?php
|
||||
/**
|
||||
* @package dompdf
|
||||
* @link https://github.com/dompdf/dompdf
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
*/
|
||||
namespace Dompdf;
|
||||
|
||||
use Dompdf\FrameDecorator\AbstractFrameDecorator;
|
||||
use Dompdf\FrameDecorator\Table as TableFrameDecorator;
|
||||
use Dompdf\FrameDecorator\TableCell as TableCellFrameDecorator;
|
||||
|
||||
/**
|
||||
* Maps table cells to the table grid.
|
||||
*
|
||||
* This class resolves borders in tables with collapsed borders and helps
|
||||
* place row & column spanned table cells.
|
||||
*
|
||||
* @package dompdf
|
||||
*/
|
||||
class Cellmap
|
||||
{
|
||||
/**
|
||||
* Border style weight lookup for collapsed border resolution.
|
||||
*/
|
||||
protected const BORDER_STYLE_SCORE = [
|
||||
"double" => 8,
|
||||
"solid" => 7,
|
||||
"dashed" => 6,
|
||||
"dotted" => 5,
|
||||
"ridge" => 4,
|
||||
"outset" => 3,
|
||||
"groove" => 2,
|
||||
"inset" => 1,
|
||||
"none" => 0
|
||||
];
|
||||
|
||||
/**
|
||||
* The table object this cellmap is attached to.
|
||||
*
|
||||
* @var TableFrameDecorator
|
||||
*/
|
||||
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 Frame[][]
|
||||
*/
|
||||
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;
|
||||
|
||||
/**
|
||||
* Tells whether the columns' width can be modified
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $_columns_locked = false;
|
||||
|
||||
/**
|
||||
* Tells whether the table has table-layout:fixed
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $_fixed_layout = false;
|
||||
|
||||
/**
|
||||
* @param TableFrameDecorator $table
|
||||
*/
|
||||
public function __construct(TableFrameDecorator $table)
|
||||
{
|
||||
$this->_table = $table;
|
||||
$this->reset();
|
||||
}
|
||||
|
||||
public function reset(): void
|
||||
{
|
||||
$this->_num_rows = 0;
|
||||
$this->_num_cols = 0;
|
||||
|
||||
$this->_cells = [];
|
||||
$this->_frames = [];
|
||||
|
||||
if (!$this->_columns_locked) {
|
||||
$this->_columns = [];
|
||||
}
|
||||
|
||||
$this->_rows = [];
|
||||
|
||||
$this->_borders = [];
|
||||
|
||||
$this->__col = $this->__row = 0;
|
||||
}
|
||||
|
||||
public function lock_columns(): void
|
||||
{
|
||||
$this->_columns_locked = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function is_columns_locked()
|
||||
{
|
||||
return $this->_columns_locked;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $fixed
|
||||
*/
|
||||
public function set_layout_fixed(bool $fixed)
|
||||
{
|
||||
$this->_fixed_layout = $fixed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function is_layout_fixed()
|
||||
{
|
||||
return $this->_fixed_layout;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function get_num_rows()
|
||||
{
|
||||
return $this->_num_rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function get_num_cols()
|
||||
{
|
||||
return $this->_num_cols;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function &get_columns()
|
||||
{
|
||||
return $this->_columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $columns
|
||||
*/
|
||||
public function set_columns($columns)
|
||||
{
|
||||
$this->_columns = $columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $i
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function &get_column($i)
|
||||
{
|
||||
if (!isset($this->_columns[$i])) {
|
||||
$this->_columns[$i] = [
|
||||
"x" => 0,
|
||||
"min-width" => 0,
|
||||
"max-width" => 0,
|
||||
"used-width" => null,
|
||||
"absolute" => 0,
|
||||
"percent" => 0,
|
||||
"auto" => true,
|
||||
];
|
||||
}
|
||||
|
||||
return $this->_columns[$i];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function &get_rows()
|
||||
{
|
||||
return $this->_rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $j
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function &get_row($j)
|
||||
{
|
||||
if (!isset($this->_rows[$j])) {
|
||||
$this->_rows[$j] = [
|
||||
"y" => 0,
|
||||
"first-column" => 0,
|
||||
"height" => null,
|
||||
];
|
||||
}
|
||||
|
||||
return $this->_rows[$j];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $i
|
||||
* @param int $j
|
||||
* @param mixed $h_v
|
||||
* @param null|mixed $prop
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function get_border($i, $j, $h_v, $prop = null)
|
||||
{
|
||||
if (!isset($this->_borders[$i][$j][$h_v])) {
|
||||
$this->_borders[$i][$j][$h_v] = [
|
||||
"width" => 0,
|
||||
"style" => "solid",
|
||||
"color" => "black",
|
||||
];
|
||||
}
|
||||
|
||||
if (isset($prop)) {
|
||||
return $this->_borders[$i][$j][$h_v][$prop];
|
||||
}
|
||||
|
||||
return $this->_borders[$i][$j][$h_v];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $i
|
||||
* @param int $j
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_border_properties($i, $j)
|
||||
{
|
||||
return [
|
||||
"top" => $this->get_border($i, $j, "horizontal"),
|
||||
"right" => $this->get_border($i, $j + 1, "vertical"),
|
||||
"bottom" => $this->get_border($i + 1, $j, "horizontal"),
|
||||
"left" => $this->get_border($i, $j, "vertical"),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Frame $frame
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
public function get_spanned_cells(Frame $frame)
|
||||
{
|
||||
$key = $frame->get_id();
|
||||
|
||||
if (isset($this->_frames[$key])) {
|
||||
return $this->_frames[$key];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Frame $frame
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function frame_exists_in_cellmap(Frame $frame)
|
||||
{
|
||||
$key = $frame->get_id();
|
||||
|
||||
return isset($this->_frames[$key]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Frame $frame
|
||||
*
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
public function get_frame_position(Frame $frame)
|
||||
{
|
||||
global $_dompdf_warnings;
|
||||
|
||||
$key = $frame->get_id();
|
||||
|
||||
if (!isset($this->_frames[$key])) {
|
||||
throw new Exception("Frame not found in cellmap");
|
||||
}
|
||||
|
||||
// Positions are stored relative to the table position
|
||||
[$table_x, $table_y] = $this->_table->get_position();
|
||||
$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 = $table_x;
|
||||
} else {
|
||||
$x = $table_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 = $table_y;
|
||||
} else {
|
||||
$y = $table_y + $this->_rows[$row]["y"];
|
||||
}
|
||||
|
||||
return [$x, $y, "x" => $x, "y" => $y];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Frame $frame
|
||||
*
|
||||
* @return int
|
||||
* @throws Exception
|
||||
*/
|
||||
public function get_frame_width(Frame $frame)
|
||||
{
|
||||
$key = $frame->get_id();
|
||||
|
||||
if (!isset($this->_frames[$key])) {
|
||||
throw new 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Frame $frame
|
||||
*
|
||||
* @return int
|
||||
* @throws Exception
|
||||
* @throws Exception
|
||||
*/
|
||||
public function get_frame_height(Frame $frame)
|
||||
{
|
||||
$key = $frame->get_id();
|
||||
|
||||
if (!isset($this->_frames[$key])) {
|
||||
throw new 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("The row #$i could not be found, please file an issue in the tracker with the HTML code");
|
||||
}
|
||||
|
||||
$h += $this->_rows[$i]["height"];
|
||||
}
|
||||
|
||||
return $h;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $j
|
||||
* @param mixed $width
|
||||
*/
|
||||
public function set_column_width($j, $width)
|
||||
{
|
||||
if ($this->_columns_locked) {
|
||||
return;
|
||||
}
|
||||
|
||||
$col =& $this->get_column($j);
|
||||
$col["used-width"] = $width;
|
||||
$next_col =& $this->get_column($j + 1);
|
||||
$next_col["x"] = $col["x"] + $width;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $i
|
||||
* @param long $height
|
||||
*/
|
||||
public function set_row_height($i, $height)
|
||||
{
|
||||
$row =& $this->get_row($i);
|
||||
if ($height > $row["height"]) {
|
||||
$row["height"] = $height;
|
||||
}
|
||||
$next_row =& $this->get_row($i + 1);
|
||||
$next_row["y"] = $row["y"] + $row["height"];
|
||||
}
|
||||
|
||||
/**
|
||||
* https://www.w3.org/TR/CSS21/tables.html#border-conflict-resolution
|
||||
*
|
||||
* @param int $i
|
||||
* @param int $j
|
||||
* @param string $h_v `horizontal` or `vertical`
|
||||
* @param array $border_spec
|
||||
*/
|
||||
protected function resolve_border(int $i, int $j, string $h_v, array $border_spec): void
|
||||
{
|
||||
if (!isset($this->_borders[$i][$j][$h_v])) {
|
||||
$this->_borders[$i][$j][$h_v] = $border_spec;
|
||||
return;
|
||||
}
|
||||
|
||||
$border = $this->_borders[$i][$j][$h_v];
|
||||
|
||||
$n_width = $border_spec["width"];
|
||||
$n_style = $border_spec["style"];
|
||||
$o_width = $border["width"];
|
||||
$o_style = $border["style"];
|
||||
|
||||
if ($o_style === "hidden") {
|
||||
return;
|
||||
}
|
||||
|
||||
// A style of `none` has lowest priority independent of its specified
|
||||
// width here, as its resolved width is always 0
|
||||
if ($n_style === "hidden" || $n_width > $o_width
|
||||
|| ($o_width == $n_width
|
||||
&& isset(self::BORDER_STYLE_SCORE[$n_style])
|
||||
&& isset(self::BORDER_STYLE_SCORE[$o_style])
|
||||
&& self::BORDER_STYLE_SCORE[$n_style] > self::BORDER_STYLE_SCORE[$o_style])
|
||||
) {
|
||||
$this->_borders[$i][$j][$h_v] = $border_spec;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the resolved border properties for the given frame.
|
||||
*
|
||||
* @param AbstractFrameDecorator $frame
|
||||
*
|
||||
* @return array[]
|
||||
*/
|
||||
protected function get_resolved_border(AbstractFrameDecorator $frame): array
|
||||
{
|
||||
$key = $frame->get_id();
|
||||
$columns = $this->_frames[$key]["columns"];
|
||||
$rows = $this->_frames[$key]["rows"];
|
||||
|
||||
$first_col = $columns[0];
|
||||
$last_col = $columns[count($columns) - 1];
|
||||
$first_row = $rows[0];
|
||||
$last_row = $rows[count($rows) - 1];
|
||||
|
||||
$max_top = null;
|
||||
$max_bottom = null;
|
||||
$max_left = null;
|
||||
$max_right = null;
|
||||
|
||||
foreach ($columns as $col) {
|
||||
$top = $this->_borders[$first_row][$col]["horizontal"];
|
||||
$bottom = $this->_borders[$last_row + 1][$col]["horizontal"];
|
||||
|
||||
if ($max_top === null || $top["width"] > $max_top["width"]) {
|
||||
$max_top = $top;
|
||||
}
|
||||
if ($max_bottom === null || $bottom["width"] > $max_bottom["width"]) {
|
||||
$max_bottom = $bottom;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$left = $this->_borders[$row][$first_col]["vertical"];
|
||||
$right = $this->_borders[$row][$last_col + 1]["vertical"];
|
||||
|
||||
if ($max_left === null || $left["width"] > $max_left["width"]) {
|
||||
$max_left = $left;
|
||||
}
|
||||
if ($max_right === null || $right["width"] > $max_right["width"]) {
|
||||
$max_right = $right;
|
||||
}
|
||||
}
|
||||
|
||||
return [$max_top, $max_right, $max_bottom, $max_left];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AbstractFrameDecorator $frame
|
||||
*/
|
||||
public function add_frame(Frame $frame): void
|
||||
{
|
||||
$style = $frame->get_style();
|
||||
$display = $style->display;
|
||||
|
||||
$collapse = $this->_table->get_style()->border_collapse === "collapse";
|
||||
|
||||
// Recursively add the frames within the table, its row groups and rows
|
||||
if ($frame === $this->_table
|
||||
|| $display === "table-row"
|
||||
|| in_array($display, TableFrameDecorator::ROW_GROUPS, true)
|
||||
) {
|
||||
$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 ($collapse) {
|
||||
$bp = $style->get_border_properties();
|
||||
|
||||
// Resolve vertical 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"]);
|
||||
}
|
||||
|
||||
// Resolve horizontal borders
|
||||
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"]);
|
||||
}
|
||||
|
||||
if ($frame === $this->_table) {
|
||||
// Clear borders because the cells are now using them. The
|
||||
// border width still needs to be set to half the resolved
|
||||
// width so that the table is positioned properly
|
||||
[$top, $right, $bottom, $left] = $this->get_resolved_border($frame);
|
||||
|
||||
$style->set_used("border_top_width", $top["width"] / 2);
|
||||
$style->set_used("border_right_width", $right["width"] / 2);
|
||||
$style->set_used("border_bottom_width", $bottom["width"] / 2);
|
||||
$style->set_used("border_left_width", $left["width"] / 2);
|
||||
$style->set_used("border_style", "none");
|
||||
} else {
|
||||
// Clear borders for rows and row groups
|
||||
$style->set_used("border_width", 0);
|
||||
$style->set_used("border_style", "none");
|
||||
}
|
||||
}
|
||||
|
||||
if ($frame === $this->_table) {
|
||||
// Apply resolved borders to table cells and calculate column
|
||||
// widths after all frames have been added
|
||||
$this->calculate_column_widths();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Add the frame to the cellmap
|
||||
$key = $frame->get_id();
|
||||
$node = $frame->get_node();
|
||||
$bp = $style->get_border_properties();
|
||||
|
||||
// Determine where this cell is going
|
||||
$colspan = max((int) $node->getAttribute("colspan"), 1);
|
||||
$rowspan = max((int) $node->getAttribute("rowspan"), 1);
|
||||
|
||||
// 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
|
||||
$this->resolve_border($row, $this->__col, "vertical", $bp["left"]);
|
||||
$this->resolve_border($row, $this->__col + $colspan, "vertical", $bp["right"]);
|
||||
}
|
||||
}
|
||||
|
||||
// Columns:
|
||||
for ($j = 0; $j < $colspan; $j++) {
|
||||
$col = $this->__col + $j;
|
||||
$this->_frames[$key]["columns"][] = $col;
|
||||
|
||||
if ($collapse) {
|
||||
// Resolve horizontal borders
|
||||
$this->resolve_border($this->__row, $col, "horizontal", $bp["top"]);
|
||||
$this->resolve_border($this->__row + $rowspan, $col, "horizontal", $bp["bottom"]);
|
||||
}
|
||||
}
|
||||
|
||||
$this->_frames[$key]["frame"] = $frame;
|
||||
|
||||
$this->__col += $colspan;
|
||||
if ($this->__col > $this->_num_cols) {
|
||||
$this->_num_cols = $this->__col;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply resolved borders to table cells and calculate column widths.
|
||||
*/
|
||||
protected function calculate_column_widths(): void
|
||||
{
|
||||
$table = $this->_table;
|
||||
$table_style = $table->get_style();
|
||||
$collapse = $table_style->border_collapse === "collapse";
|
||||
|
||||
if ($collapse) {
|
||||
$v_spacing = 0;
|
||||
$h_spacing = 0;
|
||||
} else {
|
||||
// The additional 1/2 width gets added to the table proper
|
||||
[$h, $v] = $table_style->border_spacing;
|
||||
$v_spacing = $v / 2;
|
||||
$h_spacing = $h / 2;
|
||||
}
|
||||
|
||||
foreach ($this->_frames as $frame_info) {
|
||||
/** @var TableCellFrameDecorator */
|
||||
$frame = $frame_info["frame"];
|
||||
$style = $frame->get_style();
|
||||
$display = $style->display;
|
||||
|
||||
if ($display !== "table-cell") {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($collapse) {
|
||||
// Set the resolved border at half width
|
||||
[$top, $right, $bottom, $left] = $this->get_resolved_border($frame);
|
||||
|
||||
$style->set_used("border_top_width", $top["width"] / 2);
|
||||
$style->set_used("border_top_style", $top["style"]);
|
||||
$style->set_used("border_top_color", $top["color"]);
|
||||
$style->set_used("border_right_width", $right["width"] / 2);
|
||||
$style->set_used("border_right_style", $right["style"]);
|
||||
$style->set_used("border_right_color", $right["color"]);
|
||||
$style->set_used("border_bottom_width", $bottom["width"] / 2);
|
||||
$style->set_used("border_bottom_style", $bottom["style"]);
|
||||
$style->set_used("border_bottom_color", $bottom["color"]);
|
||||
$style->set_used("border_left_width", $left["width"] / 2);
|
||||
$style->set_used("border_left_style", $left["style"]);
|
||||
$style->set_used("border_left_color", $left["color"]);
|
||||
$style->set_used("margin", 0);
|
||||
} else {
|
||||
// Border spacing is effectively a margin between cells
|
||||
$style->set_used("margin_top", $v_spacing);
|
||||
$style->set_used("margin_bottom", $v_spacing);
|
||||
$style->set_used("margin_left", $h_spacing);
|
||||
$style->set_used("margin_right", $h_spacing);
|
||||
}
|
||||
|
||||
if ($this->_columns_locked) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$node = $frame->get_node();
|
||||
$colspan = max((int) $node->getAttribute("colspan"), 1);
|
||||
$first_col = $frame_info["columns"][0];
|
||||
|
||||
// Resolve the frame's width
|
||||
if ($this->_fixed_layout) {
|
||||
list($frame_min, $frame_max) = [0, 10e-10];
|
||||
} else {
|
||||
list($frame_min, $frame_max) = $frame->get_min_max_width();
|
||||
}
|
||||
|
||||
$width = $style->width;
|
||||
|
||||
$val = null;
|
||||
if (Helpers::is_percent($width) && $colspan === 1) {
|
||||
$var = "percent";
|
||||
$val = (float)rtrim($width, "% ");
|
||||
} elseif ($width !== "auto" && $colspan === 1) {
|
||||
$var = "absolute";
|
||||
$val = $frame_min;
|
||||
}
|
||||
|
||||
$min = 0;
|
||||
$max = 0;
|
||||
for ($cs = 0; $cs < $colspan; $cs++) {
|
||||
|
||||
// Resolve the frame's width(s) with other cells
|
||||
$col =& $this->get_column($first_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 && $colspan === 1) {
|
||||
// The frame needs more space. Expand each sub-column
|
||||
// FIXME try to avoid putting this dummy value when table-layout:fixed
|
||||
$inc = ($this->is_layout_fixed() ? 10e-10 : ($frame_min - $min));
|
||||
for ($c = 0; $c < $colspan; $c++) {
|
||||
$col =& $this->get_column($first_col + $c);
|
||||
$col["min-width"] += $inc;
|
||||
}
|
||||
}
|
||||
|
||||
if ($frame_max > $max) {
|
||||
// FIXME try to avoid putting this dummy value when table-layout:fixed
|
||||
$inc = ($this->is_layout_fixed() ? 10e-10 : ($frame_max - $max) / $colspan);
|
||||
for ($c = 0; $c < $colspan; $c++) {
|
||||
$col =& $this->get_column($first_col + $c);
|
||||
$col["max-width"] += $inc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Adjust absolute columns so that the absolute (and max) width is the
|
||||
// largest minimum width of all cells. This accounts for cells without
|
||||
// absolute width within an absolute column
|
||||
foreach ($this->_columns as &$col) {
|
||||
if ($col["absolute"] > 0) {
|
||||
$col["absolute"] = $col["min-width"];
|
||||
$col["max-width"] = $col["min-width"];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function add_row(): void
|
||||
{
|
||||
$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
|
||||
*/
|
||||
public function remove_row(Frame $row)
|
||||
{
|
||||
$key = $row->get_id();
|
||||
if (!isset($this->_frames[$key])) {
|
||||
return; // Presumably this row has already 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->_cells[$r][$c] = null;
|
||||
unset($this->_cells[$r][$c]);
|
||||
|
||||
// has multiple rows?
|
||||
if (isset($this->_frames[$id]) && count($this->_frames[$id]["rows"]) > 1) {
|
||||
// remove just the desired row, but leave the frame
|
||||
if (($row_key = array_search($r, $this->_frames[$id]["rows"])) !== false) {
|
||||
unset($this->_frames[$id]["rows"][$row_key]);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->_frames[$id] = null;
|
||||
unset($this->_frames[$id]);
|
||||
}
|
||||
}
|
||||
|
||||
$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
|
||||
*/
|
||||
public function remove_row_group(Frame $group)
|
||||
{
|
||||
$key = $group->get_id();
|
||||
if (!isset($this->_frames[$key])) {
|
||||
return; // Presumably this row has already 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
|
||||
*/
|
||||
public function update_row_group(Frame $group, Frame $last_row)
|
||||
{
|
||||
$g_key = $group->get_id();
|
||||
|
||||
$first_index = $this->_frames[$g_key]["rows"][0];
|
||||
$last_index = $first_index;
|
||||
$row = $last_row;
|
||||
while ($row = $row->get_prev_sibling()) {
|
||||
$last_index++;
|
||||
}
|
||||
|
||||
$this->_frames[$g_key]["rows"] = range($first_index, $last_index);
|
||||
}
|
||||
|
||||
public function assign_x_positions(): void
|
||||
{
|
||||
// Pre-condition: widths must be resolved and assigned to columns and
|
||||
// column[0]["x"] must be set.
|
||||
|
||||
if ($this->_columns_locked) {
|
||||
return;
|
||||
}
|
||||
|
||||
$x = $this->_columns[0]["x"];
|
||||
foreach (array_keys($this->_columns) as $j) {
|
||||
$this->_columns[$j]["x"] = $x;
|
||||
$x += $this->_columns[$j]["used-width"];
|
||||
}
|
||||
}
|
||||
|
||||
public function assign_frame_heights(): void
|
||||
{
|
||||
// Pre-condition: widths and heights of each column & row must be
|
||||
// calcluated
|
||||
foreach ($this->_frames as $arr) {
|
||||
$frame = $arr["frame"];
|
||||
|
||||
$h = 0.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 TableCellFrameDecorator) {
|
||||
$frame->set_cell_height($h);
|
||||
} else {
|
||||
$frame->get_style()->set_used("height", $h);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-adjust frame height if the table height is larger than its content
|
||||
*/
|
||||
public function set_frame_heights(float $table_height, float $content_height): void
|
||||
{
|
||||
// Distribute the increased height proportionally amongst each row
|
||||
foreach ($this->_frames as $arr) {
|
||||
$frame = $arr["frame"];
|
||||
|
||||
$h = 0.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.0;
|
||||
}
|
||||
|
||||
if ($frame instanceof TableCellFrameDecorator) {
|
||||
$frame->set_cell_height($new_height);
|
||||
} else {
|
||||
$frame->get_style()->set_used("height", $new_height);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used for debugging:
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
$str = "";
|
||||
$str .= "Columns:<br/>";
|
||||
$str .= Helpers::pre_r($this->_columns, true);
|
||||
$str .= "Rows:<br/>";
|
||||
$str .= Helpers::pre_r($this->_rows, true);
|
||||
|
||||
$str .= "Frames:<br/>";
|
||||
$arr = [];
|
||||
foreach ($this->_frames as $key => $val) {
|
||||
$arr[$key] = ["columns" => $val["columns"], "rows" => $val["rows"]];
|
||||
}
|
||||
|
||||
$str .= Helpers::pre_r($arr, true);
|
||||
|
||||
if (php_sapi_name() == "cli") {
|
||||
$str = strip_tags(str_replace(["<br/>", "<b>", "</b>"],
|
||||
["\n", chr(27) . "[01;33m", chr(27) . "[0m"],
|
||||
$str));
|
||||
}
|
||||
|
||||
return $str;
|
||||
}
|
||||
}
|
||||
652
system/vendor/pancakeapp/dompdf/src/Css/AttributeTranslator.php
vendored
Executable file
652
system/vendor/pancakeapp/dompdf/src/Css/AttributeTranslator.php
vendored
Executable file
@@ -0,0 +1,652 @@
|
||||
<?php
|
||||
/**
|
||||
* @package dompdf
|
||||
* @link https://github.com/dompdf/dompdf
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
*/
|
||||
namespace Dompdf\Css;
|
||||
|
||||
use Dompdf\Frame;
|
||||
use Dompdf\Helpers;
|
||||
|
||||
/**
|
||||
* Translates HTML 4.0 attributes into CSS rules
|
||||
*
|
||||
* @package dompdf
|
||||
*/
|
||||
class AttributeTranslator
|
||||
{
|
||||
static $_style_attr = "_html_style_attribute";
|
||||
|
||||
// Munged data originally from
|
||||
// http://www.w3.org/TR/REC-html40/index/attributes.html
|
||||
// http://www.cs.tut.fi/~jkorpela/html2css.html
|
||||
private static $__ATTRIBUTE_LOOKUP = [
|
||||
//'caption' => array ( 'align' => '', ),
|
||||
'img' => [
|
||||
'align' => [
|
||||
'bottom' => 'vertical-align: baseline;',
|
||||
'middle' => 'vertical-align: middle;',
|
||||
'top' => 'vertical-align: top;',
|
||||
'left' => 'float: left;',
|
||||
'right' => 'float: right;'
|
||||
],
|
||||
'border' => 'border: %0.2Fpx solid;',
|
||||
'height' => '_set_px_height',
|
||||
'hspace' => 'padding-left: %1$0.2Fpx; padding-right: %1$0.2Fpx;',
|
||||
'vspace' => 'padding-top: %1$0.2Fpx; padding-bottom: %1$0.2Fpx;',
|
||||
'width' => '_set_px_width',
|
||||
],
|
||||
'table' => [
|
||||
'align' => [
|
||||
'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', //'border-spacing: %0.2F; border-collapse: separate;',
|
||||
'cellspacing' => '_set_table_cellspacing',
|
||||
'frame' => [
|
||||
'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' => [
|
||||
'align' => '_set_hr_align', // Need to grab width to set 'left' & 'right' correctly
|
||||
'noshade' => 'border-style: solid;',
|
||||
'size' => '_set_hr_size', //'border-width: %0.2F px;',
|
||||
'width' => 'width: %s;',
|
||||
],
|
||||
'div' => [
|
||||
'align' => 'text-align: %s;',
|
||||
],
|
||||
'h1' => [
|
||||
'align' => 'text-align: %s;',
|
||||
],
|
||||
'h2' => [
|
||||
'align' => 'text-align: %s;',
|
||||
],
|
||||
'h3' => [
|
||||
'align' => 'text-align: %s;',
|
||||
],
|
||||
'h4' => [
|
||||
'align' => 'text-align: %s;',
|
||||
],
|
||||
'h5' => [
|
||||
'align' => 'text-align: %s;',
|
||||
],
|
||||
'h6' => [
|
||||
'align' => 'text-align: %s;',
|
||||
],
|
||||
//TODO: translate more form element attributes
|
||||
'input' => [
|
||||
'size' => '_set_input_width'
|
||||
],
|
||||
'p' => [
|
||||
'align' => 'text-align: %s;',
|
||||
],
|
||||
// 'col' => array(
|
||||
// 'align' => '',
|
||||
// 'valign' => '',
|
||||
// ),
|
||||
// 'colgroup' => array(
|
||||
// 'align' => '',
|
||||
// 'valign' => '',
|
||||
// ),
|
||||
'tbody' => [
|
||||
'align' => '_set_table_row_align',
|
||||
'valign' => '_set_table_row_valign',
|
||||
],
|
||||
'td' => [
|
||||
'align' => 'text-align: %s;',
|
||||
'bgcolor' => '_set_background_color',
|
||||
'height' => 'height: %s;',
|
||||
'nowrap' => 'white-space: nowrap;',
|
||||
'valign' => 'vertical-align: %s;',
|
||||
'width' => 'width: %s;',
|
||||
],
|
||||
'tfoot' => [
|
||||
'align' => '_set_table_row_align',
|
||||
'valign' => '_set_table_row_valign',
|
||||
],
|
||||
'th' => [
|
||||
'align' => 'text-align: %s;',
|
||||
'bgcolor' => '_set_background_color',
|
||||
'height' => 'height: %s;',
|
||||
'nowrap' => 'white-space: nowrap;',
|
||||
'valign' => 'vertical-align: %s;',
|
||||
'width' => 'width: %s;',
|
||||
],
|
||||
'thead' => [
|
||||
'align' => '_set_table_row_align',
|
||||
'valign' => '_set_table_row_valign',
|
||||
],
|
||||
'tr' => [
|
||||
'align' => '_set_table_row_align',
|
||||
'bgcolor' => '_set_table_row_bgcolor',
|
||||
'valign' => '_set_table_row_valign',
|
||||
],
|
||||
'body' => [
|
||||
'background' => 'background-image: url(%s);',
|
||||
'bgcolor' => '_set_background_color',
|
||||
'link' => '_set_body_link',
|
||||
'text' => '_set_color',
|
||||
],
|
||||
'br' => [
|
||||
'clear' => 'clear: %s;',
|
||||
],
|
||||
'basefont' => [
|
||||
'color' => '_set_color',
|
||||
'face' => 'font-family: %s;',
|
||||
'size' => '_set_basefont_size',
|
||||
],
|
||||
'font' => [
|
||||
'color' => '_set_color',
|
||||
'face' => 'font-family: %s;',
|
||||
'size' => '_set_font_size',
|
||||
],
|
||||
'dir' => [
|
||||
'compact' => 'margin: 0.5em 0;',
|
||||
],
|
||||
'dl' => [
|
||||
'compact' => 'margin: 0.5em 0;',
|
||||
],
|
||||
'menu' => [
|
||||
'compact' => 'margin: 0.5em 0;',
|
||||
],
|
||||
'ol' => [
|
||||
'compact' => 'margin: 0.5em 0;',
|
||||
'start' => 'counter-reset: -dompdf-default-counter %d;',
|
||||
'type' => 'list-style-type: %s;',
|
||||
],
|
||||
'ul' => [
|
||||
'compact' => 'margin: 0.5em 0;',
|
||||
'type' => 'list-style-type: %s;',
|
||||
],
|
||||
'li' => [
|
||||
'type' => 'list-style-type: %s;',
|
||||
'value' => 'counter-reset: -dompdf-default-counter %d;',
|
||||
],
|
||||
'pre' => [
|
||||
'width' => 'width: %s;',
|
||||
],
|
||||
];
|
||||
|
||||
protected static $_last_basefont_size = 3;
|
||||
protected static $_font_size_lookup = [
|
||||
// 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",
|
||||
];
|
||||
|
||||
/**
|
||||
* @param Frame $frame
|
||||
*/
|
||||
static function translate_attributes(Frame $frame)
|
||||
{
|
||||
$node = $frame->get_node();
|
||||
$tag = $node->nodeName;
|
||||
|
||||
if (!isset(self::$__ATTRIBUTE_LOOKUP[$tag])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$valid_attrs = self::$__ATTRIBUTE_LOOKUP[$tag];
|
||||
$attrs = $node->attributes;
|
||||
$style = rtrim($node->getAttribute(self::$_style_attr), "; ");
|
||||
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(self::$_style_attr, $style);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DOMNode $node
|
||||
* @param string $target
|
||||
* @param string $value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected static function _resolve_target(\DOMNode $node, $target, $value)
|
||||
{
|
||||
if ($target[0] === "_") {
|
||||
return self::$target($node, $value);
|
||||
}
|
||||
|
||||
return $value ? sprintf($target, $value) : "";
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DOMElement $node
|
||||
* @param string $new_style
|
||||
*/
|
||||
static function append_style(\DOMElement $node, $new_style)
|
||||
{
|
||||
$style = rtrim($node->getAttribute(self::$_style_attr), ";");
|
||||
$style .= $new_style;
|
||||
$style = ltrim($style, ";");
|
||||
$node->setAttribute(self::$_style_attr, $style);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DOMNode $node
|
||||
*
|
||||
* @return \DOMNodeList|\DOMElement[]
|
||||
*/
|
||||
protected static function get_cell_list(\DOMNode $node)
|
||||
{
|
||||
$xpath = new \DOMXpath($node->ownerDocument);
|
||||
|
||||
switch ($node->nodeName) {
|
||||
default:
|
||||
case "table":
|
||||
$query = "tr/td | thead/tr/td | tbody/tr/td | tfoot/tr/td | tr/th | thead/tr/th | tbody/tr/th | tfoot/tr/th";
|
||||
break;
|
||||
|
||||
case "tbody":
|
||||
case "tfoot":
|
||||
case "thead":
|
||||
$query = "tr/td | tr/th";
|
||||
break;
|
||||
|
||||
case "tr":
|
||||
$query = "td | th";
|
||||
break;
|
||||
}
|
||||
|
||||
return $xpath->query($query, $node);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected static function _get_valid_color($value)
|
||||
{
|
||||
if (preg_match('/^#?([0-9A-F]{6})$/i', $value, $matches)) {
|
||||
$value = "#$matches[1]";
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DOMElement $node
|
||||
* @param string $value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected static function _set_color(\DOMElement $node, $value)
|
||||
{
|
||||
$value = self::_get_valid_color($value);
|
||||
|
||||
return "color: $value;";
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DOMElement $node
|
||||
* @param string $value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected static function _set_background_color(\DOMElement $node, $value)
|
||||
{
|
||||
$value = self::_get_valid_color($value);
|
||||
|
||||
return "background-color: $value;";
|
||||
}
|
||||
|
||||
protected static function _set_px_width(\DOMElement $node, string $value): string
|
||||
{
|
||||
$v = trim($value);
|
||||
|
||||
if (Helpers::is_percent($v)) {
|
||||
return sprintf("width: %s;", $v);
|
||||
}
|
||||
|
||||
if (is_numeric(mb_substr($v, 0, 1))) {
|
||||
return sprintf("width: %spx;", (float) $v);
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
protected static function _set_px_height(\DOMElement $node, string $value): string
|
||||
{
|
||||
$v = trim($value);
|
||||
|
||||
if (Helpers::is_percent($v)) {
|
||||
return sprintf("height: %s;", $v);
|
||||
}
|
||||
|
||||
if (is_numeric(mb_substr($v, 0, 1))) {
|
||||
return sprintf("height: %spx;", (float) $v);
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DOMElement $node
|
||||
* @param string $value
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
protected static function _set_table_cellpadding(\DOMElement $node, $value)
|
||||
{
|
||||
$cell_list = self::get_cell_list($node);
|
||||
|
||||
foreach ($cell_list as $cell) {
|
||||
self::append_style($cell, "; padding: {$value}px;");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DOMElement $node
|
||||
* @param string $value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected static function _set_table_border(\DOMElement $node, $value)
|
||||
{
|
||||
return "border-width: $value" . "px;";
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DOMElement $node
|
||||
* @param string $value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected static function _set_table_cellspacing(\DOMElement $node, $value)
|
||||
{
|
||||
$style = rtrim($node->getAttribute(self::$_style_attr), ";");
|
||||
|
||||
if ($value == 0) {
|
||||
$style .= "; border-collapse: collapse;";
|
||||
} else {
|
||||
$style .= "; border-spacing: {$value}px; border-collapse: separate;";
|
||||
}
|
||||
|
||||
return ltrim($style, ";");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DOMElement $node
|
||||
* @param string $value
|
||||
*
|
||||
* @return null|string
|
||||
*/
|
||||
protected static function _set_table_rules(\DOMElement $node, $value)
|
||||
{
|
||||
$new_style = "; border-collapse: collapse;";
|
||||
|
||||
switch ($value) {
|
||||
case "none":
|
||||
$new_style .= "border-style: none;";
|
||||
break;
|
||||
|
||||
case "groups":
|
||||
// FIXME: unsupported
|
||||
return null;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
$cell_list = self::get_cell_list($node);
|
||||
|
||||
foreach ($cell_list as $cell) {
|
||||
$style = $cell->getAttribute(self::$_style_attr);
|
||||
$style .= $new_style;
|
||||
$cell->setAttribute(self::$_style_attr, $style);
|
||||
}
|
||||
|
||||
$style = rtrim($node->getAttribute(self::$_style_attr), ";");
|
||||
$style .= "; border-collapse: collapse; ";
|
||||
|
||||
return ltrim($style, "; ");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DOMElement $node
|
||||
* @param string $value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected static function _set_hr_size(\DOMElement $node, $value)
|
||||
{
|
||||
$style = rtrim($node->getAttribute(self::$_style_attr), ";");
|
||||
$style .= "; border-width: " . max(0, $value - 2) . "; ";
|
||||
|
||||
return ltrim($style, "; ");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DOMElement $node
|
||||
* @param string $value
|
||||
*
|
||||
* @return null|string
|
||||
*/
|
||||
protected static function _set_hr_align(\DOMElement $node, $value)
|
||||
{
|
||||
$style = rtrim($node->getAttribute(self::$_style_attr), ";");
|
||||
$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, "; ");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DOMElement $node
|
||||
* @param string $value
|
||||
*
|
||||
* @return null|string
|
||||
*/
|
||||
protected static function _set_input_width(\DOMElement $node, $value)
|
||||
{
|
||||
if (empty($value)) { return null; }
|
||||
|
||||
if ($node->hasAttribute("type") && in_array(strtolower($node->getAttribute("type")), ["text","password"])) {
|
||||
return sprintf("width: %Fem", (((int)$value * .65)+2));
|
||||
} else {
|
||||
return sprintf("width: %upx;", (int)$value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DOMElement $node
|
||||
* @param string $value
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
protected static function _set_table_row_align(\DOMElement $node, $value)
|
||||
{
|
||||
$cell_list = self::get_cell_list($node);
|
||||
|
||||
foreach ($cell_list as $cell) {
|
||||
self::append_style($cell, "; text-align: $value;");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DOMElement $node
|
||||
* @param string $value
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
protected static function _set_table_row_valign(\DOMElement $node, $value)
|
||||
{
|
||||
$cell_list = self::get_cell_list($node);
|
||||
|
||||
foreach ($cell_list as $cell) {
|
||||
self::append_style($cell, "; vertical-align: $value;");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DOMElement $node
|
||||
* @param string $value
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
protected static function _set_table_row_bgcolor(\DOMElement $node, $value)
|
||||
{
|
||||
$cell_list = self::get_cell_list($node);
|
||||
$value = self::_get_valid_color($value);
|
||||
|
||||
foreach ($cell_list as $cell) {
|
||||
self::append_style($cell, "; background-color: $value;");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DOMElement $node
|
||||
* @param string $value
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
protected static function _set_body_link(\DOMElement $node, $value)
|
||||
{
|
||||
$a_list = $node->getElementsByTagName("a");
|
||||
$value = self::_get_valid_color($value);
|
||||
|
||||
foreach ($a_list as $a) {
|
||||
self::append_style($a, "; color: $value;");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DOMElement $node
|
||||
* @param string $value
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
protected static function _set_basefont_size(\DOMElement $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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DOMElement $node
|
||||
* @param string $value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected static function _set_font_size(\DOMElement $node, $value)
|
||||
{
|
||||
$style = $node->getAttribute(self::$_style_attr);
|
||||
|
||||
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, "; ");
|
||||
}
|
||||
}
|
||||
339
system/vendor/pancakeapp/dompdf/src/Css/Color.php
vendored
Executable file
339
system/vendor/pancakeapp/dompdf/src/Css/Color.php
vendored
Executable file
@@ -0,0 +1,339 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package dompdf
|
||||
* @link https://github.com/dompdf/dompdf
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
*/
|
||||
namespace Dompdf\Css;
|
||||
|
||||
use Dompdf\Helpers;
|
||||
|
||||
class Color
|
||||
{
|
||||
static $cssColorNames = [
|
||||
"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",
|
||||
];
|
||||
|
||||
/**
|
||||
* @param array|string|null $color
|
||||
* @return array|string|null
|
||||
*/
|
||||
static function parse($color)
|
||||
{
|
||||
if ($color === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (is_array($color)) {
|
||||
// Assume the array has the right format...
|
||||
// FIXME: should/could verify this.
|
||||
return $color;
|
||||
}
|
||||
|
||||
static $cache = [];
|
||||
|
||||
$color = strtolower($color);
|
||||
|
||||
if (isset($cache[$color])) {
|
||||
return $cache[$color];
|
||||
}
|
||||
|
||||
if ($color === "transparent") {
|
||||
return $cache[$color] = $color;
|
||||
}
|
||||
|
||||
if (isset(self::$cssColorNames[$color])) {
|
||||
return $cache[$color] = self::getArray(self::$cssColorNames[$color]);
|
||||
}
|
||||
|
||||
// https://www.w3.org/TR/css-color-4/#hex-notation
|
||||
if (mb_substr($color, 0, 1) === "#") {
|
||||
$length = mb_strlen($color);
|
||||
$alpha = 1.0;
|
||||
|
||||
// #rgb format
|
||||
if ($length === 4) {
|
||||
return $cache[$color] = self::getArray($color[1] . $color[1] . $color[2] . $color[2] . $color[3] . $color[3]);
|
||||
}
|
||||
|
||||
// #rgba format
|
||||
if ($length === 5) {
|
||||
if (ctype_xdigit($color[4])) {
|
||||
$alpha = round(hexdec($color[4] . $color[4])/255, 2);
|
||||
}
|
||||
return $cache[$color] = self::getArray($color[1] . $color[1] . $color[2] . $color[2] . $color[3] . $color[3], $alpha);
|
||||
}
|
||||
|
||||
// #rrggbb format
|
||||
if ($length === 7) {
|
||||
return $cache[$color] = self::getArray(mb_substr($color, 1, 6));
|
||||
}
|
||||
|
||||
// #rrggbbaa format
|
||||
if ($length === 9) {
|
||||
if (ctype_xdigit(mb_substr($color, 7, 2))) {
|
||||
$alpha = round(hexdec(mb_substr($color, 7, 2))/255, 2);
|
||||
}
|
||||
return $cache[$color] = self::getArray(mb_substr($color, 1, 6), $alpha);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// rgb( r g b [/α] ) / rgb( r,g,b[,α] ) format and alias rgba()
|
||||
// https://www.w3.org/TR/css-color-4/#rgb-functions
|
||||
if (mb_substr($color, 0, 4) === "rgb(" || mb_substr($color, 0, 5) === "rgba(") {
|
||||
$i = mb_strpos($color, "(");
|
||||
$j = mb_strpos($color, ")");
|
||||
|
||||
// Bad color value
|
||||
if ($i === false || $j === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$value_decl = trim(mb_substr($color, $i + 1, $j - $i - 1));
|
||||
|
||||
if (mb_strpos($value_decl, ",") === false) {
|
||||
// Space-separated values syntax `r g b` or `r g b / α`
|
||||
$parts = preg_split("/\s*\/\s*/", $value_decl);
|
||||
$triplet = preg_split("/\s+/", $parts[0]);
|
||||
$alpha = $parts[1] ?? 1.0;
|
||||
} else {
|
||||
// Comma-separated values syntax `r, g, b` or `r, g, b, α`
|
||||
$parts = preg_split("/\s*,\s*/", $value_decl);
|
||||
$triplet = array_slice($parts, 0, 3);
|
||||
$alpha = $parts[3] ?? 1.0;
|
||||
}
|
||||
|
||||
if (count($triplet) !== 3) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Parse alpha value
|
||||
if (Helpers::is_percent($alpha)) {
|
||||
$alpha = (float) $alpha / 100;
|
||||
} else {
|
||||
$alpha = (float) $alpha;
|
||||
}
|
||||
|
||||
$alpha = max(0.0, min($alpha, 1.0));
|
||||
|
||||
foreach ($triplet as &$c) {
|
||||
if (Helpers::is_percent($c)) {
|
||||
$c = round((float) $c * 2.55);
|
||||
}
|
||||
}
|
||||
|
||||
return $cache[$color] = self::getArray(vsprintf("%02X%02X%02X", $triplet), $alpha);
|
||||
}
|
||||
|
||||
// cmyk( c,m,y,k ) format
|
||||
// http://www.w3.org/TR/css3-gcpm/#cmyk-colors
|
||||
if (mb_substr($color, 0, 5) === "cmyk(") {
|
||||
$i = mb_strpos($color, "(");
|
||||
$j = mb_strpos($color, ")");
|
||||
|
||||
// Bad color value
|
||||
if ($i === false || $j === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$values = explode(",", mb_substr($color, $i + 1, $j - $i - 1));
|
||||
|
||||
if (count($values) != 4) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$values = array_map(function ($c) {
|
||||
return min(1.0, max(0.0, floatval(trim($c))));
|
||||
}, $values);
|
||||
|
||||
return $cache[$color] = self::getArray($values);
|
||||
}
|
||||
|
||||
// Invalid or unsupported color format
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array|string $color
|
||||
* @param float $alpha
|
||||
* @return array
|
||||
*/
|
||||
static function getArray($color, $alpha = 1.0)
|
||||
{
|
||||
$c = [null, null, null, null, "alpha" => $alpha, "hex" => null];
|
||||
|
||||
if (is_array($color)) {
|
||||
$c = $color;
|
||||
$c["c"] = $c[0];
|
||||
$c["m"] = $c[1];
|
||||
$c["y"] = $c[2];
|
||||
$c["k"] = $c[3];
|
||||
$c["alpha"] = $alpha;
|
||||
$c["hex"] = "cmyk($c[0],$c[1],$c[2],$c[3])";
|
||||
} else {
|
||||
if (ctype_xdigit($color) === false || mb_strlen($color) !== 6) {
|
||||
// invalid color value ... expected 6-character hex
|
||||
return $c;
|
||||
}
|
||||
$c[0] = hexdec(mb_substr($color, 0, 2)) / 0xff;
|
||||
$c[1] = hexdec(mb_substr($color, 2, 2)) / 0xff;
|
||||
$c[2] = hexdec(mb_substr($color, 4, 2)) / 0xff;
|
||||
$c["r"] = $c[0];
|
||||
$c["g"] = $c[1];
|
||||
$c["b"] = $c[2];
|
||||
$c["alpha"] = $alpha;
|
||||
$c["hex"] = sprintf("#%s%02X", $color, round($alpha * 255));
|
||||
}
|
||||
|
||||
return $c;
|
||||
}
|
||||
}
|
||||
3743
system/vendor/pancakeapp/dompdf/src/Css/Style.php
vendored
Executable file
3743
system/vendor/pancakeapp/dompdf/src/Css/Style.php
vendored
Executable file
File diff suppressed because it is too large
Load Diff
1655
system/vendor/pancakeapp/dompdf/src/Css/Stylesheet.php
vendored
Executable file
1655
system/vendor/pancakeapp/dompdf/src/Css/Stylesheet.php
vendored
Executable file
File diff suppressed because it is too large
Load Diff
1464
system/vendor/pancakeapp/dompdf/src/Dompdf.php
vendored
Executable file
1464
system/vendor/pancakeapp/dompdf/src/Dompdf.php
vendored
Executable file
File diff suppressed because it is too large
Load Diff
27
system/vendor/pancakeapp/dompdf/src/Exception.php
vendored
Executable file
27
system/vendor/pancakeapp/dompdf/src/Exception.php
vendored
Executable file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
/**
|
||||
* @package dompdf
|
||||
* @link https://github.com/dompdf/dompdf
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
*/
|
||||
namespace Dompdf;
|
||||
|
||||
/**
|
||||
* Standard exception thrown by DOMPDF classes
|
||||
*
|
||||
* @package dompdf
|
||||
*/
|
||||
class Exception extends \Exception
|
||||
{
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param string $message Error message
|
||||
* @param int $code Error code
|
||||
*/
|
||||
public function __construct($message = null, $code = 0)
|
||||
{
|
||||
parent::__construct($message, $code);
|
||||
}
|
||||
}
|
||||
30
system/vendor/pancakeapp/dompdf/src/Exception/ImageException.php
vendored
Executable file
30
system/vendor/pancakeapp/dompdf/src/Exception/ImageException.php
vendored
Executable file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
/**
|
||||
* @package dompdf
|
||||
* @link https://github.com/dompdf/dompdf
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
*/
|
||||
namespace Dompdf\Exception;
|
||||
|
||||
use Dompdf\Exception;
|
||||
|
||||
/**
|
||||
* Image exception thrown by DOMPDF
|
||||
*
|
||||
* @package dompdf
|
||||
*/
|
||||
class ImageException extends Exception
|
||||
{
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param string $message Error message
|
||||
* @param int $code Error code
|
||||
*/
|
||||
function __construct($message = null, $code = 0)
|
||||
{
|
||||
parent::__construct($message, $code);
|
||||
}
|
||||
|
||||
}
|
||||
635
system/vendor/pancakeapp/dompdf/src/FontMetrics.php
vendored
Executable file
635
system/vendor/pancakeapp/dompdf/src/FontMetrics.php
vendored
Executable file
@@ -0,0 +1,635 @@
|
||||
<?php
|
||||
/**
|
||||
* @package dompdf
|
||||
* @link https://github.com/dompdf/dompdf
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
*/
|
||||
namespace Dompdf;
|
||||
|
||||
use FontLib\Font;
|
||||
|
||||
/**
|
||||
* 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 FontMetrics
|
||||
{
|
||||
/**
|
||||
* Name of the user font families 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 FontMetrics::saveFontFamilies().
|
||||
* This is typically done only from command line with load_font.php on converting
|
||||
* ttf fonts to ufm with php-font-lib.
|
||||
*/
|
||||
const USER_FONTS_FILE = "installed-fonts.json";
|
||||
|
||||
|
||||
/**
|
||||
* Underlying {@link Canvas} object to perform text size calculations
|
||||
*
|
||||
* @var Canvas
|
||||
*/
|
||||
protected $canvas;
|
||||
|
||||
/**
|
||||
* Array of bundled font family names to variants
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $bundledFonts = [];
|
||||
|
||||
/**
|
||||
* Array of user defined font family names to variants
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $userFonts = [];
|
||||
|
||||
/**
|
||||
* combined list of all font families with absolute paths
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fontFamilies;
|
||||
|
||||
/**
|
||||
* @var Options
|
||||
*/
|
||||
private $options;
|
||||
|
||||
/**
|
||||
* Class initialization
|
||||
*/
|
||||
public function __construct(Canvas $canvas, Options $options)
|
||||
{
|
||||
$this->setCanvas($canvas);
|
||||
$this->setOptions($options);
|
||||
$this->loadFontFamilies();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
public function save_font_families()
|
||||
{
|
||||
$this->saveFontFamilies();
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the stored font family cache
|
||||
*
|
||||
* The name and location of the cache file are determined by {@link
|
||||
* FontMetrics::USER_FONTS_FILE}. This file should be writable by the
|
||||
* webserver process.
|
||||
*
|
||||
* @see FontMetrics::loadFontFamilies()
|
||||
*/
|
||||
public function saveFontFamilies()
|
||||
{
|
||||
file_put_contents($this->getUserFontsFilePath(), json_encode($this->userFonts, JSON_PRETTY_PRINT));
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
public function load_font_families()
|
||||
{
|
||||
$this->loadFontFamilies();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the stored font family cache
|
||||
*
|
||||
* @see FontMetrics::saveFontFamilies()
|
||||
*/
|
||||
public function loadFontFamilies()
|
||||
{
|
||||
$file = $this->options->getRootDir() . "/lib/fonts/installed-fonts.dist.json";
|
||||
$this->bundledFonts = json_decode(file_get_contents($file), true);
|
||||
|
||||
if (is_readable($this->getUserFontsFilePath())) {
|
||||
$this->userFonts = json_decode(file_get_contents($this->getUserFontsFilePath()), true);
|
||||
} else {
|
||||
$this->loadFontFamiliesLegacy();
|
||||
}
|
||||
}
|
||||
|
||||
private function loadFontFamiliesLegacy()
|
||||
{
|
||||
$legacyCacheFile = $this->options->getFontDir() . '/dompdf_font_family_cache.php';
|
||||
if (is_readable($legacyCacheFile)) {
|
||||
$fontDir = $this->options->getFontDir();
|
||||
$rootDir = $this->options->getRootDir();
|
||||
|
||||
if (!defined("DOMPDF_DIR")) { define("DOMPDF_DIR", $rootDir); }
|
||||
if (!defined("DOMPDF_FONT_DIR")) { define("DOMPDF_FONT_DIR", $fontDir); }
|
||||
|
||||
$cacheDataClosure = require $legacyCacheFile;
|
||||
$cacheData = is_array($cacheDataClosure) ? $cacheDataClosure : $cacheDataClosure($fontDir, $rootDir);
|
||||
if (is_array($cacheData)) {
|
||||
foreach ($cacheData as $family => $variants) {
|
||||
if (!isset($this->bundledFonts[$family]) && is_array($variants)) {
|
||||
foreach ($variants as $variant => $variantPath) {
|
||||
$variantName = basename($variantPath);
|
||||
$variantDir = dirname($variantPath);
|
||||
if ($variantDir == $fontDir) {
|
||||
$this->userFonts[$family][$variant] = $variantName;
|
||||
} else {
|
||||
$this->userFonts[$family][$variant] = $variantPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->saveFontFamilies();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $style
|
||||
* @param string $remote_file
|
||||
* @param resource $context
|
||||
* @return bool
|
||||
* @deprecated
|
||||
*/
|
||||
public function register_font($style, $remote_file, $context = null)
|
||||
{
|
||||
return $this->registerFont($style, $remote_file);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $style
|
||||
* @param string $remoteFile
|
||||
* @param resource $context
|
||||
* @return bool
|
||||
*/
|
||||
public function registerFont($style, $remoteFile, $context = null)
|
||||
{
|
||||
$fontname = mb_strtolower($style["family"]);
|
||||
$families = $this->getFontFamilies();
|
||||
|
||||
$entry = [];
|
||||
if (isset($families[$fontname])) {
|
||||
$entry = $families[$fontname];
|
||||
}
|
||||
|
||||
$styleString = $this->getType("{$style['weight']} {$style['style']}");
|
||||
|
||||
$remoteHash = md5($remoteFile);
|
||||
|
||||
$prefix = $fontname . "_" . $styleString;
|
||||
$prefix = trim($prefix, "-");
|
||||
if (function_exists('iconv')) {
|
||||
$prefix = @iconv('utf-8', 'us-ascii//TRANSLIT', $prefix);
|
||||
}
|
||||
$prefix_encoding = mb_detect_encoding($prefix, mb_detect_order(), true);
|
||||
$substchar = mb_substitute_character();
|
||||
mb_substitute_character(0x005F);
|
||||
$prefix = mb_convert_encoding($prefix, "ISO-8859-1", $prefix_encoding);
|
||||
mb_substitute_character($substchar);
|
||||
$prefix = preg_replace("[\W]", "_", $prefix);
|
||||
$prefix = preg_replace("/[^-_\w]+/", "", $prefix);
|
||||
|
||||
$localFile = $prefix . "_" . $remoteHash;
|
||||
$localFilePath = $this->getOptions()->getFontDir() . "/" . $localFile;
|
||||
|
||||
if (isset($entry[$styleString]) && $localFilePath == $entry[$styleString]) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
$entry[$styleString] = $localFile;
|
||||
|
||||
// Download the remote file
|
||||
[$protocol] = Helpers::explode_url($remoteFile);
|
||||
$allowed_protocols = $this->options->getAllowedProtocols();
|
||||
if (!array_key_exists($protocol, $allowed_protocols)) {
|
||||
Helpers::record_warnings(E_USER_WARNING, "Permission denied on $remoteFile. The communication protocol is not supported.", __FILE__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($allowed_protocols[$protocol]["rules"] as $rule) {
|
||||
[$result, $message] = $rule($remoteFile);
|
||||
if ($result !== true) {
|
||||
Helpers::record_warnings(E_USER_WARNING, "Error loading $remoteFile: $message", __FILE__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
list($remoteFileContent, $http_response_header) = @Helpers::getFileContent($remoteFile, $context);
|
||||
if ($remoteFileContent === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$localTempFile = @tempnam($this->options->get("tempDir"), "dompdf-font-");
|
||||
file_put_contents($localTempFile, $remoteFileContent);
|
||||
|
||||
$font = Font::load($localTempFile);
|
||||
|
||||
if (!$font) {
|
||||
unlink($localTempFile);
|
||||
return false;
|
||||
}
|
||||
|
||||
$font->parse();
|
||||
$font->saveAdobeFontMetrics("$localFilePath.ufm");
|
||||
$font->close();
|
||||
|
||||
unlink($localTempFile);
|
||||
|
||||
if ( !file_exists("$localFilePath.ufm") ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$fontExtension = ".ttf";
|
||||
switch ($font->getFontType()) {
|
||||
case "TrueType":
|
||||
default:
|
||||
$fontExtension = ".ttf";
|
||||
break;
|
||||
}
|
||||
|
||||
// Save the changes
|
||||
file_put_contents($localFilePath.$fontExtension, $remoteFileContent);
|
||||
|
||||
if ( !file_exists($localFilePath.$fontExtension) ) {
|
||||
unlink("$localFilePath.ufm");
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->setFontFamily($fontname, $entry);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $text
|
||||
* @param $font
|
||||
* @param $size
|
||||
* @param float $word_spacing
|
||||
* @param float $char_spacing
|
||||
* @return float
|
||||
* @deprecated
|
||||
*/
|
||||
public function get_text_width($text, $font, $size, $word_spacing = 0.0, $char_spacing = 0.0)
|
||||
{
|
||||
//return self::$_pdf->get_text_width($text, $font, $size, $word_spacing, $char_spacing);
|
||||
return $this->getTextWidth($text, $font, $size, $word_spacing, $char_spacing);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates text size, in points
|
||||
*
|
||||
* @param string $text The text to be sized
|
||||
* @param string $font The font file to use
|
||||
* @param float $size The font size, in points
|
||||
* @param float $wordSpacing Word spacing, if any
|
||||
* @param float $charSpacing Char spacing, if any
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function getTextWidth(string $text, $font, float $size, float $wordSpacing = 0.0, float $charSpacing = 0.0): float
|
||||
{
|
||||
// @todo Make sure this cache is efficient before enabling it
|
||||
static $cache = [];
|
||||
|
||||
if ($text === "") {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Don't cache long strings
|
||||
$useCache = !isset($text[50]); // Faster than strlen
|
||||
|
||||
// Text-size calculations depend on the canvas used. Make sure to not
|
||||
// return wrong values when switching canvas backends
|
||||
$canvasClass = get_class($this->canvas);
|
||||
$key = "$canvasClass/$font/$size/$wordSpacing/$charSpacing";
|
||||
|
||||
if ($useCache && isset($cache[$key][$text])) {
|
||||
return $cache[$key][$text];
|
||||
}
|
||||
|
||||
$width = $this->canvas->get_text_width($text, $font, $size, $wordSpacing, $charSpacing);
|
||||
|
||||
if ($useCache) {
|
||||
$cache[$key][$text] = $width;
|
||||
}
|
||||
|
||||
return $width;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $font
|
||||
* @param $size
|
||||
* @return float
|
||||
* @deprecated
|
||||
*/
|
||||
public function get_font_height($font, $size)
|
||||
{
|
||||
return $this->getFontHeight($font, $size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates font height, in points
|
||||
*
|
||||
* @param string $font The font file to use
|
||||
* @param float $size The font size, in points
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function getFontHeight($font, float $size): float
|
||||
{
|
||||
return $this->canvas->get_font_height($font, $size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates font baseline, in points
|
||||
*
|
||||
* @param string $font The font file to use
|
||||
* @param float $size The font size, in points
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function getFontBaseline($font, float $size): float
|
||||
{
|
||||
return $this->canvas->get_font_baseline($font, $size);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $family_raw
|
||||
* @param string $subtype_raw
|
||||
* @return string
|
||||
* @deprecated
|
||||
*/
|
||||
public function get_font($family_raw, $subtype_raw = "normal")
|
||||
{
|
||||
return $this->getFont($family_raw, $subtype_raw);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 Options::defaultFont}) is used. The font file returned
|
||||
* is the absolute pathname to the font file on the system.
|
||||
*
|
||||
* @param string|null $familyRaw
|
||||
* @param string $subtypeRaw
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getFont($familyRaw, $subtypeRaw = "normal")
|
||||
{
|
||||
static $cache = [];
|
||||
|
||||
if (isset($cache[$familyRaw][$subtypeRaw])) {
|
||||
return $cache[$familyRaw][$subtypeRaw];
|
||||
}
|
||||
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
$subtype = strtolower($subtypeRaw);
|
||||
|
||||
$families = $this->getFontFamilies();
|
||||
if ($familyRaw) {
|
||||
$family = str_replace(["'", '"'], "", strtolower($familyRaw));
|
||||
|
||||
if (isset($families[$family][$subtype])) {
|
||||
return $cache[$familyRaw][$subtypeRaw] = $families[$family][$subtype];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
$fallback_families = [strtolower($this->options->getDefaultFont()), "serif"];
|
||||
foreach ($fallback_families as $family) {
|
||||
if (isset($families[$family][$subtype])) {
|
||||
return $cache[$familyRaw][$subtypeRaw] = $families[$family][$subtype];
|
||||
}
|
||||
|
||||
if (!isset($families[$family])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$family = $families[$family];
|
||||
|
||||
foreach ($family as $sub => $font) {
|
||||
if (strpos($subtype, $sub) !== false) {
|
||||
return $cache[$familyRaw][$subtypeRaw] = $font;
|
||||
}
|
||||
}
|
||||
|
||||
if ($subtype !== "normal") {
|
||||
foreach ($family as $sub => $font) {
|
||||
if ($sub !== "normal") {
|
||||
return $cache[$familyRaw][$subtypeRaw] = $font;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$subtype = "normal";
|
||||
|
||||
if (isset($family[$subtype])) {
|
||||
return $cache[$familyRaw][$subtypeRaw] = $family[$subtype];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $family
|
||||
* @return null|string
|
||||
* @deprecated
|
||||
*/
|
||||
public function get_family($family)
|
||||
{
|
||||
return $this->getFamily($family);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $family
|
||||
* @return null|string
|
||||
*/
|
||||
public function getFamily($family)
|
||||
{
|
||||
$family = str_replace(["'", '"'], "", mb_strtolower($family));
|
||||
$families = $this->getFontFamilies();
|
||||
|
||||
if (isset($families[$family])) {
|
||||
return $families[$family];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $type
|
||||
* @return string
|
||||
* @deprecated
|
||||
*/
|
||||
public function get_type($type)
|
||||
{
|
||||
return $this->getType($type);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $type
|
||||
* @return string
|
||||
*/
|
||||
public function getType($type)
|
||||
{
|
||||
if (preg_match('/bold/i', $type)) {
|
||||
$weight = 700;
|
||||
} elseif (preg_match('/([1-9]00)/', $type, $match)) {
|
||||
$weight = (int)$match[0];
|
||||
} else {
|
||||
$weight = 400;
|
||||
}
|
||||
$weight = $weight === 400 ? 'normal' : $weight;
|
||||
$weight = $weight === 700 ? 'bold' : $weight;
|
||||
|
||||
$style = preg_match('/italic|oblique/i', $type) ? 'italic' : null;
|
||||
|
||||
if ($weight === 'normal' && $style !== null) {
|
||||
return $style;
|
||||
}
|
||||
|
||||
return $style === null
|
||||
? $weight
|
||||
: $weight.'_'.$style;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
* @deprecated
|
||||
*/
|
||||
public function get_font_families()
|
||||
{
|
||||
return $this->getFontFamilies();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current font lookup table
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getFontFamilies()
|
||||
{
|
||||
if (!isset($this->fontFamilies)) {
|
||||
$this->setFontFamilies();
|
||||
}
|
||||
return $this->fontFamilies;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert loaded fonts to font lookup table
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function setFontFamilies()
|
||||
{
|
||||
$fontFamilies = [];
|
||||
if (isset($this->bundledFonts) && is_array($this->bundledFonts)) {
|
||||
foreach ($this->bundledFonts as $family => $variants) {
|
||||
if (!isset($fontFamilies[$family])) {
|
||||
$fontFamilies[$family] = array_map(function ($variant) {
|
||||
return $this->getOptions()->getRootDir() . '/lib/fonts/' . $variant;
|
||||
}, $variants);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isset($this->userFonts) && is_array($this->userFonts)) {
|
||||
foreach ($this->userFonts as $family => $variants) {
|
||||
$fontFamilies[$family] = array_map(function ($variant) {
|
||||
$variantName = basename($variant);
|
||||
if ($variantName === $variant) {
|
||||
return $this->getOptions()->getFontDir() . '/' . $variant;
|
||||
}
|
||||
return $variant;
|
||||
}, $variants);
|
||||
}
|
||||
}
|
||||
$this->fontFamilies = $fontFamilies;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $fontname
|
||||
* @param mixed $entry
|
||||
* @deprecated
|
||||
*/
|
||||
public function set_font_family($fontname, $entry)
|
||||
{
|
||||
$this->setFontFamily($fontname, $entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $fontname
|
||||
* @param mixed $entry
|
||||
*/
|
||||
public function setFontFamily($fontname, $entry)
|
||||
{
|
||||
$this->userFonts[mb_strtolower($fontname)] = $entry;
|
||||
$this->saveFontFamilies();
|
||||
unset($this->fontFamilies);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getUserFontsFilePath()
|
||||
{
|
||||
return $this->options->getFontDir() . '/' . self::USER_FONTS_FILE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Options $options
|
||||
* @return $this
|
||||
*/
|
||||
public function setOptions(Options $options)
|
||||
{
|
||||
$this->options = $options;
|
||||
unset($this->fontFamilies);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Options
|
||||
*/
|
||||
public function getOptions()
|
||||
{
|
||||
return $this->options;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Canvas $canvas
|
||||
* @return $this
|
||||
*/
|
||||
public function setCanvas(Canvas $canvas)
|
||||
{
|
||||
$this->canvas = $canvas;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Canvas
|
||||
*/
|
||||
public function getCanvas()
|
||||
{
|
||||
return $this->canvas;
|
||||
}
|
||||
}
|
||||
1217
system/vendor/pancakeapp/dompdf/src/Frame.php
vendored
Executable file
1217
system/vendor/pancakeapp/dompdf/src/Frame.php
vendored
Executable file
File diff suppressed because it is too large
Load Diff
262
system/vendor/pancakeapp/dompdf/src/Frame/Factory.php
vendored
Executable file
262
system/vendor/pancakeapp/dompdf/src/Frame/Factory.php
vendored
Executable file
@@ -0,0 +1,262 @@
|
||||
<?php
|
||||
/**
|
||||
* @package dompdf
|
||||
* @link https://github.com/dompdf/dompdf
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
*/
|
||||
namespace Dompdf\Frame;
|
||||
|
||||
use Dompdf\Dompdf;
|
||||
use Dompdf\Exception;
|
||||
use Dompdf\Frame;
|
||||
use Dompdf\FrameDecorator\AbstractFrameDecorator;
|
||||
use Dompdf\FrameDecorator\Page as PageFrameDecorator;
|
||||
use Dompdf\FrameReflower\Page as PageFrameReflower;
|
||||
use Dompdf\Positioner\AbstractPositioner;
|
||||
use DOMXPath;
|
||||
|
||||
/**
|
||||
* Contains frame decorating logic
|
||||
*
|
||||
* This class is responsible for assigning the correct {@link AbstractFrameDecorator},
|
||||
* {@link AbstractPositioner}, and {@link AbstractFrameReflower} 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)
|
||||
*
|
||||
* @package dompdf
|
||||
*/
|
||||
class Factory
|
||||
{
|
||||
|
||||
/**
|
||||
* Array of positioners for specific frame types
|
||||
*
|
||||
* @var AbstractPositioner[]
|
||||
*/
|
||||
protected static $_positioners;
|
||||
|
||||
/**
|
||||
* Decorate the root Frame
|
||||
*
|
||||
* @param Frame $root The frame to decorate
|
||||
* @param Dompdf $dompdf The dompdf instance
|
||||
*
|
||||
* @return PageFrameDecorator
|
||||
*/
|
||||
public static function decorate_root(Frame $root, Dompdf $dompdf): PageFrameDecorator
|
||||
{
|
||||
$frame = new PageFrameDecorator($root, $dompdf);
|
||||
$frame->set_reflower(new PageFrameReflower($frame));
|
||||
$root->set_decorator($frame);
|
||||
|
||||
return $frame;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decorate a Frame
|
||||
*
|
||||
* @param Frame $frame The frame to decorate
|
||||
* @param Dompdf $dompdf The dompdf instance
|
||||
* @param Frame|null $root The root of the frame
|
||||
*
|
||||
* @throws Exception
|
||||
* @return AbstractFrameDecorator|null
|
||||
* FIXME: this is admittedly a little smelly...
|
||||
*/
|
||||
public static function decorate_frame(Frame $frame, Dompdf $dompdf, ?Frame $root = null): ?AbstractFrameDecorator
|
||||
{
|
||||
$style = $frame->get_style();
|
||||
$display = $style->display;
|
||||
|
||||
switch ($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->is_text_node()) {
|
||||
$decorator = "Text";
|
||||
$reflower = "Text";
|
||||
} 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 = "NullPositioner";
|
||||
$decorator = "TableRowGroup";
|
||||
$reflower = "TableRowGroup";
|
||||
break;
|
||||
|
||||
case "table-row":
|
||||
$positioner = "NullPositioner";
|
||||
$decorator = "TableRow";
|
||||
$reflower = "TableRow";
|
||||
break;
|
||||
|
||||
case "table-cell":
|
||||
$positioner = "TableCell";
|
||||
$decorator = "TableCell";
|
||||
$reflower = "TableCell";
|
||||
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 = "ListBullet";
|
||||
}
|
||||
|
||||
if ($style->list_style_image !== "none") {
|
||||
$decorator = "ListBulletImage";
|
||||
} else {
|
||||
$decorator = "ListBullet";
|
||||
}
|
||||
|
||||
$reflower = "ListBullet";
|
||||
break;
|
||||
|
||||
case "-dompdf-image":
|
||||
$positioner = "Inline";
|
||||
$decorator = "Image";
|
||||
$reflower = "Image";
|
||||
break;
|
||||
|
||||
case "-dompdf-br":
|
||||
$positioner = "Inline";
|
||||
$decorator = "Inline";
|
||||
$reflower = "Inline";
|
||||
break;
|
||||
|
||||
default:
|
||||
case "none":
|
||||
if ($style->_dompdf_keep !== "yes") {
|
||||
// Remove the node and the frame
|
||||
$frame->get_parent()->remove_child($frame);
|
||||
return null;
|
||||
}
|
||||
|
||||
$positioner = "NullPositioner";
|
||||
$decorator = "NullFrameDecorator";
|
||||
$reflower = "NullFrameReflower";
|
||||
break;
|
||||
}
|
||||
|
||||
// Handle CSS position
|
||||
$position = $style->position;
|
||||
|
||||
if ($position === "absolute") {
|
||||
$positioner = "Absolute";
|
||||
} elseif ($position === "fixed") {
|
||||
$positioner = "Fixed";
|
||||
}
|
||||
|
||||
$node = $frame->get_node();
|
||||
|
||||
// Handle nodeName
|
||||
if ($node->nodeName === "img") {
|
||||
$style->set_prop("display", "-dompdf-image");
|
||||
$decorator = "Image";
|
||||
$reflower = "Image";
|
||||
}
|
||||
|
||||
$decorator = "Dompdf\\FrameDecorator\\$decorator";
|
||||
$reflower = "Dompdf\\FrameReflower\\$reflower";
|
||||
|
||||
/** @var AbstractFrameDecorator $deco */
|
||||
$deco = new $decorator($frame, $dompdf);
|
||||
|
||||
$deco->set_positioner(self::getPositionerInstance($positioner));
|
||||
$deco->set_reflower(new $reflower($deco, $dompdf->getFontMetrics()));
|
||||
|
||||
if ($root) {
|
||||
$deco->set_root($root);
|
||||
}
|
||||
|
||||
if ($display === "list-item") {
|
||||
// Insert a list-bullet frame
|
||||
$xml = $dompdf->getDom();
|
||||
$bullet_node = $xml->createElement("bullet"); // arbitrary choice
|
||||
$b_f = new Frame($bullet_node);
|
||||
|
||||
$node = $frame->get_node();
|
||||
$parent_node = $node->parentNode;
|
||||
if ($parent_node && $parent_node instanceof \DOMElement) {
|
||||
if (!$parent_node->hasAttribute("dompdf-children-count")) {
|
||||
$xpath = new DOMXPath($xml);
|
||||
$count = $xpath->query("li", $parent_node)->length;
|
||||
$parent_node->setAttribute("dompdf-children-count", $count);
|
||||
}
|
||||
|
||||
if (is_numeric($node->getAttribute("value"))) {
|
||||
$index = intval($node->getAttribute("value"));
|
||||
} else {
|
||||
if (!$parent_node->hasAttribute("dompdf-counter")) {
|
||||
$index = ($parent_node->hasAttribute("start") ? $parent_node->getAttribute("start") : 1);
|
||||
} else {
|
||||
$index = (int)$parent_node->getAttribute("dompdf-counter") + 1;
|
||||
}
|
||||
}
|
||||
|
||||
$parent_node->setAttribute("dompdf-counter", $index);
|
||||
$bullet_node->setAttribute("dompdf-counter", $index);
|
||||
}
|
||||
|
||||
$new_style = $dompdf->getCss()->create_style();
|
||||
$new_style->set_prop("display", "-dompdf-list-bullet");
|
||||
$new_style->inherit($style);
|
||||
$b_f->set_style($new_style);
|
||||
|
||||
$deco->prepend_child(Factory::decorate_frame($b_f, $dompdf, $root));
|
||||
}
|
||||
|
||||
return $deco;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates Positioners
|
||||
*
|
||||
* @param string $type Type of positioner to use
|
||||
*
|
||||
* @return AbstractPositioner
|
||||
*/
|
||||
protected static function getPositionerInstance(string $type): AbstractPositioner
|
||||
{
|
||||
if (!isset(self::$_positioners[$type])) {
|
||||
$class = '\\Dompdf\\Positioner\\'.$type;
|
||||
self::$_positioners[$type] = new $class();
|
||||
}
|
||||
return self::$_positioners[$type];
|
||||
}
|
||||
}
|
||||
100
system/vendor/pancakeapp/dompdf/src/Frame/FrameListIterator.php
vendored
Executable file
100
system/vendor/pancakeapp/dompdf/src/Frame/FrameListIterator.php
vendored
Executable file
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
/**
|
||||
* @package dompdf
|
||||
* @link https://github.com/dompdf/dompdf
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
*/
|
||||
namespace Dompdf\Frame;
|
||||
|
||||
use Iterator;
|
||||
use Dompdf\Frame;
|
||||
|
||||
/**
|
||||
* Linked-list Iterator
|
||||
*
|
||||
* Returns children in order and allows for the list to change during iteration,
|
||||
* provided the changes occur to or after the current element.
|
||||
*
|
||||
* @package dompdf
|
||||
*/
|
||||
class FrameListIterator implements Iterator
|
||||
{
|
||||
/**
|
||||
* @var Frame
|
||||
*/
|
||||
protected $parent;
|
||||
|
||||
/**
|
||||
* @var Frame|null
|
||||
*/
|
||||
protected $cur;
|
||||
|
||||
/**
|
||||
* @var Frame|null
|
||||
*/
|
||||
protected $prev;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $num;
|
||||
|
||||
/**
|
||||
* @param Frame $frame
|
||||
*/
|
||||
public function __construct(Frame $frame)
|
||||
{
|
||||
$this->parent = $frame;
|
||||
$this->rewind();
|
||||
}
|
||||
|
||||
public function rewind(): void
|
||||
{
|
||||
$this->cur = $this->parent->get_first_child();
|
||||
$this->prev = null;
|
||||
$this->num = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function valid(): bool
|
||||
{
|
||||
return $this->cur !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function key(): int
|
||||
{
|
||||
return $this->num;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Frame|null
|
||||
*/
|
||||
public function current(): ?Frame
|
||||
{
|
||||
return $this->cur;
|
||||
}
|
||||
|
||||
public function next(): void
|
||||
{
|
||||
if ($this->cur === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->cur->get_parent() === $this->parent) {
|
||||
$this->prev = $this->cur;
|
||||
$this->cur = $this->cur->get_next_sibling();
|
||||
$this->num++;
|
||||
} else {
|
||||
// Continue from the previous child if the current frame has been
|
||||
// moved to another parent
|
||||
$this->cur = $this->prev !== null
|
||||
? $this->prev->get_next_sibling()
|
||||
: $this->parent->get_first_child();
|
||||
}
|
||||
}
|
||||
}
|
||||
324
system/vendor/pancakeapp/dompdf/src/Frame/FrameTree.php
vendored
Executable file
324
system/vendor/pancakeapp/dompdf/src/Frame/FrameTree.php
vendored
Executable file
@@ -0,0 +1,324 @@
|
||||
<?php
|
||||
/**
|
||||
* @package dompdf
|
||||
* @link https://github.com/dompdf/dompdf
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
*/
|
||||
namespace Dompdf\Frame;
|
||||
|
||||
use DOMDocument;
|
||||
use DOMNode;
|
||||
use DOMElement;
|
||||
use DOMXPath;
|
||||
|
||||
use Dompdf\Exception;
|
||||
use Dompdf\Frame;
|
||||
use IteratorAggregate;
|
||||
|
||||
/**
|
||||
* Represents an entire document as a tree of frames
|
||||
*
|
||||
* 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 capabilities for
|
||||
* styling and layout.
|
||||
*
|
||||
* @package dompdf
|
||||
*/
|
||||
class FrameTree implements IteratorAggregate
|
||||
{
|
||||
/**
|
||||
* Tags to ignore while parsing the tree
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $HIDDEN_TAGS = [
|
||||
"area",
|
||||
"base",
|
||||
"basefont",
|
||||
"head",
|
||||
"style",
|
||||
"meta",
|
||||
"title",
|
||||
"colgroup",
|
||||
"noembed",
|
||||
"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
|
||||
*/
|
||||
public function __construct(DomDocument $dom)
|
||||
{
|
||||
$this->_dom = $dom;
|
||||
$this->_root = null;
|
||||
$this->_registry = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the DOMDocument object representing the current html document
|
||||
*
|
||||
* @return DOMDocument
|
||||
*/
|
||||
public function get_dom()
|
||||
{
|
||||
return $this->_dom;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the root frame of the tree
|
||||
*
|
||||
* @return Frame
|
||||
*/
|
||||
public function get_root()
|
||||
{
|
||||
return $this->_root;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a specific frame given its id
|
||||
*
|
||||
* @param string $id
|
||||
*
|
||||
* @return Frame|null
|
||||
*/
|
||||
public function get_frame($id)
|
||||
{
|
||||
return isset($this->_registry[$id]) ? $this->_registry[$id] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a post-order iterator for all frames in the tree
|
||||
*
|
||||
* @deprecated Iterate the tree directly instead
|
||||
* @return FrameTreeIterator
|
||||
*/
|
||||
public function get_frames(): FrameTreeIterator
|
||||
{
|
||||
return new FrameTreeIterator($this->_root);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a post-order iterator for all frames in the tree
|
||||
*
|
||||
* @return FrameTreeIterator
|
||||
*/
|
||||
public function getIterator(): FrameTreeIterator
|
||||
{
|
||||
return new FrameTreeIterator($this->_root);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the tree
|
||||
*/
|
||||
public function build_tree()
|
||||
{
|
||||
$html = $this->_dom->getElementsByTagName("html")->item(0);
|
||||
if (is_null($html)) {
|
||||
$html = $this->_dom->firstChild;
|
||||
}
|
||||
|
||||
if (is_null($html)) {
|
||||
throw new Exception("Requested HTML document contains no data.");
|
||||
}
|
||||
|
||||
$this->fix_tables();
|
||||
|
||||
$this->_root = $this->_build_tree_r($html);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds missing TBODYs around TR
|
||||
*/
|
||||
protected function fix_tables()
|
||||
{
|
||||
$xp = new DOMXPath($this->_dom);
|
||||
|
||||
// Move table caption before the table
|
||||
// FIXME find a better way to deal with it...
|
||||
$captions = $xp->query('//table/caption');
|
||||
foreach ($captions as $caption) {
|
||||
$table = $caption->parentNode;
|
||||
$table->parentNode->insertBefore($caption, $table);
|
||||
}
|
||||
|
||||
$firstRows = $xp->query('//table/tr[1]');
|
||||
/** @var DOMElement $tableChild */
|
||||
foreach ($firstRows as $tableChild) {
|
||||
$tbody = $this->_dom->createElement('tbody');
|
||||
$tableNode = $tableChild->parentNode;
|
||||
do {
|
||||
if ($tableChild->nodeName === 'tr') {
|
||||
$tmpNode = $tableChild;
|
||||
$tableChild = $tableChild->nextSibling;
|
||||
$tableNode->removeChild($tmpNode);
|
||||
$tbody->appendChild($tmpNode);
|
||||
} else {
|
||||
if ($tbody->hasChildNodes() === true) {
|
||||
$tableNode->insertBefore($tbody, $tableChild);
|
||||
$tbody = $this->_dom->createElement('tbody');
|
||||
}
|
||||
$tableChild = $tableChild->nextSibling;
|
||||
}
|
||||
} while ($tableChild);
|
||||
if ($tbody->hasChildNodes() === true) {
|
||||
$tableNode->appendChild($tbody);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: temporary hack, preferably we will improve rendering of sequential #text nodes
|
||||
/**
|
||||
* Remove a child from a node
|
||||
*
|
||||
* Remove a child from a node. If the removed node results in two
|
||||
* adjacent #text nodes then combine them.
|
||||
*
|
||||
* @param DOMNode $node the current DOMNode being considered
|
||||
* @param array $children an array of nodes that are the children of $node
|
||||
* @param int $index index from the $children array of the node to remove
|
||||
*/
|
||||
protected function _remove_node(DOMNode $node, array &$children, $index)
|
||||
{
|
||||
$child = $children[$index];
|
||||
$previousChild = $child->previousSibling;
|
||||
$nextChild = $child->nextSibling;
|
||||
$node->removeChild($child);
|
||||
if (isset($previousChild, $nextChild)) {
|
||||
if ($previousChild->nodeName === "#text" && $nextChild->nodeName === "#text") {
|
||||
$previousChild->nodeValue .= $nextChild->nodeValue;
|
||||
$this->_remove_node($node, $children, $index+1);
|
||||
}
|
||||
}
|
||||
array_splice($children, $index, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
// Store the children in an array so that the tree can be modified
|
||||
$children = [];
|
||||
$length = $node->childNodes->length;
|
||||
for ($i = 0; $i < $length; $i++) {
|
||||
$children[] = $node->childNodes->item($i);
|
||||
}
|
||||
$index = 0;
|
||||
// INFO: We don't advance $index if a node is removed to avoid skipping nodes
|
||||
while ($index < count($children)) {
|
||||
$child = $children[$index];
|
||||
$nodeName = strtolower($child->nodeName);
|
||||
|
||||
// Skip non-displaying nodes
|
||||
if (in_array($nodeName, self::$HIDDEN_TAGS)) {
|
||||
if ($nodeName !== "head" && $nodeName !== "style") {
|
||||
$this->_remove_node($node, $children, $index);
|
||||
} else {
|
||||
$index++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// Skip empty text nodes
|
||||
if ($nodeName === "#text" && $child->nodeValue === "") {
|
||||
$this->_remove_node($node, $children, $index);
|
||||
continue;
|
||||
}
|
||||
// Skip empty image nodes
|
||||
if ($nodeName === "img" && $child->getAttribute("src") === "") {
|
||||
$this->_remove_node($node, $children, $index);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_object($child)) {
|
||||
$frame->append_child($this->_build_tree_r($child), false);
|
||||
}
|
||||
$index++;
|
||||
}
|
||||
|
||||
return $frame;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DOMElement $node
|
||||
* @param DOMElement $new_node
|
||||
* @param string $pos
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function insert_node(DOMElement $node, DOMElement $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 ($parent) {
|
||||
if ($pos === "before") {
|
||||
$parent->prepend_child($frame, false);
|
||||
} else {
|
||||
$parent->append_child($frame, false);
|
||||
}
|
||||
}
|
||||
|
||||
return $frame_id;
|
||||
}
|
||||
}
|
||||
88
system/vendor/pancakeapp/dompdf/src/Frame/FrameTreeIterator.php
vendored
Executable file
88
system/vendor/pancakeapp/dompdf/src/Frame/FrameTreeIterator.php
vendored
Executable file
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
/**
|
||||
* @package dompdf
|
||||
* @link https://github.com/dompdf/dompdf
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
*/
|
||||
namespace Dompdf\Frame;
|
||||
|
||||
use Iterator;
|
||||
use Dompdf\Frame;
|
||||
|
||||
/**
|
||||
* Pre-order Iterator
|
||||
*
|
||||
* Returns frames in preorder traversal order (parent then children)
|
||||
*
|
||||
* @package dompdf
|
||||
*/
|
||||
class FrameTreeIterator implements Iterator
|
||||
{
|
||||
/**
|
||||
* @var Frame
|
||||
*/
|
||||
protected $_root;
|
||||
|
||||
/**
|
||||
* @var Frame[]
|
||||
*/
|
||||
protected $_stack = [];
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $_num;
|
||||
|
||||
/**
|
||||
* @param Frame $root
|
||||
*/
|
||||
public function __construct(Frame $root)
|
||||
{
|
||||
$this->_stack[] = $this->_root = $root;
|
||||
$this->_num = 0;
|
||||
}
|
||||
|
||||
public function rewind(): void
|
||||
{
|
||||
$this->_stack = [$this->_root];
|
||||
$this->_num = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function valid(): bool
|
||||
{
|
||||
return count($this->_stack) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function key(): int
|
||||
{
|
||||
return $this->_num;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Frame
|
||||
*/
|
||||
public function current(): Frame
|
||||
{
|
||||
return end($this->_stack);
|
||||
}
|
||||
|
||||
public function next(): void
|
||||
{
|
||||
$b = array_pop($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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
923
system/vendor/pancakeapp/dompdf/src/FrameDecorator/AbstractFrameDecorator.php
vendored
Executable file
923
system/vendor/pancakeapp/dompdf/src/FrameDecorator/AbstractFrameDecorator.php
vendored
Executable file
@@ -0,0 +1,923 @@
|
||||
<?php
|
||||
/**
|
||||
* @package dompdf
|
||||
* @link https://github.com/dompdf/dompdf
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
*/
|
||||
namespace Dompdf\FrameDecorator;
|
||||
|
||||
use DOMElement;
|
||||
use DOMNode;
|
||||
use Dompdf\Helpers;
|
||||
use Dompdf\Dompdf;
|
||||
use Dompdf\Exception;
|
||||
use Dompdf\Frame;
|
||||
use Dompdf\Frame\Factory;
|
||||
use Dompdf\Frame\FrameListIterator;
|
||||
use Dompdf\Frame\FrameTreeIterator;
|
||||
use Dompdf\FrameReflower\AbstractFrameReflower;
|
||||
use Dompdf\Css\Style;
|
||||
use Dompdf\Positioner\AbstractPositioner;
|
||||
|
||||
/**
|
||||
* Base AbstractFrameDecorator class
|
||||
*
|
||||
* @package dompdf
|
||||
*/
|
||||
abstract class AbstractFrameDecorator extends Frame
|
||||
{
|
||||
const DEFAULT_COUNTER = "-dompdf-default-counter";
|
||||
|
||||
/**
|
||||
* array([id] => counter_value) (for generated content)
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $_counters = [];
|
||||
|
||||
/**
|
||||
* The root node of the DOM tree
|
||||
*
|
||||
* @var Frame
|
||||
*/
|
||||
protected $_root;
|
||||
|
||||
/**
|
||||
* The decorated frame
|
||||
*
|
||||
* @var Frame
|
||||
*/
|
||||
protected $_frame;
|
||||
|
||||
/**
|
||||
* AbstractPositioner object used to position this frame (Strategy pattern)
|
||||
*
|
||||
* @var AbstractPositioner
|
||||
*/
|
||||
protected $_positioner;
|
||||
|
||||
/**
|
||||
* Reflower object used to calculate frame dimensions (Strategy pattern)
|
||||
*
|
||||
* @var AbstractFrameReflower
|
||||
*/
|
||||
protected $_reflower;
|
||||
|
||||
/**
|
||||
* Reference to the current dompdf instance
|
||||
*
|
||||
* @var Dompdf
|
||||
*/
|
||||
protected $_dompdf;
|
||||
|
||||
/**
|
||||
* First block parent
|
||||
*
|
||||
* @var Block
|
||||
*/
|
||||
private $_block_parent;
|
||||
|
||||
/**
|
||||
* First positioned parent (position: relative | absolute | fixed)
|
||||
*
|
||||
* @var AbstractFrameDecorator
|
||||
*/
|
||||
private $_positioned_parent;
|
||||
|
||||
/**
|
||||
* Cache for the get_parent while loop results
|
||||
*
|
||||
* @var Frame
|
||||
*/
|
||||
private $_cached_parent;
|
||||
|
||||
/**
|
||||
* Whether generated content and counters have been set.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $content_set = false;
|
||||
|
||||
/**
|
||||
* Whether the frame has been split
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $is_split = false;
|
||||
|
||||
/**
|
||||
* Whether the frame is a split-off frame
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $is_split_off = false;
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param Frame $frame The decoration target
|
||||
* @param Dompdf $dompdf The Dompdf object
|
||||
*/
|
||||
function __construct(Frame $frame, Dompdf $dompdf)
|
||||
{
|
||||
$this->_frame = $frame;
|
||||
$this->_root = null;
|
||||
$this->_dompdf = $dompdf;
|
||||
$frame->set_decorator($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* "Destructor": forcibly 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 AbstractFrameDecorator
|
||||
*/
|
||||
function copy(DOMNode $node)
|
||||
{
|
||||
$frame = new Frame($node);
|
||||
$style = clone $this->_frame->get_style();
|
||||
|
||||
$style->reset();
|
||||
$frame->set_style($style);
|
||||
|
||||
if ($node instanceof DOMElement && $node->hasAttribute("id")) {
|
||||
$node->setAttribute("data-dompdf-original-id", $node->getAttribute("id"));
|
||||
$node->removeAttribute("id");
|
||||
}
|
||||
|
||||
return Factory::decorate_frame($frame, $this->_dompdf, $this->_root);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a deep copy: copy this node and all children
|
||||
*
|
||||
* @return AbstractFrameDecorator
|
||||
*/
|
||||
function deep_copy()
|
||||
{
|
||||
$node = $this->_frame->get_node()->cloneNode();
|
||||
$frame = new Frame($node);
|
||||
$style = clone $this->_frame->get_style();
|
||||
|
||||
$style->reset();
|
||||
$frame->set_style($style);
|
||||
|
||||
if ($node instanceof DOMElement && $node->hasAttribute("id")) {
|
||||
$node->setAttribute("data-dompdf-original-id", $node->getAttribute("id"));
|
||||
$node->removeAttribute("id");
|
||||
}
|
||||
|
||||
$deco = Factory::decorate_frame($frame, $this->_dompdf, $this->_root);
|
||||
|
||||
foreach ($this->get_children() as $child) {
|
||||
$deco->append_child($child->deep_copy());
|
||||
}
|
||||
|
||||
return $deco;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an anonymous child frame, inheriting styles from this frame.
|
||||
*
|
||||
* @param string $node_name
|
||||
* @param string $display
|
||||
*
|
||||
* @return AbstractFrameDecorator
|
||||
*/
|
||||
public function create_anonymous_child(string $node_name, string $display): AbstractFrameDecorator
|
||||
{
|
||||
$style = $this->get_style();
|
||||
$child_style = $style->get_stylesheet()->create_style();
|
||||
$child_style->set_prop("display", $display);
|
||||
$child_style->inherit($style);
|
||||
|
||||
$node = $this->get_node()->ownerDocument->createElement($node_name);
|
||||
$frame = new Frame($node);
|
||||
$frame->set_style($child_style);
|
||||
|
||||
return Factory::decorate_frame($frame, $this->_dompdf, $this->_root);
|
||||
}
|
||||
|
||||
function reset()
|
||||
{
|
||||
$this->_frame->reset();
|
||||
$this->_reflower->reset();
|
||||
$this->reset_generated_content();
|
||||
$this->revert_counter_increment();
|
||||
|
||||
$this->content_set = false;
|
||||
$this->_counters = [];
|
||||
|
||||
// clear parent lookup caches
|
||||
$this->_cached_parent = null;
|
||||
$this->_block_parent = null;
|
||||
$this->_positioned_parent = null;
|
||||
|
||||
// Reset all children
|
||||
foreach ($this->get_children() as $child) {
|
||||
$child->reset();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If this represents a generated node then child nodes represent generated
|
||||
* content. Remove the children since the content will be generated next
|
||||
* time this frame is reflowed.
|
||||
*/
|
||||
protected function reset_generated_content(): void
|
||||
{
|
||||
if ($this->content_set
|
||||
&& $this->get_node()->nodeName === "dompdf_generated"
|
||||
) {
|
||||
$content = $this->get_style()->content;
|
||||
|
||||
if ($content !== "normal" && $content !== "none") {
|
||||
foreach ($this->get_children() as $child) {
|
||||
$this->remove_child($child);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrement any counters that were incremented on the current node, unless
|
||||
* that node is the body.
|
||||
*/
|
||||
protected function revert_counter_increment(): void
|
||||
{
|
||||
if ($this->content_set
|
||||
&& $this->get_node()->nodeName !== "body"
|
||||
&& ($decrement = $this->get_style()->counter_increment) !== "none"
|
||||
) {
|
||||
$this->decrement_counters($decrement);
|
||||
}
|
||||
}
|
||||
|
||||
// Getters -----------
|
||||
|
||||
function get_id()
|
||||
{
|
||||
return $this->_frame->get_id();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Frame
|
||||
*/
|
||||
function get_frame()
|
||||
{
|
||||
return $this->_frame;
|
||||
}
|
||||
|
||||
function get_node()
|
||||
{
|
||||
return $this->_frame->get_node();
|
||||
}
|
||||
|
||||
function get_style()
|
||||
{
|
||||
return $this->_frame->get_style();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
function get_original_style()
|
||||
{
|
||||
return $this->_frame->get_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;
|
||||
}
|
||||
|
||||
public function get_margin_width(): float
|
||||
{
|
||||
return $this->_frame->get_margin_width();
|
||||
}
|
||||
|
||||
public function get_margin_height(): float
|
||||
{
|
||||
return $this->_frame->get_margin_height();
|
||||
}
|
||||
|
||||
public function get_content_box(): array
|
||||
{
|
||||
return $this->_frame->get_content_box();
|
||||
}
|
||||
|
||||
public function get_padding_box(): array
|
||||
{
|
||||
return $this->_frame->get_padding_box();
|
||||
}
|
||||
|
||||
public function get_border_box(): array
|
||||
{
|
||||
return $this->_frame->get_border_box();
|
||||
}
|
||||
|
||||
function set_id($id)
|
||||
{
|
||||
$this->_frame->set_id($id);
|
||||
}
|
||||
|
||||
public function set_style(Style $style): void
|
||||
{
|
||||
$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 is_auto_height()
|
||||
{
|
||||
return $this->_frame->is_auto_height();
|
||||
}
|
||||
|
||||
function is_auto_width()
|
||||
{
|
||||
return $this->_frame->is_auto_width();
|
||||
}
|
||||
|
||||
function __toString()
|
||||
{
|
||||
return $this->_frame->__toString();
|
||||
}
|
||||
|
||||
function prepend_child(Frame $child, $update_node = true)
|
||||
{
|
||||
while ($child instanceof AbstractFrameDecorator) {
|
||||
$child = $child->_frame;
|
||||
}
|
||||
|
||||
$this->_frame->prepend_child($child, $update_node);
|
||||
}
|
||||
|
||||
function append_child(Frame $child, $update_node = true)
|
||||
{
|
||||
while ($child instanceof AbstractFrameDecorator) {
|
||||
$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 AbstractFrameDecorator) {
|
||||
$new_child = $new_child->_frame;
|
||||
}
|
||||
|
||||
if ($ref instanceof AbstractFrameDecorator) {
|
||||
$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)
|
||||
{
|
||||
$insert_frame = $new_child;
|
||||
while ($insert_frame instanceof AbstractFrameDecorator) {
|
||||
$insert_frame = $insert_frame->_frame;
|
||||
}
|
||||
|
||||
$reference_frame = $ref;
|
||||
while ($reference_frame instanceof AbstractFrameDecorator) {
|
||||
$reference_frame = $reference_frame->_frame;
|
||||
}
|
||||
|
||||
$this->_frame->insert_child_after($insert_frame, $reference_frame, $update_node);
|
||||
}
|
||||
|
||||
function remove_child(Frame $child, $update_node = true)
|
||||
{
|
||||
while ($child instanceof AbstractFrameDecorator) {
|
||||
$child = $child->_frame;
|
||||
}
|
||||
|
||||
return $this->_frame->remove_child($child, $update_node);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $use_cache
|
||||
* @return AbstractFrameDecorator
|
||||
*/
|
||||
function get_parent($use_cache = true)
|
||||
{
|
||||
if ($use_cache && $this->_cached_parent) {
|
||||
return $this->_cached_parent;
|
||||
}
|
||||
$p = $this->_frame->get_parent();
|
||||
if ($p && $deco = $p->get_decorator()) {
|
||||
while ($tmp = $deco->get_decorator()) {
|
||||
$deco = $tmp;
|
||||
}
|
||||
|
||||
return $this->_cached_parent = $deco;
|
||||
} else {
|
||||
return $this->_cached_parent = $p;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return AbstractFrameDecorator
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return AbstractFrameDecorator
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return AbstractFrameDecorator
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return AbstractFrameDecorator
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return FrameListIterator<AbstractFrameDecorator>
|
||||
*/
|
||||
public function get_children(): FrameListIterator
|
||||
{
|
||||
return new FrameListIterator($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return FrameTreeIterator<AbstractFrameDecorator>
|
||||
*/
|
||||
function get_subtree(): FrameTreeIterator
|
||||
{
|
||||
return new FrameTreeIterator($this);
|
||||
}
|
||||
|
||||
function set_positioner(AbstractPositioner $posn)
|
||||
{
|
||||
$this->_positioner = $posn;
|
||||
if ($this->_frame instanceof AbstractFrameDecorator) {
|
||||
$this->_frame->set_positioner($posn);
|
||||
}
|
||||
}
|
||||
|
||||
function set_reflower(AbstractFrameReflower $reflower)
|
||||
{
|
||||
$this->_reflower = $reflower;
|
||||
if ($this->_frame instanceof AbstractFrameDecorator) {
|
||||
$this->_frame->set_reflower($reflower);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return AbstractPositioner
|
||||
*/
|
||||
function get_positioner()
|
||||
{
|
||||
return $this->_positioner;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return AbstractFrameReflower
|
||||
*/
|
||||
function get_reflower()
|
||||
{
|
||||
return $this->_reflower;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Frame $root
|
||||
*/
|
||||
function set_root(Frame $root)
|
||||
{
|
||||
$this->_root = $root;
|
||||
|
||||
if ($this->_frame instanceof AbstractFrameDecorator) {
|
||||
$this->_frame->set_root($root);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Page
|
||||
*/
|
||||
function get_root()
|
||||
{
|
||||
return $this->_root;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Block
|
||||
*/
|
||||
function find_block_parent()
|
||||
{
|
||||
// Find our nearest block level parent
|
||||
if (isset($this->_block_parent)) {
|
||||
return $this->_block_parent;
|
||||
}
|
||||
|
||||
$p = $this->get_parent();
|
||||
|
||||
while ($p) {
|
||||
if ($p->is_block()) {
|
||||
break;
|
||||
}
|
||||
|
||||
$p = $p->get_parent();
|
||||
}
|
||||
|
||||
return $this->_block_parent = $p;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return AbstractFrameDecorator
|
||||
*/
|
||||
function find_positioned_parent()
|
||||
{
|
||||
// Find our nearest relative positioned parent
|
||||
if (isset($this->_positioned_parent)) {
|
||||
return $this->_positioned_parent;
|
||||
}
|
||||
|
||||
$p = $this->get_parent();
|
||||
while ($p) {
|
||||
if ($p->is_positioned()) {
|
||||
break;
|
||||
}
|
||||
|
||||
$p = $p->get_parent();
|
||||
}
|
||||
|
||||
if (!$p) {
|
||||
$p = $this->_root;
|
||||
}
|
||||
|
||||
return $this->_positioned_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|null $child
|
||||
* @param bool $page_break
|
||||
* @param bool $forced Whether the page break is forced.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function split(?Frame $child = null, bool $page_break = false, bool $forced = false): void
|
||||
{
|
||||
if (is_null($child)) {
|
||||
$this->get_parent()->split($this, $page_break, $forced);
|
||||
return;
|
||||
}
|
||||
|
||||
if ($child->get_parent() !== $this) {
|
||||
throw new Exception("Unable to split: frame is not a child of this one.");
|
||||
}
|
||||
|
||||
$this->revert_counter_increment();
|
||||
|
||||
$node = $this->_frame->get_node();
|
||||
$split = $this->copy($node->cloneNode());
|
||||
|
||||
$style = $this->_frame->get_style();
|
||||
$split_style = $split->get_style();
|
||||
|
||||
// Truncate the box decoration at the split, except for the body
|
||||
if ($node->nodeName !== "body") {
|
||||
// Clear bottom decoration of original frame
|
||||
$style->margin_bottom = 0.0;
|
||||
$style->padding_bottom = 0.0;
|
||||
$style->border_bottom_width = 0.0;
|
||||
$style->border_bottom_left_radius = 0.0;
|
||||
$style->border_bottom_right_radius = 0.0;
|
||||
|
||||
// Clear top decoration of split frame
|
||||
$split_style->margin_top = 0.0;
|
||||
$split_style->padding_top = 0.0;
|
||||
$split_style->border_top_width = 0.0;
|
||||
$split_style->border_top_left_radius = 0.0;
|
||||
$split_style->border_top_right_radius = 0.0;
|
||||
$split_style->page_break_before = "auto";
|
||||
}
|
||||
|
||||
$split_style->text_indent = 0.0;
|
||||
$split_style->counter_reset = "none";
|
||||
|
||||
$this->is_split = true;
|
||||
$split->is_split_off = true;
|
||||
$split->_already_pushed = true;
|
||||
|
||||
$this->get_parent()->insert_child_after($split, $this);
|
||||
|
||||
if ($this instanceof Block) {
|
||||
// Remove the frames that will be moved to the new split node from
|
||||
// the line boxes
|
||||
$this->remove_frames_from_line($child);
|
||||
|
||||
// recalculate the float offsets after paging
|
||||
foreach ($this->get_line_boxes() as $line_box) {
|
||||
$line_box->get_float_offsets();
|
||||
}
|
||||
}
|
||||
|
||||
if (!$forced) {
|
||||
// Reset top margin in case of an unforced page break
|
||||
// https://www.w3.org/TR/CSS21/page.html#allowed-page-breaks
|
||||
$child->get_style()->margin_top = 0.0;
|
||||
}
|
||||
|
||||
// Add $child 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, $page_break, $forced);
|
||||
|
||||
// Preserve the current counter values. This must be done after the
|
||||
// parent split, as counters get reset on frame reset
|
||||
$split->_counters = $this->_counters;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $counters
|
||||
*/
|
||||
public function reset_counters(array $counters): void
|
||||
{
|
||||
foreach ($counters as $id => $value) {
|
||||
$this->reset_counter($id, $value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $id
|
||||
* @param int $value
|
||||
*/
|
||||
public function reset_counter(string $id = self::DEFAULT_COUNTER, int $value = 0): void
|
||||
{
|
||||
$this->get_parent()->_counters[$id] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $counters
|
||||
*/
|
||||
public function decrement_counters(array $counters): void
|
||||
{
|
||||
foreach ($counters as $id => $increment) {
|
||||
$this->increment_counter($id, $increment * -1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $counters
|
||||
*/
|
||||
public function increment_counters(array $counters): void
|
||||
{
|
||||
foreach ($counters as $id => $increment) {
|
||||
$this->increment_counter($id, $increment);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $id
|
||||
* @param int $increment
|
||||
*/
|
||||
public function increment_counter(string $id = self::DEFAULT_COUNTER, int $increment = 1): void
|
||||
{
|
||||
$counter_frame = $this->lookup_counter_frame($id);
|
||||
|
||||
if ($counter_frame) {
|
||||
if (!isset($counter_frame->_counters[$id])) {
|
||||
$counter_frame->_counters[$id] = 0;
|
||||
}
|
||||
|
||||
$counter_frame->_counters[$id] += $increment;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $id
|
||||
* @return AbstractFrameDecorator|null
|
||||
*/
|
||||
function lookup_counter_frame($id = self::DEFAULT_COUNTER)
|
||||
{
|
||||
$f = $this->get_parent();
|
||||
|
||||
while ($f) {
|
||||
if (isset($f->_counters[$id])) {
|
||||
return $f;
|
||||
}
|
||||
$fp = $f->get_parent();
|
||||
|
||||
if (!$fp) {
|
||||
return $f;
|
||||
}
|
||||
|
||||
$f = $fp;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $id
|
||||
* @param string $type
|
||||
* @return bool|string
|
||||
*
|
||||
* TODO: What version is the best : this one or the one in ListBullet ?
|
||||
*/
|
||||
function counter_value(string $id = self::DEFAULT_COUNTER, string $type = "decimal")
|
||||
{
|
||||
$type = mb_strtolower($type);
|
||||
|
||||
if (!isset($this->_counters[$id])) {
|
||||
$this->_counters[$id] = 0;
|
||||
}
|
||||
|
||||
$value = $this->_counters[$id];
|
||||
|
||||
switch ($type) {
|
||||
default:
|
||||
case "decimal":
|
||||
return $value;
|
||||
|
||||
case "decimal-leading-zero":
|
||||
return str_pad($value, 2, "0", STR_PAD_LEFT);
|
||||
|
||||
case "lower-roman":
|
||||
return Helpers::dec2roman($value);
|
||||
|
||||
case "upper-roman":
|
||||
return mb_strtoupper(Helpers::dec2roman($value));
|
||||
|
||||
case "lower-latin":
|
||||
case "lower-alpha":
|
||||
return chr((($value - 1) % 26) + ord('a'));
|
||||
|
||||
case "upper-latin":
|
||||
case "upper-alpha":
|
||||
return chr((($value - 1) % 26) + ord('A'));
|
||||
|
||||
case "lower-greek":
|
||||
return Helpers::unichr($value + 944);
|
||||
|
||||
case "upper-greek":
|
||||
return Helpers::unichr($value + 912);
|
||||
}
|
||||
}
|
||||
|
||||
final function position()
|
||||
{
|
||||
$this->_positioner->position($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param float $offset_x
|
||||
* @param float $offset_y
|
||||
* @param bool $ignore_self
|
||||
*/
|
||||
final function move(float $offset_x, float $offset_y, bool $ignore_self = false): void
|
||||
{
|
||||
$this->_positioner->move($this, $offset_x, $offset_y, $ignore_self);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Block|null $block
|
||||
*/
|
||||
final function reflow(Block $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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
final public function get_min_max_width(): array
|
||||
{
|
||||
return $this->_reflower->get_min_max_width();
|
||||
}
|
||||
}
|
||||
256
system/vendor/pancakeapp/dompdf/src/FrameDecorator/Block.php
vendored
Executable file
256
system/vendor/pancakeapp/dompdf/src/FrameDecorator/Block.php
vendored
Executable file
@@ -0,0 +1,256 @@
|
||||
<?php
|
||||
/**
|
||||
* @package dompdf
|
||||
* @link https://github.com/dompdf/dompdf
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
*/
|
||||
namespace Dompdf\FrameDecorator;
|
||||
|
||||
use Dompdf\Dompdf;
|
||||
use Dompdf\Frame;
|
||||
use Dompdf\LineBox;
|
||||
|
||||
/**
|
||||
* Decorates frames for block layout
|
||||
*
|
||||
* @package dompdf
|
||||
*/
|
||||
class Block extends AbstractFrameDecorator
|
||||
{
|
||||
/**
|
||||
* Current line index
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $_cl;
|
||||
|
||||
/**
|
||||
* The block's line boxes
|
||||
*
|
||||
* @var LineBox[]
|
||||
*/
|
||||
protected $_line_boxes;
|
||||
|
||||
/**
|
||||
* List of markers that have not found their line box to vertically align
|
||||
* with yet. Markers are collected by nested block containers until an
|
||||
* inline line box is found at the start of the block.
|
||||
*
|
||||
* @var ListBullet[]
|
||||
*/
|
||||
protected $dangling_markers;
|
||||
|
||||
/**
|
||||
* Block constructor.
|
||||
* @param Frame $frame
|
||||
* @param Dompdf $dompdf
|
||||
*/
|
||||
function __construct(Frame $frame, Dompdf $dompdf)
|
||||
{
|
||||
parent::__construct($frame, $dompdf);
|
||||
|
||||
$this->_line_boxes = [new LineBox($this)];
|
||||
$this->_cl = 0;
|
||||
$this->dangling_markers = [];
|
||||
}
|
||||
|
||||
function reset()
|
||||
{
|
||||
parent::reset();
|
||||
|
||||
$this->_line_boxes = [new LineBox($this)];
|
||||
$this->_cl = 0;
|
||||
$this->dangling_markers = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return LineBox
|
||||
*/
|
||||
function get_current_line_box()
|
||||
{
|
||||
return $this->_line_boxes[$this->_cl];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
function get_current_line_number()
|
||||
{
|
||||
return $this->_cl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return LineBox[]
|
||||
*/
|
||||
function get_line_boxes()
|
||||
{
|
||||
return $this->_line_boxes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $line_number
|
||||
* @return int
|
||||
*/
|
||||
function set_current_line_number($line_number)
|
||||
{
|
||||
$line_boxes_count = count($this->_line_boxes);
|
||||
$cl = max(min($line_number, $line_boxes_count), 0);
|
||||
return ($this->_cl = $cl);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $i
|
||||
*/
|
||||
function clear_line($i)
|
||||
{
|
||||
if (isset($this->_line_boxes[$i])) {
|
||||
unset($this->_line_boxes[$i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Frame $frame
|
||||
* @return LineBox|null
|
||||
*/
|
||||
public function add_frame_to_line(Frame $frame): ?LineBox
|
||||
{
|
||||
$current_line = $this->_line_boxes[$this->_cl];
|
||||
$frame->set_containing_line($current_line);
|
||||
|
||||
// Inline frames are currently treated as wrappers, and are not actually
|
||||
// added to the line
|
||||
if ($frame instanceof Inline) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$current_line->add_frame($frame);
|
||||
|
||||
$this->increase_line_width($frame->get_margin_width());
|
||||
$this->maximize_line_height($frame->get_margin_height(), $frame);
|
||||
|
||||
// Add any dangling list markers to the first line box if it is inline
|
||||
if ($this->_cl === 0 && $current_line->inline
|
||||
&& $this->dangling_markers !== []
|
||||
) {
|
||||
foreach ($this->dangling_markers as $marker) {
|
||||
$current_line->add_list_marker($marker);
|
||||
$this->maximize_line_height($marker->get_margin_height(), $marker);
|
||||
}
|
||||
|
||||
$this->dangling_markers = [];
|
||||
}
|
||||
|
||||
return $current_line;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the given frame and all following frames and lines from the block.
|
||||
*
|
||||
* @param Frame $frame
|
||||
*/
|
||||
public function remove_frames_from_line(Frame $frame): void
|
||||
{
|
||||
// Inline frames are not added to line boxes themselves, only their
|
||||
// text frame children
|
||||
$actualFrame = $frame;
|
||||
while ($actualFrame !== null && $actualFrame instanceof Inline) {
|
||||
$actualFrame = $actualFrame->get_first_child();
|
||||
}
|
||||
|
||||
if ($actualFrame === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Search backwards through the lines for $frame
|
||||
$frame = $actualFrame;
|
||||
$i = $this->_cl;
|
||||
$j = null;
|
||||
|
||||
while ($i > 0) {
|
||||
$line = $this->_line_boxes[$i];
|
||||
foreach ($line->get_frames() as $index => $f) {
|
||||
if ($frame === $f) {
|
||||
$j = $index;
|
||||
break 2;
|
||||
}
|
||||
}
|
||||
$i--;
|
||||
}
|
||||
|
||||
if ($j === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove all lines that follow
|
||||
for ($k = $this->_cl; $k > $i; $k--) {
|
||||
unset($this->_line_boxes[$k]);
|
||||
}
|
||||
|
||||
// Remove the line, if it is empty
|
||||
if ($j > 0) {
|
||||
$line->remove_frames($j);
|
||||
} else {
|
||||
unset($this->_line_boxes[$i]);
|
||||
}
|
||||
|
||||
// Reset array indices
|
||||
$this->_line_boxes = array_values($this->_line_boxes);
|
||||
$this->_cl = count($this->_line_boxes) - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param float $w
|
||||
*/
|
||||
public function increase_line_width(float $w): void
|
||||
{
|
||||
$this->_line_boxes[$this->_cl]->w += $w;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param float $val
|
||||
* @param Frame $frame
|
||||
*/
|
||||
public function maximize_line_height(float $val, Frame $frame): void
|
||||
{
|
||||
if ($val > $this->_line_boxes[$this->_cl]->h) {
|
||||
$this->_line_boxes[$this->_cl]->tallest_frame = $frame;
|
||||
$this->_line_boxes[$this->_cl]->h = $val;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $br
|
||||
*/
|
||||
public function add_line(bool $br = false): void
|
||||
{
|
||||
$line = $this->_line_boxes[$this->_cl];
|
||||
|
||||
$line->br = $br;
|
||||
$y = $line->y + $line->h;
|
||||
|
||||
$new_line = new LineBox($this, $y);
|
||||
|
||||
$this->_line_boxes[++$this->_cl] = $new_line;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ListBullet $marker
|
||||
*/
|
||||
public function add_dangling_marker(ListBullet $marker): void
|
||||
{
|
||||
$this->dangling_markers[] = $marker;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inherit any dangling markers from the parent block.
|
||||
*
|
||||
* @param Block $block
|
||||
*/
|
||||
public function inherit_dangling_markers(self $block): void
|
||||
{
|
||||
if ($block->dangling_markers !== []) {
|
||||
$this->dangling_markers = $block->dangling_markers;
|
||||
$block->dangling_markers = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
120
system/vendor/pancakeapp/dompdf/src/FrameDecorator/Image.php
vendored
Executable file
120
system/vendor/pancakeapp/dompdf/src/FrameDecorator/Image.php
vendored
Executable file
@@ -0,0 +1,120 @@
|
||||
<?php
|
||||
/**
|
||||
* @package dompdf
|
||||
* @link https://github.com/dompdf/dompdf
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
*/
|
||||
namespace Dompdf\FrameDecorator;
|
||||
|
||||
use Dompdf\Dompdf;
|
||||
use Dompdf\Frame;
|
||||
use Dompdf\Helpers;
|
||||
use Dompdf\Image\Cache;
|
||||
|
||||
/**
|
||||
* Decorates frames for image layout and rendering
|
||||
*
|
||||
* @package dompdf
|
||||
*/
|
||||
class Image extends AbstractFrameDecorator
|
||||
{
|
||||
|
||||
/**
|
||||
* The path to the image file (note that remote images are
|
||||
* downloaded locally to Options:tempDir).
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $_image_url;
|
||||
|
||||
/**
|
||||
* The image's file error message
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $_image_msg;
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
parent::__construct($frame, $dompdf);
|
||||
$url = $frame->get_node()->getAttribute("src");
|
||||
|
||||
$debug_png = $dompdf->getOptions()->getDebugPng();
|
||||
if ($debug_png) {
|
||||
print '[__construct ' . $url . ']';
|
||||
}
|
||||
|
||||
list($this->_image_url, /*$type*/, $this->_image_msg) = Cache::resolve_url(
|
||||
$url,
|
||||
$dompdf->getProtocol(),
|
||||
$dompdf->getBaseHost(),
|
||||
$dompdf->getBasePath(),
|
||||
$dompdf->getOptions()
|
||||
);
|
||||
|
||||
if (Cache::is_broken($this->_image_url) &&
|
||||
$alt = $frame->get_node()->getAttribute("alt")
|
||||
) {
|
||||
$fontMetrics = $dompdf->getFontMetrics();
|
||||
$style = $frame->get_style();
|
||||
$font = $style->font_family;
|
||||
$size = $style->font_size;
|
||||
$word_spacing = $style->word_spacing;
|
||||
$letter_spacing = $style->letter_spacing;
|
||||
|
||||
$style->width = (4 / 3) * $fontMetrics->getTextWidth($alt, $font, $size, $word_spacing, $letter_spacing);
|
||||
$style->height = $fontMetrics->getFontHeight($font, $size);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the intrinsic pixel dimensions of the image.
|
||||
*
|
||||
* @return array Width and height as `float|int`.
|
||||
*/
|
||||
public function get_intrinsic_dimensions(): array
|
||||
{
|
||||
[$width, $height] = Helpers::dompdf_getimagesize($this->_image_url, $this->_dompdf->getHttpContext());
|
||||
|
||||
return [$width, $height];
|
||||
}
|
||||
|
||||
/**
|
||||
* Resample the given pixel length according to dpi.
|
||||
*
|
||||
* @param float|int $length
|
||||
* @return float
|
||||
*/
|
||||
public function resample($length): float
|
||||
{
|
||||
$dpi = $this->_dompdf->getOptions()->getDpi();
|
||||
return ($length * 72) / $dpi;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the image's url
|
||||
*
|
||||
* @return string The url of this image
|
||||
*/
|
||||
function get_image_url()
|
||||
{
|
||||
return $this->_image_url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the image's error message
|
||||
*
|
||||
* @return string The image's error message
|
||||
*/
|
||||
function get_image_msg()
|
||||
{
|
||||
return $this->_image_msg;
|
||||
}
|
||||
|
||||
}
|
||||
121
system/vendor/pancakeapp/dompdf/src/FrameDecorator/Inline.php
vendored
Executable file
121
system/vendor/pancakeapp/dompdf/src/FrameDecorator/Inline.php
vendored
Executable file
@@ -0,0 +1,121 @@
|
||||
<?php
|
||||
/**
|
||||
* @package dompdf
|
||||
* @link https://github.com/dompdf/dompdf
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
*/
|
||||
namespace Dompdf\FrameDecorator;
|
||||
|
||||
use Dompdf\Dompdf;
|
||||
use Dompdf\Frame;
|
||||
use Dompdf\Exception;
|
||||
|
||||
/**
|
||||
* Decorates frames for inline layout
|
||||
*
|
||||
* @package dompdf
|
||||
*/
|
||||
class Inline extends AbstractFrameDecorator
|
||||
{
|
||||
|
||||
/**
|
||||
* Inline constructor.
|
||||
* @param Frame $frame
|
||||
* @param Dompdf $dompdf
|
||||
*/
|
||||
function __construct(Frame $frame, Dompdf $dompdf)
|
||||
{
|
||||
parent::__construct($frame, $dompdf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Vertical padding, border, and margin do not apply when determining the
|
||||
* height for inline 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.
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function get_margin_height(): float
|
||||
{
|
||||
$style = $this->get_style();
|
||||
$font = $style->font_family;
|
||||
$size = $style->font_size;
|
||||
$fontHeight = $this->_dompdf->getFontMetrics()->getFontHeight($font, $size);
|
||||
|
||||
return ($style->line_height / ($size > 0 ? $size : 1)) * $fontHeight;
|
||||
}
|
||||
|
||||
public function split(?Frame $child = null, bool $page_break = false, bool $forced = false): void
|
||||
{
|
||||
if (is_null($child)) {
|
||||
$this->get_parent()->split($this, $page_break, $forced);
|
||||
return;
|
||||
}
|
||||
|
||||
if ($child->get_parent() !== $this) {
|
||||
throw new Exception("Unable to split: frame is not a child of this one.");
|
||||
}
|
||||
|
||||
$this->revert_counter_increment();
|
||||
$node = $this->_frame->get_node();
|
||||
$split = $this->copy($node->cloneNode());
|
||||
|
||||
$style = $this->_frame->get_style();
|
||||
$split_style = $split->get_style();
|
||||
|
||||
// Unset the current node's right style properties
|
||||
$style->margin_right = 0.0;
|
||||
$style->padding_right = 0.0;
|
||||
$style->border_right_width = 0.0;
|
||||
$style->border_top_right_radius = 0.0;
|
||||
$style->border_bottom_right_radius = 0.0;
|
||||
|
||||
// Unset the split node's left style properties since we don't want them
|
||||
// to propagate
|
||||
$split_style->margin_left = 0.0;
|
||||
$split_style->padding_left = 0.0;
|
||||
$split_style->border_left_width = 0.0;
|
||||
$split_style->border_top_left_radius = 0.0;
|
||||
$split_style->border_bottom_left_radius = 0.0;
|
||||
|
||||
// If this is a generated node don't propagate the content style
|
||||
if ($split->get_node()->nodeName == "dompdf_generated") {
|
||||
$split_style->content = "normal";
|
||||
}
|
||||
|
||||
//On continuation of inline element on next line,
|
||||
//don't repeat non-horizontally repeatable background images
|
||||
//See e.g. in testcase image_variants, long descriptions
|
||||
if (($url = $style->background_image) && $url !== "none"
|
||||
&& ($repeat = $style->background_repeat) && $repeat !== "repeat" && $repeat !== "repeat-x"
|
||||
) {
|
||||
$split_style->background_image = "none";
|
||||
}
|
||||
|
||||
$this->get_parent()->insert_child_after($split, $this);
|
||||
|
||||
// Add $child 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);
|
||||
}
|
||||
|
||||
$parent = $this->get_parent();
|
||||
|
||||
if ($page_break) {
|
||||
$parent->split($split, $page_break, $forced);
|
||||
} elseif ($parent instanceof Inline) {
|
||||
$parent->split($split);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
117
system/vendor/pancakeapp/dompdf/src/FrameDecorator/ListBullet.php
vendored
Executable file
117
system/vendor/pancakeapp/dompdf/src/FrameDecorator/ListBullet.php
vendored
Executable file
@@ -0,0 +1,117 @@
|
||||
<?php
|
||||
/**
|
||||
* @package dompdf
|
||||
* @link https://github.com/dompdf/dompdf
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
*/
|
||||
namespace Dompdf\FrameDecorator;
|
||||
|
||||
use Dompdf\Dompdf;
|
||||
use Dompdf\Frame;
|
||||
|
||||
/**
|
||||
* Decorates frames for list bullet rendering
|
||||
*
|
||||
* @package dompdf
|
||||
*/
|
||||
class ListBullet extends AbstractFrameDecorator
|
||||
{
|
||||
/**
|
||||
* Bullet diameter as fraction of font size.
|
||||
*/
|
||||
public const BULLET_SIZE = 0.35;
|
||||
|
||||
/**
|
||||
* Bullet offset from font baseline as fraction of font size.
|
||||
*/
|
||||
public const BULLET_OFFSET = 0.1;
|
||||
|
||||
/**
|
||||
* Thickness of bullet outline as fraction of font size.
|
||||
* See also `DECO_THICKNESS`. Screen: 0.08, print: better less, e.g. 0.04.
|
||||
*/
|
||||
public const BULLET_THICKNESS = 0.04;
|
||||
|
||||
/**
|
||||
* Indentation from the start of the line as fraction of font size.
|
||||
*/
|
||||
public const MARKER_INDENT = 0.52;
|
||||
|
||||
/**
|
||||
* ListBullet constructor.
|
||||
* @param Frame $frame
|
||||
* @param Dompdf $dompdf
|
||||
*/
|
||||
function __construct(Frame $frame, Dompdf $dompdf)
|
||||
{
|
||||
parent::__construct($frame, $dompdf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the width of the bullet symbol.
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function get_width(): float
|
||||
{
|
||||
$style = $this->_frame->get_style();
|
||||
|
||||
if ($style->list_style_type === "none") {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
return $style->font_size * self::BULLET_SIZE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the height of the bullet symbol.
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function get_height(): float
|
||||
{
|
||||
$style = $this->_frame->get_style();
|
||||
|
||||
if ($style->list_style_type === "none") {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
return $style->font_size * self::BULLET_SIZE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the width of the bullet, including indentation.
|
||||
*/
|
||||
public function get_margin_width(): float
|
||||
{
|
||||
$style = $this->get_style();
|
||||
|
||||
if ($style->list_style_type === "none") {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
return $style->font_size * (self::BULLET_SIZE + self::MARKER_INDENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the line height for the bullet.
|
||||
*
|
||||
* This increases the height of the corresponding line box when necessary.
|
||||
*/
|
||||
public function get_margin_height(): float
|
||||
{
|
||||
$style = $this->get_style();
|
||||
|
||||
if ($style->list_style_type === "none") {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
// TODO: This is a copy of `FrameDecorator\Text::get_margin_height()`
|
||||
// Would be nice to properly refactor that at some point
|
||||
$font = $style->font_family;
|
||||
$size = $style->font_size;
|
||||
$fontHeight = $this->_dompdf->getFontMetrics()->getFontHeight($font, $size);
|
||||
|
||||
return ($style->line_height / ($size > 0 ? $size : 1)) * $fontHeight;
|
||||
}
|
||||
}
|
||||
112
system/vendor/pancakeapp/dompdf/src/FrameDecorator/ListBulletImage.php
vendored
Executable file
112
system/vendor/pancakeapp/dompdf/src/FrameDecorator/ListBulletImage.php
vendored
Executable file
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
/**
|
||||
* @package dompdf
|
||||
* @link https://github.com/dompdf/dompdf
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
*/
|
||||
namespace Dompdf\FrameDecorator;
|
||||
|
||||
use Dompdf\Dompdf;
|
||||
use Dompdf\Frame;
|
||||
use Dompdf\Helpers;
|
||||
use Dompdf\Image\Cache;
|
||||
|
||||
/**
|
||||
* Decorates frames for list bullets with custom images
|
||||
*
|
||||
* @package dompdf
|
||||
*/
|
||||
class ListBulletImage extends ListBullet
|
||||
{
|
||||
|
||||
/**
|
||||
* The underlying image frame
|
||||
*
|
||||
* @var Image
|
||||
*/
|
||||
protected $_img;
|
||||
|
||||
/**
|
||||
* The image's width in pixels
|
||||
*
|
||||
* @var float
|
||||
*/
|
||||
protected $_width;
|
||||
|
||||
/**
|
||||
* The image's height in pixels
|
||||
*
|
||||
* @var float
|
||||
*/
|
||||
protected $_height;
|
||||
|
||||
/**
|
||||
* ListBulletImage constructor.
|
||||
* @param Frame $frame
|
||||
* @param Dompdf $dompdf
|
||||
*/
|
||||
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, $dompdf);
|
||||
parent::__construct($this->_img, $dompdf);
|
||||
|
||||
$url = $this->_img->get_image_url();
|
||||
|
||||
if (Cache::is_broken($url)) {
|
||||
$this->_width = parent::get_width();
|
||||
$this->_height = parent::get_height();
|
||||
} else {
|
||||
// Resample the bullet image to be consistent with 'auto' sized images
|
||||
[$width, $height] = $this->_img->get_intrinsic_dimensions();
|
||||
$this->_width = $this->_img->resample($width);
|
||||
$this->_height = $this->_img->resample($height);
|
||||
}
|
||||
}
|
||||
|
||||
public function get_width(): float
|
||||
{
|
||||
return $this->_width;
|
||||
}
|
||||
|
||||
public function get_height(): float
|
||||
{
|
||||
return $this->_height;
|
||||
}
|
||||
|
||||
public function get_margin_width(): float
|
||||
{
|
||||
$style = $this->get_style();
|
||||
return $this->_width + $style->font_size * self::MARKER_INDENT;
|
||||
}
|
||||
|
||||
public function get_margin_height(): float
|
||||
{
|
||||
$fontMetrics = $this->_dompdf->getFontMetrics();
|
||||
$style = $this->get_style();
|
||||
$font = $style->font_family;
|
||||
$size = $style->font_size;
|
||||
$fontHeight = $fontMetrics->getFontHeight($font, $size);
|
||||
$baseline = $fontMetrics->getFontBaseline($font, $size);
|
||||
|
||||
// This is the same factor as used in
|
||||
// `FrameDecorator\Text::get_margin_height()`
|
||||
$f = $style->line_height / ($size > 0 ? $size : 1);
|
||||
|
||||
// FIXME: Tries to approximate replacing the space above the font
|
||||
// baseline with the image
|
||||
return $f * ($fontHeight - $baseline) + $this->_height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return image url
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function get_image_url()
|
||||
{
|
||||
return $this->_img->get_image_url();
|
||||
}
|
||||
}
|
||||
33
system/vendor/pancakeapp/dompdf/src/FrameDecorator/NullFrameDecorator.php
vendored
Executable file
33
system/vendor/pancakeapp/dompdf/src/FrameDecorator/NullFrameDecorator.php
vendored
Executable file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
/**
|
||||
* @package dompdf
|
||||
* @link https://github.com/dompdf/dompdf
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
*/
|
||||
namespace Dompdf\FrameDecorator;
|
||||
|
||||
use Dompdf\Dompdf;
|
||||
use Dompdf\Frame;
|
||||
|
||||
/**
|
||||
* Dummy decorator
|
||||
*
|
||||
* @package dompdf
|
||||
*/
|
||||
class NullFrameDecorator extends AbstractFrameDecorator
|
||||
{
|
||||
/**
|
||||
* NullFrameDecorator constructor.
|
||||
* @param Frame $frame
|
||||
* @param Dompdf $dompdf
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
753
system/vendor/pancakeapp/dompdf/src/FrameDecorator/Page.php
vendored
Executable file
753
system/vendor/pancakeapp/dompdf/src/FrameDecorator/Page.php
vendored
Executable file
@@ -0,0 +1,753 @@
|
||||
<?php
|
||||
/**
|
||||
* @package dompdf
|
||||
* @link https://github.com/dompdf/dompdf
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
*/
|
||||
namespace Dompdf\FrameDecorator;
|
||||
|
||||
use Dompdf\Dompdf;
|
||||
use Dompdf\Helpers;
|
||||
use Dompdf\Frame;
|
||||
use Dompdf\Renderer;
|
||||
|
||||
/**
|
||||
* Decorates frames for page layout
|
||||
*
|
||||
* @package dompdf
|
||||
*/
|
||||
class Page extends AbstractFrameDecorator
|
||||
{
|
||||
/**
|
||||
* The y value of the bottom edge of the page area.
|
||||
*
|
||||
* https://www.w3.org/TR/CSS21/page.html#page-margins
|
||||
*
|
||||
* @var float
|
||||
*/
|
||||
protected $bottom_page_edge;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* This page's floating frames
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $_floating_frames = [];
|
||||
|
||||
//........................................................................
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param Frame $frame the frame to decorate
|
||||
* @param Dompdf $dompdf
|
||||
*/
|
||||
function __construct(Frame $frame, Dompdf $dompdf)
|
||||
{
|
||||
parent::__construct($frame, $dompdf);
|
||||
$this->_page_full = false;
|
||||
$this->_in_table = 0;
|
||||
$this->bottom_page_edge = 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the bottom edge of the page area after margins have been
|
||||
* applied for the current page.
|
||||
*/
|
||||
public function calculate_bottom_page_edge(): void
|
||||
{
|
||||
[, , , $cbh] = $this->get_containing_block();
|
||||
$style = $this->get_style();
|
||||
$margin_bottom = (float) $style->length_in_pt($style->margin_bottom, $cbh);
|
||||
|
||||
$this->bottom_page_edge = $cbh - $margin_bottom;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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->_floating_frames = [];
|
||||
$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 preceding frame's
|
||||
* page_break_after property.
|
||||
*
|
||||
* @link http://www.w3.org/TR/CSS21/page.html#forced
|
||||
*
|
||||
* @param AbstractFrameDecorator $frame the frame to check
|
||||
*
|
||||
* @return bool true if a page break occurred
|
||||
*/
|
||||
function check_forced_page_break(Frame $frame)
|
||||
{
|
||||
// Skip check if page is already split and for the body
|
||||
if ($this->_page_full || $frame->get_node()->nodeName === "body") {
|
||||
return false;
|
||||
}
|
||||
|
||||
$page_breaks = ["always", "left", "right"];
|
||||
$style = $frame->get_style();
|
||||
|
||||
if (($frame->is_block_level() || $style->display === "table-row")
|
||||
&& in_array($style->page_break_before, $page_breaks, true)
|
||||
) {
|
||||
// Prevent cascading splits
|
||||
$frame->split(null, true, true);
|
||||
$style->page_break_before = "auto";
|
||||
$this->_page_full = true;
|
||||
$frame->_already_pushed = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Find the preceding block-level sibling (or table row). Inline
|
||||
// elements are treated as if wrapped in an anonymous block container
|
||||
// here. See https://www.w3.org/TR/CSS21/visuren.html#anonymous-block-level
|
||||
$prev = $frame->get_prev_sibling();
|
||||
while ($prev && (($prev->is_text_node() && $prev->get_node()->nodeValue === "")
|
||||
|| $prev->get_node()->nodeName === "bullet")
|
||||
) {
|
||||
$prev = $prev->get_prev_sibling();
|
||||
}
|
||||
|
||||
if ($prev && ($prev->is_block_level() || $prev->get_style()->display === "table-row")) {
|
||||
if (in_array($prev->get_style()->page_break_after, $page_breaks, true)) {
|
||||
// Prevent cascading splits
|
||||
$frame->split(null, true, true);
|
||||
$prev->get_style()->page_break_after = "auto";
|
||||
$this->_page_full = true;
|
||||
$frame->_already_pushed = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
$prev_last_child = $prev->get_last_child();
|
||||
while ($prev_last_child && (($prev_last_child->is_text_node() && $prev_last_child->get_node()->nodeValue === "")
|
||||
|| $prev_last_child->get_node()->nodeName === "bullet")
|
||||
) {
|
||||
$prev_last_child = $prev_last_child->get_prev_sibling();
|
||||
}
|
||||
|
||||
if ($prev_last_child
|
||||
&& $prev_last_child->is_block_level()
|
||||
&& in_array($prev_last_child->get_style()->page_break_after, $page_breaks, true)
|
||||
) {
|
||||
$frame->split(null, true, true);
|
||||
$prev_last_child->get_style()->page_break_after = "auto";
|
||||
$this->_page_full = true;
|
||||
$frame->_already_pushed = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for a gap between the top content edge of a frame and its child
|
||||
* content.
|
||||
*
|
||||
* Additionally, the top margin, border, and padding of the frame must fit
|
||||
* on the current page.
|
||||
*
|
||||
* @param float $childPos The top margin or line-box edge of the child content.
|
||||
* @param Frame $frame The parent frame to check.
|
||||
* @return bool
|
||||
*/
|
||||
protected function hasGap(float $childPos, Frame $frame): bool
|
||||
{
|
||||
$style = $frame->get_style();
|
||||
$cbw = $frame->get_containing_block("w");
|
||||
$contentEdge = $frame->get_position("y") + (float) $style->length_in_pt([
|
||||
$style->margin_top,
|
||||
$style->border_top_width,
|
||||
$style->padding_top
|
||||
], $cbw);
|
||||
|
||||
return Helpers::lengthGreater($childPos, $contentEdge)
|
||||
&& Helpers::lengthLessOrEqual($contentEdge, $this->bottom_page_edge);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 an
|
||||
* unforced page break occurs here, the used values of the
|
||||
* relevant 'margin-top' and 'margin-bottom' properties are set
|
||||
* to '0'. When a forced page break occurs here, the used value
|
||||
* of the relevant 'margin-bottom' property is set to '0'; the
|
||||
* relevant 'margin-top' used value may either be set to '0' or
|
||||
* retained.
|
||||
* 2. Between line boxes inside a block container 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:
|
||||
*
|
||||
* * 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 a 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) or (3) is allowed only
|
||||
* if the 'page-break-inside' property of the element and all
|
||||
* its ancestors is 'auto'.
|
||||
*
|
||||
* If the above does not provide enough break points to keep content
|
||||
* from overflowing the page boxes, then rules A, B and D are
|
||||
* dropped in order to find additional breakpoints.
|
||||
*
|
||||
* If that still does not lead to sufficient break points, rule C is
|
||||
* dropped as well, to find still more break points.
|
||||
*
|
||||
* We also allow breaks between table rows.
|
||||
*
|
||||
* @param AbstractFrameDecorator $frame the frame to check
|
||||
*
|
||||
* @return bool true if a break is allowed, false otherwise
|
||||
*/
|
||||
protected function _page_break_allowed(Frame $frame)
|
||||
{
|
||||
Helpers::dompdf_debug("page-break", "_page_break_allowed(" . $frame->get_node()->nodeName . ")");
|
||||
$display = $frame->get_style()->display;
|
||||
|
||||
// Block Frames (1):
|
||||
if ($frame->is_block_level() || $display === "-dompdf-image") {
|
||||
|
||||
// Avoid breaks within table-cells
|
||||
if ($this->_in_table > ($display === "table" ? 1 : 0)) {
|
||||
Helpers::dompdf_debug("page-break", "In table: " . $this->_in_table);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Rule A
|
||||
if ($frame->get_style()->page_break_before === "avoid") {
|
||||
Helpers::dompdf_debug("page-break", "before: avoid");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find the preceding block-level sibling. Inline elements are
|
||||
// treated as if wrapped in an anonymous block container here. See
|
||||
// https://www.w3.org/TR/CSS21/visuren.html#anonymous-block-level
|
||||
$prev = $frame->get_prev_sibling();
|
||||
while ($prev && (($prev->is_text_node() && $prev->get_node()->nodeValue === "")
|
||||
|| $prev->get_node()->nodeName === "bullet")
|
||||
) {
|
||||
$prev = $prev->get_prev_sibling();
|
||||
}
|
||||
|
||||
// Does the previous element allow a page break after?
|
||||
if ($prev && ($prev->is_block_level() || $prev->get_style()->display === "-dompdf-image")
|
||||
&& $prev->get_style()->page_break_after === "avoid"
|
||||
) {
|
||||
Helpers::dompdf_debug("page-break", "after: avoid");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Rules B & D
|
||||
$parent = $frame->get_parent();
|
||||
$p = $parent;
|
||||
while ($p) {
|
||||
if ($p->get_style()->page_break_inside === "avoid") {
|
||||
Helpers::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 is
|
||||
// on the page before splitting.
|
||||
if ($parent->get_node()->nodeName === "body" && !$prev) {
|
||||
// We are the body's first child
|
||||
Helpers::dompdf_debug("page-break", "Body's first child.");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for a possible type (3) break
|
||||
if (!$prev && $parent && !$this->hasGap($frame->get_position("y"), $parent)) {
|
||||
Helpers::dompdf_debug("page-break", "First block-level frame, no gap");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Helpers::dompdf_debug("page-break", "block: break allowed");
|
||||
|
||||
return true;
|
||||
|
||||
} // Inline frames (2):
|
||||
else {
|
||||
if ($frame->is_inline_level()) {
|
||||
|
||||
// Avoid breaks within table-cells
|
||||
if ($this->_in_table) {
|
||||
Helpers::dompdf_debug("page-break", "In table: " . $this->_in_table);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Rule C
|
||||
$block_parent = $frame->find_block_parent();
|
||||
$parent_style = $block_parent->get_style();
|
||||
$line = $block_parent->get_current_line_box();
|
||||
$line_count = count($block_parent->get_line_boxes());
|
||||
$line_number = $frame->get_containing_line() && empty($line->get_frames())
|
||||
? $line_count - 1
|
||||
: $line_count;
|
||||
|
||||
// The line number of the frame can be less than the current
|
||||
// number of line boxes, in case we are backtracking. As long as
|
||||
// we are not checking for widows yet, just checking against the
|
||||
// number of line boxes is sufficient in most cases, though.
|
||||
if ($line_number <= $parent_style->orphans) {
|
||||
Helpers::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") {
|
||||
Helpers::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->is_text_node() && 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
|
||||
Helpers::dompdf_debug("page-break", "Body's first child.");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Helpers::dompdf_debug("page-break", "inline: break allowed");
|
||||
|
||||
return true;
|
||||
|
||||
// Table-rows
|
||||
} else {
|
||||
if ($display === "table-row") {
|
||||
|
||||
// If this is a nested table, prevent the page from breaking
|
||||
if ($this->_in_table > 1) {
|
||||
Helpers::dompdf_debug("page-break", "table: nested table");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Rule A (table row)
|
||||
if ($frame->get_style()->page_break_before === "avoid") {
|
||||
Helpers::dompdf_debug("page-break", "before: avoid");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find the preceding row
|
||||
$prev = $frame->get_prev_sibling();
|
||||
|
||||
if (!$prev) {
|
||||
$prev_group = $frame->get_parent()->get_prev_sibling();
|
||||
|
||||
if ($prev_group
|
||||
&& in_array($prev_group->get_style()->display, Table::ROW_GROUPS, true)
|
||||
) {
|
||||
$prev = $prev_group->get_last_child();
|
||||
}
|
||||
}
|
||||
|
||||
// Check if a page break is allowed after the preceding row
|
||||
if ($prev && $prev->get_style()->page_break_after === "avoid") {
|
||||
Helpers::dompdf_debug("page-break", "after: avoid");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Avoid breaking before the first row of a table
|
||||
if (!$prev) {
|
||||
Helpers::dompdf_debug("page-break", "table: first-row");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Rule B (table row)
|
||||
// Check if the page_break_inside property is not 'avoid'
|
||||
// for the parent table or any of its ancestors
|
||||
$table = Table::find_parent_table($frame);
|
||||
|
||||
$p = $table;
|
||||
while ($p) {
|
||||
if ($p->get_style()->page_break_inside === "avoid") {
|
||||
Helpers::dompdf_debug("page-break", "parent->inside: avoid");
|
||||
|
||||
return false;
|
||||
}
|
||||
$p = $p->find_block_parent();
|
||||
}
|
||||
|
||||
Helpers::dompdf_debug("page-break", "table-row: break allowed");
|
||||
|
||||
return true;
|
||||
} else {
|
||||
if (in_array($display, Table::ROW_GROUPS, true)) {
|
||||
|
||||
// Disallow breaks at row-groups: only split at row boundaries
|
||||
return false;
|
||||
|
||||
} else {
|
||||
Helpers::dompdf_debug("page-break", "? " . $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 AbstractFrameDecorator $frame the frame to check
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
function check_page_break(Frame $frame)
|
||||
{
|
||||
if ($this->_page_full || $frame->_already_pushed
|
||||
// Never check for breaks on empty text nodes
|
||||
|| ($frame->is_text_node() && $frame->get_node()->nodeValue === "")
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$p = $frame;
|
||||
do {
|
||||
$display = $p->get_style()->display;
|
||||
if ($display == "table-row") {
|
||||
if ($p->_already_pushed) { return false; }
|
||||
}
|
||||
} while ($p = $p->get_parent());
|
||||
|
||||
// If the frame is absolute or fixed it shouldn't break
|
||||
$p = $frame;
|
||||
do {
|
||||
if ($p->is_absolute()) {
|
||||
return false;
|
||||
}
|
||||
} while ($p = $p->get_parent());
|
||||
|
||||
$margin_height = $frame->get_margin_height();
|
||||
|
||||
// Determine the frame's maximum y value
|
||||
$max_y = (float)$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 && $p !== $this) {
|
||||
$cbw = $p->get_containing_block("w");
|
||||
$max_y += (float) $p->get_style()->computed_bottom_spacing($cbw);
|
||||
$p = $p->get_parent();
|
||||
}
|
||||
|
||||
// Check if $frame flows off the page
|
||||
if (Helpers::lengthLessOrEqual($max_y, $this->bottom_page_edge)) {
|
||||
// no: do nothing
|
||||
return false;
|
||||
}
|
||||
|
||||
Helpers::dompdf_debug("page-break", "check_page_break");
|
||||
Helpers::dompdf_debug("page-break", "in_table: " . $this->_in_table);
|
||||
|
||||
// yes: determine page break location
|
||||
$iter = $frame;
|
||||
$flg = false;
|
||||
$pushed_flg = false;
|
||||
|
||||
$in_table = $this->_in_table;
|
||||
|
||||
Helpers::dompdf_debug("page-break", "Starting search");
|
||||
while ($iter) {
|
||||
// echo "\nbacktrack: " .$iter->get_node()->nodeName ." ".spl_object_hash($iter->get_node()). "";
|
||||
if ($iter === $this) {
|
||||
Helpers::dompdf_debug("page-break", "reached root.");
|
||||
// We've reached the root in our search. Just split at $frame.
|
||||
break;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (!$flg && $next = $iter->get_last_child()) {
|
||||
Helpers::dompdf_debug("page-break", "following last child.");
|
||||
|
||||
if ($next->is_table()) {
|
||||
$this->_in_table++;
|
||||
}
|
||||
|
||||
$iter = $next;
|
||||
$pushed_flg = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($pushed_flg) {
|
||||
// The frame was already pushed, avoid breaking on a previous page
|
||||
break;
|
||||
}
|
||||
|
||||
$next = $iter->get_prev_sibling();
|
||||
// Skip empty text nodes
|
||||
while ($next && $next->is_text_node() && $next->get_node()->nodeValue === "") {
|
||||
$next = $next->get_prev_sibling();
|
||||
}
|
||||
|
||||
if ($next) {
|
||||
Helpers::dompdf_debug("page-break", "following prev sibling.");
|
||||
|
||||
if ($next->is_table() && !$iter->is_table()) {
|
||||
$this->_in_table++;
|
||||
} elseif (!$next->is_table() && $iter->is_table()) {
|
||||
$this->_in_table--;
|
||||
}
|
||||
|
||||
$iter = $next;
|
||||
$flg = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($next = $iter->get_parent()) {
|
||||
Helpers::dompdf_debug("page-break", "following parent.");
|
||||
|
||||
if ($iter->is_table()) {
|
||||
$this->_in_table--;
|
||||
}
|
||||
|
||||
$iter = $next;
|
||||
$flg = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
$this->_in_table = $in_table;
|
||||
|
||||
// No valid page break found. Just break at $frame.
|
||||
Helpers::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) {
|
||||
$iter = $frame;
|
||||
while ($iter && $iter->get_style()->display !== "table-row" && $iter->get_style()->display !== 'table-row-group' && $iter->_already_pushed === false) {
|
||||
$iter = $iter->get_parent();
|
||||
}
|
||||
|
||||
if ($iter) {
|
||||
$iter->split(null, true);
|
||||
$iter->_already_pushed = true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
$frame->split(null, true);
|
||||
}
|
||||
|
||||
$this->_page_full = true;
|
||||
$frame->_already_pushed = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//........................................................................
|
||||
|
||||
public function split(?Frame $child = null, bool $page_break = false, bool $forced = false): void
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a floating frame
|
||||
*
|
||||
* @param Frame $frame
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function add_floating_frame(Frame $frame)
|
||||
{
|
||||
array_unshift($this->_floating_frames, $frame);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Frame[]
|
||||
*/
|
||||
function get_floating_frames()
|
||||
{
|
||||
return $this->_floating_frames;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $key
|
||||
*/
|
||||
public function remove_floating_frame($key)
|
||||
{
|
||||
unset($this->_floating_frames[$key]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Frame $child
|
||||
* @return int|mixed
|
||||
*/
|
||||
public function get_lowest_float_offset(Frame $child)
|
||||
{
|
||||
$style = $child->get_style();
|
||||
$side = $style->clear;
|
||||
$float = $style->float;
|
||||
|
||||
$y = 0;
|
||||
|
||||
if ($float === "none") {
|
||||
foreach ($this->_floating_frames as $key => $frame) {
|
||||
if ($side === "both" || $frame->get_style()->float === $side) {
|
||||
$y = max($y, $frame->get_position("y") + $frame->get_margin_height());
|
||||
}
|
||||
$this->remove_floating_frame($key);
|
||||
}
|
||||
}
|
||||
|
||||
if ($y > 0) {
|
||||
$y++; // add 1px buffer from float
|
||||
}
|
||||
|
||||
return $y;
|
||||
}
|
||||
}
|
||||
343
system/vendor/pancakeapp/dompdf/src/FrameDecorator/Table.php
vendored
Executable file
343
system/vendor/pancakeapp/dompdf/src/FrameDecorator/Table.php
vendored
Executable file
@@ -0,0 +1,343 @@
|
||||
<?php
|
||||
/**
|
||||
* @package dompdf
|
||||
* @link https://github.com/dompdf/dompdf
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
*/
|
||||
namespace Dompdf\FrameDecorator;
|
||||
|
||||
use Dompdf\Cellmap;
|
||||
use DOMNode;
|
||||
use Dompdf\Css\Style;
|
||||
use Dompdf\Dompdf;
|
||||
use Dompdf\Frame;
|
||||
|
||||
/**
|
||||
* Decorates Frames for table layout
|
||||
*
|
||||
* @package dompdf
|
||||
*/
|
||||
class Table extends AbstractFrameDecorator
|
||||
{
|
||||
public const VALID_CHILDREN = Style::TABLE_INTERNAL_TYPES;
|
||||
|
||||
/**
|
||||
* List of all row-group display types.
|
||||
*/
|
||||
public const ROW_GROUPS = [
|
||||
"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;
|
||||
|
||||
/**
|
||||
* Table header rows. Each table header is duplicated when a table
|
||||
* spans pages.
|
||||
*
|
||||
* @var TableRowGroup[]
|
||||
*/
|
||||
protected $_headers;
|
||||
|
||||
/**
|
||||
* Table footer rows. Each table footer is duplicated when a table
|
||||
* spans pages.
|
||||
*
|
||||
* @var TableRowGroup[]
|
||||
*/
|
||||
protected $_footers;
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param Frame $frame the frame to decorate
|
||||
* @param Dompdf $dompdf
|
||||
*/
|
||||
public function __construct(Frame $frame, Dompdf $dompdf)
|
||||
{
|
||||
parent::__construct($frame, $dompdf);
|
||||
$this->_cellmap = new Cellmap($this);
|
||||
|
||||
if ($frame->get_style()->table_layout === "fixed") {
|
||||
$this->_cellmap->set_layout_fixed(true);
|
||||
}
|
||||
|
||||
$this->_headers = [];
|
||||
$this->_footers = [];
|
||||
}
|
||||
|
||||
public function reset()
|
||||
{
|
||||
parent::reset();
|
||||
$this->_cellmap->reset();
|
||||
$this->_headers = [];
|
||||
$this->_footers = [];
|
||||
$this->_reflower->reset();
|
||||
}
|
||||
|
||||
//........................................................................
|
||||
|
||||
/**
|
||||
* Split the table at $row. $row and all subsequent rows will be
|
||||
* added to the clone. This method is overridden in order to remove
|
||||
* frames from the cellmap properly.
|
||||
*/
|
||||
public function split(?Frame $child = null, bool $page_break = false, bool $forced = false): void
|
||||
{
|
||||
if (is_null($child)) {
|
||||
parent::split($child, $page_break, $forced);
|
||||
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, $page_break, $forced);
|
||||
|
||||
} elseif (in_array($child->get_style()->display, self::ROW_GROUPS, true)) {
|
||||
|
||||
// Individual rows should have already been handled
|
||||
parent::split($child, $page_break, $forced);
|
||||
|
||||
} else {
|
||||
|
||||
$iter = $child;
|
||||
|
||||
while ($iter) {
|
||||
$this->_cellmap->remove_row($iter);
|
||||
$iter = $iter->get_next_sibling();
|
||||
}
|
||||
|
||||
parent::split($child, $page_break, $forced);
|
||||
}
|
||||
}
|
||||
|
||||
public function copy(DOMNode $node)
|
||||
{
|
||||
$deco = parent::copy($node);
|
||||
|
||||
// In order to keep columns' widths through pages
|
||||
$deco->_cellmap->set_columns($this->_cellmap->get_columns());
|
||||
$deco->_cellmap->lock_columns();
|
||||
|
||||
return $deco;
|
||||
}
|
||||
|
||||
/**
|
||||
* Static function to locate the parent table of a frame
|
||||
*
|
||||
* @param Frame $frame
|
||||
*
|
||||
* @return Table the table that is an ancestor of $frame
|
||||
*/
|
||||
public static function find_parent_table(Frame $frame)
|
||||
{
|
||||
while ($frame = $frame->get_parent()) {
|
||||
if ($frame->is_table()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $frame;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return this table's Cellmap
|
||||
*
|
||||
* @return Cellmap
|
||||
*/
|
||||
public function get_cellmap()
|
||||
{
|
||||
return $this->_cellmap;
|
||||
}
|
||||
|
||||
//........................................................................
|
||||
|
||||
/**
|
||||
* Check for text nodes between valid table children that only contain white
|
||||
* space, except if white space is to be preserved.
|
||||
*
|
||||
* @param AbstractFrameDecorator $frame
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function isEmptyTextNode(AbstractFrameDecorator $frame): bool
|
||||
{
|
||||
// This is based on the white-space pattern in `FrameReflower\Text`,
|
||||
// i.e. only match on collapsible white space
|
||||
$wsPattern = '/^[^\S\xA0\x{202F}\x{2007}]*$/u';
|
||||
$validChildOrNull = function ($frame) {
|
||||
return $frame === null
|
||||
|| in_array($frame->get_style()->display, self::VALID_CHILDREN, true);
|
||||
};
|
||||
|
||||
return $frame instanceof Text
|
||||
&& !$frame->is_pre()
|
||||
&& preg_match($wsPattern, $frame->get_text())
|
||||
&& $validChildOrNull($frame->get_prev_sibling())
|
||||
&& $validChildOrNull($frame->get_next_sibling());
|
||||
}
|
||||
|
||||
/**
|
||||
* Restructure tree so that the table has the correct structure. Misplaced
|
||||
* children are appropriately wrapped in anonymous row groups, rows, and
|
||||
* cells.
|
||||
*
|
||||
* https://www.w3.org/TR/CSS21/tables.html#anonymous-boxes
|
||||
*/
|
||||
public function normalize(): void
|
||||
{
|
||||
$column_caption = ["table-column-group", "table-column", "table-caption"];
|
||||
$children = iterator_to_array($this->get_children());
|
||||
$tbody = null;
|
||||
|
||||
foreach ($children as $child) {
|
||||
$display = $child->get_style()->display;
|
||||
|
||||
if (in_array($display, self::ROW_GROUPS, true)) {
|
||||
// Reset anonymous tbody
|
||||
$tbody = null;
|
||||
|
||||
// Add headers and footers
|
||||
if ($display === "table-header-group") {
|
||||
$this->_headers[] = $child;
|
||||
} elseif ($display === "table-footer-group") {
|
||||
$this->_footers[] = $child;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (in_array($display, $column_caption, true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Remove empty text nodes between valid children
|
||||
if ($this->isEmptyTextNode($child)) {
|
||||
$this->remove_child($child);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Catch consecutive misplaced frames within a single anonymous group
|
||||
if ($tbody === null) {
|
||||
$tbody = $this->create_anonymous_child("tbody", "table-row-group");
|
||||
$this->insert_child_before($tbody, $child);
|
||||
}
|
||||
|
||||
$tbody->append_child($child);
|
||||
}
|
||||
|
||||
// Handle empty table: Make sure there is at least one row group
|
||||
if (!$this->get_first_child()) {
|
||||
$tbody = $this->create_anonymous_child("tbody", "table-row-group");
|
||||
$this->append_child($tbody);
|
||||
}
|
||||
|
||||
foreach ($this->get_children() as $child) {
|
||||
$display = $child->get_style()->display;
|
||||
|
||||
if (in_array($display, self::ROW_GROUPS, true)) {
|
||||
$this->normalizeRowGroup($child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function normalizeRowGroup(AbstractFrameDecorator $frame): void
|
||||
{
|
||||
$children = iterator_to_array($frame->get_children());
|
||||
$tr = null;
|
||||
|
||||
foreach ($children as $child) {
|
||||
$display = $child->get_style()->display;
|
||||
|
||||
if ($display === "table-row") {
|
||||
// Reset anonymous tr
|
||||
$tr = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Remove empty text nodes between valid children
|
||||
if ($this->isEmptyTextNode($child)) {
|
||||
$frame->remove_child($child);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Catch consecutive misplaced frames within a single anonymous row
|
||||
if ($tr === null) {
|
||||
$tr = $frame->create_anonymous_child("tr", "table-row");
|
||||
$frame->insert_child_before($tr, $child);
|
||||
}
|
||||
|
||||
$tr->append_child($child);
|
||||
}
|
||||
|
||||
// Handle empty row group: Make sure there is at least one row
|
||||
if (!$frame->get_first_child()) {
|
||||
$tr = $frame->create_anonymous_child("tr", "table-row");
|
||||
$frame->append_child($tr);
|
||||
}
|
||||
|
||||
foreach ($frame->get_children() as $child) {
|
||||
$this->normalizeRow($child);
|
||||
}
|
||||
}
|
||||
|
||||
private function normalizeRow(AbstractFrameDecorator $frame): void
|
||||
{
|
||||
$children = iterator_to_array($frame->get_children());
|
||||
$td = null;
|
||||
|
||||
foreach ($children as $child) {
|
||||
$display = $child->get_style()->display;
|
||||
|
||||
if ($display === "table-cell") {
|
||||
// Reset anonymous td
|
||||
$td = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Remove empty text nodes between valid children
|
||||
if ($this->isEmptyTextNode($child)) {
|
||||
$frame->remove_child($child);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Catch consecutive misplaced frames within a single anonymous cell
|
||||
if ($td === null) {
|
||||
$td = $frame->create_anonymous_child("td", "table-cell");
|
||||
$frame->insert_child_before($td, $child);
|
||||
}
|
||||
|
||||
$td->append_child($child);
|
||||
}
|
||||
|
||||
// Handle empty row: Make sure there is at least one cell
|
||||
if (!$frame->get_first_child()) {
|
||||
$td = $frame->create_anonymous_child("td", "table-cell");
|
||||
$frame->append_child($td);
|
||||
}
|
||||
}
|
||||
}
|
||||
143
system/vendor/pancakeapp/dompdf/src/FrameDecorator/TableCell.php
vendored
Executable file
143
system/vendor/pancakeapp/dompdf/src/FrameDecorator/TableCell.php
vendored
Executable file
@@ -0,0 +1,143 @@
|
||||
<?php
|
||||
/**
|
||||
* @package dompdf
|
||||
* @link https://github.com/dompdf/dompdf
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
*/
|
||||
namespace Dompdf\FrameDecorator;
|
||||
|
||||
use Dompdf\Dompdf;
|
||||
use Dompdf\Frame;
|
||||
use Dompdf\FrameDecorator\Block as BlockFrameDecorator;
|
||||
|
||||
/**
|
||||
* Decorates table cells for layout
|
||||
*
|
||||
* @package dompdf
|
||||
*/
|
||||
class TableCell extends BlockFrameDecorator
|
||||
{
|
||||
|
||||
protected $_resolved_borders;
|
||||
protected $_content_height;
|
||||
|
||||
//........................................................................
|
||||
|
||||
/**
|
||||
* TableCell constructor.
|
||||
* @param Frame $frame
|
||||
* @param Dompdf $dompdf
|
||||
*/
|
||||
function __construct(Frame $frame, Dompdf $dompdf)
|
||||
{
|
||||
parent::__construct($frame, $dompdf);
|
||||
$this->_resolved_borders = [];
|
||||
$this->_content_height = 0;
|
||||
}
|
||||
|
||||
//........................................................................
|
||||
|
||||
function reset()
|
||||
{
|
||||
parent::reset();
|
||||
$this->_resolved_borders = [];
|
||||
$this->_content_height = 0;
|
||||
$this->_frame->reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
function get_content_height()
|
||||
{
|
||||
return $this->_content_height;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $height
|
||||
*/
|
||||
function set_content_height($height)
|
||||
{
|
||||
$this->_content_height = $height;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $height
|
||||
*/
|
||||
function set_cell_height($height)
|
||||
{
|
||||
$style = $this->get_style();
|
||||
$v_space = (float)$style->length_in_pt(
|
||||
[
|
||||
$style->margin_top,
|
||||
$style->padding_top,
|
||||
$style->border_top_width,
|
||||
$style->border_bottom_width,
|
||||
$style->padding_bottom,
|
||||
$style->margin_bottom
|
||||
],
|
||||
(float)$style->length_in_pt($style->height)
|
||||
);
|
||||
|
||||
$new_height = $height - $v_space;
|
||||
$style->set_used("height", $new_height);
|
||||
|
||||
if ($new_height > $this->_content_height) {
|
||||
$y_offset = 0;
|
||||
|
||||
// Adjust our vertical alignment
|
||||
switch ($style->vertical_align) {
|
||||
default:
|
||||
case "baseline":
|
||||
// FIXME: this isn't right
|
||||
|
||||
case "top":
|
||||
// Don't need to do anything
|
||||
return;
|
||||
|
||||
case "middle":
|
||||
$y_offset = ($new_height - $this->_content_height) / 2;
|
||||
break;
|
||||
|
||||
case "bottom":
|
||||
$y_offset = $new_height - $this->_content_height;
|
||||
break;
|
||||
}
|
||||
|
||||
if ($y_offset) {
|
||||
// Move our children
|
||||
foreach ($this->get_line_boxes() as $line) {
|
||||
foreach ($line->get_frames() as $frame) {
|
||||
$frame->move(0, $y_offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $side
|
||||
* @param $border_spec
|
||||
*/
|
||||
function set_resolved_border($side, $border_spec)
|
||||
{
|
||||
$this->_resolved_borders[$side] = $border_spec;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $side
|
||||
* @return mixed
|
||||
*/
|
||||
function get_resolved_border($side)
|
||||
{
|
||||
return $this->_resolved_borders[$side];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
function get_resolved_borders()
|
||||
{
|
||||
return $this->_resolved_borders;
|
||||
}
|
||||
}
|
||||
28
system/vendor/pancakeapp/dompdf/src/FrameDecorator/TableRow.php
vendored
Executable file
28
system/vendor/pancakeapp/dompdf/src/FrameDecorator/TableRow.php
vendored
Executable file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
/**
|
||||
* @package dompdf
|
||||
* @link https://github.com/dompdf/dompdf
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
*/
|
||||
namespace Dompdf\FrameDecorator;
|
||||
|
||||
use Dompdf\Dompdf;
|
||||
use Dompdf\Frame;
|
||||
|
||||
/**
|
||||
* Decorates Frames for table row layout
|
||||
*
|
||||
* @package dompdf
|
||||
*/
|
||||
class TableRow extends AbstractFrameDecorator
|
||||
{
|
||||
/**
|
||||
* TableRow constructor.
|
||||
* @param Frame $frame
|
||||
* @param Dompdf $dompdf
|
||||
*/
|
||||
function __construct(Frame $frame, Dompdf $dompdf)
|
||||
{
|
||||
parent::__construct($frame, $dompdf);
|
||||
}
|
||||
}
|
||||
74
system/vendor/pancakeapp/dompdf/src/FrameDecorator/TableRowGroup.php
vendored
Executable file
74
system/vendor/pancakeapp/dompdf/src/FrameDecorator/TableRowGroup.php
vendored
Executable file
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
/**
|
||||
* @package dompdf
|
||||
* @link https://github.com/dompdf/dompdf
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
*/
|
||||
namespace Dompdf\FrameDecorator;
|
||||
|
||||
use Dompdf\Dompdf;
|
||||
use Dompdf\Frame;
|
||||
|
||||
/**
|
||||
* Table row group decorator
|
||||
*
|
||||
* Overrides split() method for tbody, thead & tfoot elements
|
||||
*
|
||||
* @package dompdf
|
||||
*/
|
||||
class TableRowGroup extends AbstractFrameDecorator
|
||||
{
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param Frame $frame Frame to decorate
|
||||
* @param Dompdf $dompdf Current dompdf instance
|
||||
*/
|
||||
function __construct(Frame $frame, Dompdf $dompdf)
|
||||
{
|
||||
parent::__construct($frame, $dompdf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Split the row group at the given child and remove all subsequent child
|
||||
* rows and all subsequent row groups from the cellmap.
|
||||
*/
|
||||
public function split(?Frame $child = null, bool $page_break = false, bool $forced = false): void
|
||||
{
|
||||
if (is_null($child)) {
|
||||
parent::split($child, $page_break, $forced);
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove child & all subsequent rows from the cellmap
|
||||
/** @var Table $parent */
|
||||
$parent = $this->get_parent();
|
||||
$cellmap = $parent->get_cellmap();
|
||||
$iter = $child;
|
||||
|
||||
while ($iter) {
|
||||
$cellmap->remove_row($iter);
|
||||
$iter = $iter->get_next_sibling();
|
||||
}
|
||||
|
||||
// Remove all subsequent row groups from the cellmap
|
||||
$iter = $this->get_next_sibling();
|
||||
|
||||
while ($iter) {
|
||||
$cellmap->remove_row_group($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(null, $page_break, $forced);
|
||||
return;
|
||||
}
|
||||
|
||||
$cellmap->update_row_group($this, $child->get_prev_sibling());
|
||||
parent::split($child, $page_break, $forced);
|
||||
}
|
||||
}
|
||||
191
system/vendor/pancakeapp/dompdf/src/FrameDecorator/Text.php
vendored
Executable file
191
system/vendor/pancakeapp/dompdf/src/FrameDecorator/Text.php
vendored
Executable file
@@ -0,0 +1,191 @@
|
||||
<?php
|
||||
/**
|
||||
* @package dompdf
|
||||
* @link https://github.com/dompdf/dompdf
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
*/
|
||||
namespace Dompdf\FrameDecorator;
|
||||
|
||||
use Dompdf\Dompdf;
|
||||
use Dompdf\Frame;
|
||||
use Dompdf\Exception;
|
||||
|
||||
/**
|
||||
* Decorates Frame objects for text layout
|
||||
*
|
||||
* @package dompdf
|
||||
*/
|
||||
class Text extends AbstractFrameDecorator
|
||||
{
|
||||
/**
|
||||
* @var float
|
||||
*/
|
||||
protected $text_spacing;
|
||||
|
||||
/**
|
||||
* Text constructor.
|
||||
* @param Frame $frame
|
||||
* @param Dompdf $dompdf
|
||||
* @throws Exception
|
||||
*/
|
||||
function __construct(Frame $frame, Dompdf $dompdf)
|
||||
{
|
||||
if (!$frame->is_text_node()) {
|
||||
throw new Exception("Text_Decorator can only be applied to #text nodes.");
|
||||
}
|
||||
|
||||
parent::__construct($frame, $dompdf);
|
||||
$this->text_spacing = 0.0;
|
||||
}
|
||||
|
||||
function reset()
|
||||
{
|
||||
parent::reset();
|
||||
$this->text_spacing = 0.0;
|
||||
}
|
||||
|
||||
// Accessor methods
|
||||
|
||||
/**
|
||||
* @return float
|
||||
*/
|
||||
public function get_text_spacing(): float
|
||||
{
|
||||
return $this->text_spacing;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
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";
|
||||
// }
|
||||
|
||||
// Helpers::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++)
|
||||
// Helpers::pre_r("$i: " . $asc[$i] . " - " . ord($asc[$i]));
|
||||
// Helpers::pre_r("width: " . $this->_dompdf->getFontMetrics()->getTextWidth($text, $style->font_family, $style->font_size));
|
||||
|
||||
return $this->_frame->get_node()->data;
|
||||
}
|
||||
|
||||
//........................................................................
|
||||
|
||||
/**
|
||||
* Vertical padding, border, and margin do not apply when determining the
|
||||
* height for inline 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.
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function get_margin_height(): float
|
||||
{
|
||||
// This function is also called in add_frame_to_line() and is used to
|
||||
// determine the line height
|
||||
$style = $this->get_style();
|
||||
$font = $style->font_family;
|
||||
$size = $style->font_size;
|
||||
$fontHeight = $this->_dompdf->getFontMetrics()->getFontHeight($font, $size);
|
||||
|
||||
return ($style->line_height / ($size > 0 ? $size : 1)) * $fontHeight;
|
||||
}
|
||||
|
||||
public function get_padding_box(): array
|
||||
{
|
||||
$style = $this->_frame->get_style();
|
||||
$pb = $this->_frame->get_padding_box();
|
||||
$pb[3] = $pb["h"] = (float) $style->length_in_pt($style->height);
|
||||
return $pb;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param float $spacing
|
||||
*/
|
||||
public function set_text_spacing(float $spacing): void
|
||||
{
|
||||
$this->text_spacing = $spacing;
|
||||
$this->recalculate_width();
|
||||
}
|
||||
|
||||
/**
|
||||
* Recalculate the text width
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function recalculate_width(): float
|
||||
{
|
||||
$fontMetrics = $this->_dompdf->getFontMetrics();
|
||||
$style = $this->get_style();
|
||||
$text = $this->get_text();
|
||||
$font = $style->font_family;
|
||||
$size = $style->font_size;
|
||||
$word_spacing = $this->text_spacing + $style->word_spacing;
|
||||
$letter_spacing = $style->letter_spacing;
|
||||
$text_width = $fontMetrics->getTextWidth($text, $font, $size, $word_spacing, $letter_spacing);
|
||||
|
||||
$style->set_used("width", $text_width);
|
||||
return $text_width;
|
||||
}
|
||||
|
||||
// Text manipulation methods
|
||||
|
||||
/**
|
||||
* Split the text in this frame at the offset specified. The remaining
|
||||
* text is added as a sibling frame following this one and is returned.
|
||||
*
|
||||
* @param int $offset
|
||||
* @return Frame|null
|
||||
*/
|
||||
function split_text($offset)
|
||||
{
|
||||
if ($offset == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$split = $this->_frame->get_node()->splitText($offset);
|
||||
if ($split === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$deco = $this->copy($split);
|
||||
|
||||
$p = $this->get_parent();
|
||||
$p->insert_child_after($deco, $this, false);
|
||||
|
||||
if ($p instanceof Inline) {
|
||||
$p->split($deco);
|
||||
}
|
||||
|
||||
return $deco;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $offset
|
||||
* @param int $count
|
||||
*/
|
||||
function delete_text($offset, $count)
|
||||
{
|
||||
$this->_frame->get_node()->deleteData($offset, $count);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $text
|
||||
*/
|
||||
function set_text($text)
|
||||
{
|
||||
$this->_frame->get_node()->data = $text;
|
||||
}
|
||||
}
|
||||
705
system/vendor/pancakeapp/dompdf/src/FrameReflower/AbstractFrameReflower.php
vendored
Executable file
705
system/vendor/pancakeapp/dompdf/src/FrameReflower/AbstractFrameReflower.php
vendored
Executable file
@@ -0,0 +1,705 @@
|
||||
<?php
|
||||
/**
|
||||
* @package dompdf
|
||||
* @link https://github.com/dompdf/dompdf
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
*/
|
||||
namespace Dompdf\FrameReflower;
|
||||
|
||||
use Dompdf\Dompdf;
|
||||
use Dompdf\Helpers;
|
||||
use Dompdf\Frame;
|
||||
use Dompdf\Frame\Factory;
|
||||
use Dompdf\FrameDecorator\AbstractFrameDecorator;
|
||||
use Dompdf\FrameDecorator\Block;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @package dompdf
|
||||
*/
|
||||
abstract class AbstractFrameReflower
|
||||
{
|
||||
|
||||
/**
|
||||
* Frame for this reflower
|
||||
*
|
||||
* @var AbstractFrameDecorator
|
||||
*/
|
||||
protected $_frame;
|
||||
|
||||
/**
|
||||
* Cached min/max child size
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $_min_max_child_cache;
|
||||
|
||||
/**
|
||||
* Cached min/max size
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $_min_max_cache;
|
||||
|
||||
/**
|
||||
* AbstractFrameReflower constructor.
|
||||
* @param AbstractFrameDecorator $frame
|
||||
*/
|
||||
function __construct(AbstractFrameDecorator $frame)
|
||||
{
|
||||
$this->_frame = $frame;
|
||||
$this->_min_max_child_cache = null;
|
||||
$this->_min_max_cache = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Dompdf
|
||||
*/
|
||||
function get_dompdf()
|
||||
{
|
||||
return $this->_frame->get_dompdf();
|
||||
}
|
||||
|
||||
public function reset(): void
|
||||
{
|
||||
$this->_min_max_child_cache = null;
|
||||
$this->_min_max_cache = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the actual containing block for absolute and fixed position.
|
||||
*
|
||||
* https://www.w3.org/TR/CSS21/visudet.html#containing-block-details
|
||||
*/
|
||||
protected function determine_absolute_containing_block(): void
|
||||
{
|
||||
$frame = $this->_frame;
|
||||
$style = $frame->get_style();
|
||||
|
||||
switch ($style->position) {
|
||||
case "absolute":
|
||||
$parent = $frame->find_positioned_parent();
|
||||
if ($parent !== $frame->get_root()) {
|
||||
$parent_style = $parent->get_style();
|
||||
$parent_padding_box = $parent->get_padding_box();
|
||||
//FIXME: an accurate measure of the positioned parent height
|
||||
// is not possible until reflow has completed;
|
||||
// we'll fall back to the parent's containing block,
|
||||
// which is wrong for auto-height parents
|
||||
if ($parent_style->height === "auto") {
|
||||
$parent_containing_block = $parent->get_containing_block();
|
||||
$containing_block_height = $parent_containing_block["h"] -
|
||||
(float)$parent_style->length_in_pt([
|
||||
$parent_style->margin_top,
|
||||
$parent_style->margin_bottom,
|
||||
$parent_style->border_top_width,
|
||||
$parent_style->border_bottom_width
|
||||
], $parent_containing_block["w"]);
|
||||
} else {
|
||||
$containing_block_height = $parent_padding_box["h"];
|
||||
}
|
||||
$frame->set_containing_block($parent_padding_box["x"], $parent_padding_box["y"], $parent_padding_box["w"], $containing_block_height);
|
||||
break;
|
||||
}
|
||||
case "fixed":
|
||||
$initial_cb = $frame->get_root()->get_first_child()->get_containing_block();
|
||||
$frame->set_containing_block($initial_cb["x"], $initial_cb["y"], $initial_cb["w"], $initial_cb["h"]);
|
||||
break;
|
||||
default:
|
||||
// Nothing to do, containing block already set via parent
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Collapse frames margins
|
||||
* http://www.w3.org/TR/CSS21/box.html#collapsing-margins
|
||||
*/
|
||||
protected function _collapse_margins(): void
|
||||
{
|
||||
$frame = $this->_frame;
|
||||
|
||||
// Margins of float/absolutely positioned/inline-level elements do not collapse
|
||||
if (!$frame->is_in_flow() || $frame->is_inline_level()
|
||||
|| $frame->get_root() === $frame || $frame->get_parent() === $frame->get_root()
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
$cb = $frame->get_containing_block();
|
||||
$style = $frame->get_style();
|
||||
|
||||
$t = $style->length_in_pt($style->margin_top, $cb["w"]);
|
||||
$b = $style->length_in_pt($style->margin_bottom, $cb["w"]);
|
||||
|
||||
// Handle 'auto' values
|
||||
if ($t === "auto") {
|
||||
$style->set_used("margin_top", 0.0);
|
||||
$t = 0.0;
|
||||
}
|
||||
|
||||
if ($b === "auto") {
|
||||
$style->set_used("margin_bottom", 0.0);
|
||||
$b = 0.0;
|
||||
}
|
||||
|
||||
// Collapse vertical margins:
|
||||
$n = $frame->get_next_sibling();
|
||||
if ( $n && !($n->is_block_level() && $n->is_in_flow()) ) {
|
||||
while ($n = $n->get_next_sibling()) {
|
||||
if ($n->is_block_level() && $n->is_in_flow()) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!$n->get_first_child()) {
|
||||
$n = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($n) {
|
||||
$n_style = $n->get_style();
|
||||
$n_t = (float)$n_style->length_in_pt($n_style->margin_top, $cb["w"]);
|
||||
|
||||
$b = $this->get_collapsed_margin_length($b, $n_t);
|
||||
$style->set_used("margin_bottom", $b);
|
||||
$n_style->set_used("margin_top", 0.0);
|
||||
}
|
||||
|
||||
// Collapse our first child's margin, if there is no border or padding
|
||||
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_level() && $f->is_in_flow()) ) {
|
||||
while ($f = $f->get_next_sibling()) {
|
||||
if ($f->is_block_level() && $f->is_in_flow()) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!$f->get_first_child()) {
|
||||
$f = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Margins are collapsed only between block-level boxes
|
||||
if ($f) {
|
||||
$f_style = $f->get_style();
|
||||
$f_t = (float)$f_style->length_in_pt($f_style->margin_top, $cb["w"]);
|
||||
|
||||
$t = $this->get_collapsed_margin_length($t, $f_t);
|
||||
$style->set_used("margin_top", $t);
|
||||
$f_style->set_used("margin_top", 0.0);
|
||||
}
|
||||
}
|
||||
|
||||
// Collapse our last child's margin, if there is no border or padding
|
||||
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_level() && $l->is_in_flow()) ) {
|
||||
while ($l = $l->get_prev_sibling()) {
|
||||
if ($l->is_block_level() && $l->is_in_flow()) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!$l->get_last_child()) {
|
||||
$l = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Margins are collapsed only between block-level boxes
|
||||
if ($l) {
|
||||
$l_style = $l->get_style();
|
||||
$l_b = (float)$l_style->length_in_pt($l_style->margin_bottom, $cb["w"]);
|
||||
|
||||
$b = $this->get_collapsed_margin_length($b, $l_b);
|
||||
$style->set_used("margin_bottom", $b);
|
||||
$l_style->set_used("margin_bottom", 0.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the combined (collapsed) length of two adjoining margins.
|
||||
*
|
||||
* See http://www.w3.org/TR/CSS21/box.html#collapsing-margins.
|
||||
*
|
||||
* @param float $l1
|
||||
* @param float $l2
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
private function get_collapsed_margin_length(float $l1, float $l2): float
|
||||
{
|
||||
if ($l1 < 0 && $l2 < 0) {
|
||||
return min($l1, $l2); // min(x, y) = - max(abs(x), abs(y)), if x < 0 && y < 0
|
||||
}
|
||||
|
||||
if ($l1 < 0 || $l2 < 0) {
|
||||
return $l1 + $l2; // x + y = x - abs(y), if y < 0
|
||||
}
|
||||
|
||||
return max($l1, $l2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle relative positioning according to
|
||||
* https://www.w3.org/TR/CSS21/visuren.html#relative-positioning.
|
||||
*
|
||||
* @param AbstractFrameDecorator $frame The frame to handle.
|
||||
*/
|
||||
protected function position_relative(AbstractFrameDecorator $frame): void
|
||||
{
|
||||
$style = $frame->get_style();
|
||||
|
||||
if ($style->position === "relative") {
|
||||
$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"]);
|
||||
|
||||
// FIXME RTL case:
|
||||
// if ($left !== "auto" && $right !== "auto") $left = -$right;
|
||||
if ($left === "auto" && $right === "auto") {
|
||||
$left = 0;
|
||||
} elseif ($left === "auto") {
|
||||
$left = -$right;
|
||||
}
|
||||
|
||||
if ($top === "auto" && $bottom === "auto") {
|
||||
$top = 0;
|
||||
} elseif ($top === "auto") {
|
||||
$top = -$bottom;
|
||||
}
|
||||
|
||||
$frame->move($left, $top);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Block|null $block
|
||||
*/
|
||||
abstract function reflow(Block $block = null);
|
||||
|
||||
/**
|
||||
* Resolve the `min-width` property.
|
||||
*
|
||||
* Resolves to 0 if not set or if a percentage and the containing-block
|
||||
* width is not defined.
|
||||
*
|
||||
* @param float|null $cbw Width of the containing block.
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
protected function resolve_min_width(?float $cbw): float
|
||||
{
|
||||
$style = $this->_frame->get_style();
|
||||
$min_width = $style->min_width;
|
||||
|
||||
return $min_width !== "auto"
|
||||
? $style->length_in_pt($min_width, $cbw ?? 0)
|
||||
: 0.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the `max-width` property.
|
||||
*
|
||||
* Resolves to `INF` if not set or if a percentage and the containing-block
|
||||
* width is not defined.
|
||||
*
|
||||
* @param float|null $cbw Width of the containing block.
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
protected function resolve_max_width(?float $cbw): float
|
||||
{
|
||||
$style = $this->_frame->get_style();
|
||||
$max_width = $style->max_width;
|
||||
|
||||
return $max_width !== "none"
|
||||
? $style->length_in_pt($max_width, $cbw ?? INF)
|
||||
: INF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the `min-height` property.
|
||||
*
|
||||
* Resolves to 0 if not set or if a percentage and the containing-block
|
||||
* height is not defined.
|
||||
*
|
||||
* @param float|null $cbh Height of the containing block.
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
protected function resolve_min_height(?float $cbh): float
|
||||
{
|
||||
$style = $this->_frame->get_style();
|
||||
$min_height = $style->min_height;
|
||||
|
||||
return $min_height !== "auto"
|
||||
? $style->length_in_pt($min_height, $cbh ?? 0)
|
||||
: 0.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the `max-height` property.
|
||||
*
|
||||
* Resolves to `INF` if not set or if a percentage and the containing-block
|
||||
* height is not defined.
|
||||
*
|
||||
* @param float|null $cbh Height of the containing block.
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
protected function resolve_max_height(?float $cbh): float
|
||||
{
|
||||
$style = $this->_frame->get_style();
|
||||
$max_height = $style->max_height;
|
||||
|
||||
return $max_height !== "none"
|
||||
? $style->length_in_pt($style->max_height, $cbh ?? INF)
|
||||
: INF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the minimum and maximum preferred width of the contents of the frame,
|
||||
* as requested by its children.
|
||||
*
|
||||
* @return array A two-element array of min and max width.
|
||||
*/
|
||||
public function get_min_max_child_width(): array
|
||||
{
|
||||
if (!is_null($this->_min_max_child_cache)) {
|
||||
return $this->_min_max_child_cache;
|
||||
}
|
||||
|
||||
$low = [];
|
||||
$high = [];
|
||||
|
||||
for ($iter = $this->_frame->get_children(); $iter->valid(); $iter->next()) {
|
||||
$inline_min = 0;
|
||||
$inline_max = 0;
|
||||
|
||||
// Add all adjacent inline widths together to calculate max width
|
||||
while ($iter->valid() && ($iter->current()->is_inline_level() || $iter->current()->get_style()->display === "-dompdf-image")) {
|
||||
/** @var AbstractFrameDecorator */
|
||||
$child = $iter->current();
|
||||
$child->get_reflower()->_set_content();
|
||||
$minmax = $child->get_min_max_width();
|
||||
|
||||
if (in_array($child->get_style()->white_space, ["pre", "nowrap"], true)) {
|
||||
$inline_min += $minmax["min"];
|
||||
} else {
|
||||
$low[] = $minmax["min"];
|
||||
}
|
||||
|
||||
$inline_max += $minmax["max"];
|
||||
$iter->next();
|
||||
}
|
||||
|
||||
if ($inline_min > 0) {
|
||||
$low[] = $inline_min;
|
||||
}
|
||||
if ($inline_max > 0) {
|
||||
$high[] = $inline_max;
|
||||
}
|
||||
|
||||
// Skip children with absolute position
|
||||
if ($iter->valid() && !$iter->current()->is_absolute()) {
|
||||
/** @var AbstractFrameDecorator */
|
||||
$child = $iter->current();
|
||||
$child->get_reflower()->_set_content();
|
||||
list($low[], $high[]) = $child->get_min_max_width();
|
||||
}
|
||||
}
|
||||
|
||||
$min = count($low) ? max($low) : 0;
|
||||
$max = count($high) ? max($high) : 0;
|
||||
|
||||
return $this->_min_max_child_cache = [$min, $max];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the minimum and maximum preferred content-box width of the frame.
|
||||
*
|
||||
* @return array A two-element array of min and max width.
|
||||
*/
|
||||
public function get_min_max_content_width(): array
|
||||
{
|
||||
return $this->get_min_max_child_width();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the minimum and maximum preferred border-box width of the frame.
|
||||
*
|
||||
* Required for shrink-to-fit width calculation, as used in automatic table
|
||||
* layout, absolute positioning, float and inline-block. This provides a
|
||||
* basic implementation. Child classes should override this or
|
||||
* `get_min_max_content_width` as necessary.
|
||||
*
|
||||
* @return array An array `[0 => min, 1 => max, "min" => min, "max" => max]`
|
||||
* of min and max width.
|
||||
*/
|
||||
public function get_min_max_width(): array
|
||||
{
|
||||
if (!is_null($this->_min_max_cache)) {
|
||||
return $this->_min_max_cache;
|
||||
}
|
||||
|
||||
$style = $this->_frame->get_style();
|
||||
[$min, $max] = $this->get_min_max_content_width();
|
||||
|
||||
// Account for margins, borders, and padding
|
||||
$dims = [
|
||||
$style->padding_left,
|
||||
$style->padding_right,
|
||||
$style->border_left_width,
|
||||
$style->border_right_width,
|
||||
$style->margin_left,
|
||||
$style->margin_right
|
||||
];
|
||||
|
||||
// The containing block is not defined yet, treat percentages as 0
|
||||
$delta = (float) $style->length_in_pt($dims, 0);
|
||||
$min += $delta;
|
||||
$max += $delta;
|
||||
|
||||
return $this->_min_max_cache = [$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(["\\\n", '\\"', "\\'"],
|
||||
["", '"', "'"], $string);
|
||||
|
||||
// Convert escaped hex characters into ascii characters (e.g. \A => newline)
|
||||
$string = preg_replace_callback("/\\\\([0-9a-fA-F]{0,6})/",
|
||||
function ($matches) { return \Dompdf\Helpers::unichr(hexdec($matches[1])); },
|
||||
$string);
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a CSS "quotes" property
|
||||
*
|
||||
* https://www.w3.org/TR/css-content-3/#quotes
|
||||
*
|
||||
* @return array An array of pairs of quotes
|
||||
*/
|
||||
protected function _parse_quotes(): array
|
||||
{
|
||||
$quotes = $this->_frame->get_style()->quotes;
|
||||
|
||||
if ($quotes === "none") {
|
||||
return [];
|
||||
}
|
||||
|
||||
if ($quotes === "auto") {
|
||||
// TODO: Use typographically appropriate quotes for the current
|
||||
// language here
|
||||
return [['"', '"'], ["'", "'"]];
|
||||
}
|
||||
|
||||
// Matches quote types
|
||||
$re = '/(\'[^\']*\')|(\"[^\"]*\")/';
|
||||
|
||||
// Split on spaces, except within quotes
|
||||
if (!preg_match_all($re, $quotes, $matches, PREG_SET_ORDER)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$quotes_array = [];
|
||||
foreach ($matches as $_quote) {
|
||||
$quotes_array[] = $this->_parse_string($_quote[0], true);
|
||||
}
|
||||
|
||||
return array_chunk($quotes_array, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the CSS "content" property
|
||||
*
|
||||
* https://www.w3.org/TR/CSS21/generate.html#content
|
||||
*
|
||||
* @return string The resulting string
|
||||
*/
|
||||
protected function _parse_content(): string
|
||||
{
|
||||
$style = $this->_frame->get_style();
|
||||
$content = $style->content;
|
||||
|
||||
if ($content === "normal" || $content === "none") {
|
||||
return "";
|
||||
}
|
||||
|
||||
$quotes = $this->_parse_quotes();
|
||||
$text = "";
|
||||
|
||||
foreach ($content as $val) {
|
||||
// String
|
||||
if (in_array(mb_substr($val, 0, 1), ['"', "'"], true)) {
|
||||
$text .= $this->_parse_string($val);
|
||||
continue;
|
||||
}
|
||||
|
||||
$val = mb_strtolower($val);
|
||||
|
||||
// Keywords
|
||||
if ($val === "open-quote") {
|
||||
// FIXME: Take quotation depth into account
|
||||
if (isset($quotes[0][0])) {
|
||||
$text .= $quotes[0][0];
|
||||
}
|
||||
continue;
|
||||
} elseif ($val === "close-quote") {
|
||||
// FIXME: Take quotation depth into account
|
||||
if (isset($quotes[0][1])) {
|
||||
$text .= $quotes[0][1];
|
||||
}
|
||||
continue;
|
||||
} elseif ($val === "no-open-quote") {
|
||||
// FIXME: Increment quotation depth
|
||||
continue;
|
||||
} elseif ($val === "no-close-quote") {
|
||||
// FIXME: Decrement quotation depth
|
||||
continue;
|
||||
}
|
||||
|
||||
// attr()
|
||||
if (mb_substr($val, 0, 5) === "attr(") {
|
||||
$i = mb_strpos($val, ")");
|
||||
if ($i === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$attr = trim(mb_substr($val, 5, $i - 5));
|
||||
if ($attr === "") {
|
||||
continue;
|
||||
}
|
||||
|
||||
$text .= $this->_frame->get_parent()->get_node()->getAttribute($attr);
|
||||
continue;
|
||||
}
|
||||
|
||||
// counter()/counters()
|
||||
if (mb_substr($val, 0, 7) === "counter") {
|
||||
// Handle counter() references:
|
||||
// http://www.w3.org/TR/CSS21/generate.html#content
|
||||
|
||||
$i = mb_strpos($val, ")");
|
||||
if ($i === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
preg_match('/(counters?)(^\()*?\(\s*([^\s,]+)\s*(,\s*["\']?([^"\'\)]*)["\']?\s*(,\s*([^\s)]+)\s*)?)?\)/i', $val, $args);
|
||||
$counter_id = $args[3];
|
||||
|
||||
if (strtolower($args[1]) === "counter") {
|
||||
// counter(name [,style])
|
||||
if (isset($args[5])) {
|
||||
$type = trim($args[5]);
|
||||
} else {
|
||||
$type = "decimal";
|
||||
}
|
||||
$p = $this->_frame->lookup_counter_frame($counter_id);
|
||||
|
||||
$text .= $p->counter_value($counter_id, $type);
|
||||
} elseif (strtolower($args[1]) === "counters") {
|
||||
// counters(name, string [,style])
|
||||
if (isset($args[5])) {
|
||||
$string = $this->_parse_string($args[5]);
|
||||
} else {
|
||||
$string = "";
|
||||
}
|
||||
|
||||
if (isset($args[7])) {
|
||||
$type = trim($args[7]);
|
||||
} else {
|
||||
$type = "decimal";
|
||||
}
|
||||
|
||||
$p = $this->_frame->lookup_counter_frame($counter_id);
|
||||
$tmp = [];
|
||||
while ($p) {
|
||||
// We only want to use the counter values when they actually increment the counter
|
||||
if (array_key_exists($counter_id, $p->_counters)) {
|
||||
array_unshift($tmp, $p->counter_value($counter_id, $type));
|
||||
}
|
||||
$p = $p->lookup_counter_frame($counter_id);
|
||||
}
|
||||
$text .= implode($string, $tmp);
|
||||
} else {
|
||||
// countertops?
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return $text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle counters and set generated content if the frame is a
|
||||
* generated-content frame.
|
||||
*/
|
||||
protected function _set_content(): void
|
||||
{
|
||||
$frame = $this->_frame;
|
||||
|
||||
if ($frame->content_set) {
|
||||
return;
|
||||
}
|
||||
|
||||
$style = $frame->get_style();
|
||||
|
||||
if (($reset = $style->counter_reset) !== "none") {
|
||||
$frame->reset_counters($reset);
|
||||
}
|
||||
|
||||
if (($increment = $style->counter_increment) !== "none") {
|
||||
$frame->increment_counters($increment);
|
||||
}
|
||||
|
||||
if ($frame->get_node()->nodeName === "dompdf_generated") {
|
||||
$content = $this->_parse_content();
|
||||
|
||||
if ($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);
|
||||
|
||||
Factory::decorate_frame($new_frame, $frame->get_dompdf(), $frame->get_root());
|
||||
$frame->append_child($new_frame);
|
||||
}
|
||||
}
|
||||
|
||||
$frame->content_set = true;
|
||||
}
|
||||
}
|
||||
948
system/vendor/pancakeapp/dompdf/src/FrameReflower/Block.php
vendored
Executable file
948
system/vendor/pancakeapp/dompdf/src/FrameReflower/Block.php
vendored
Executable file
@@ -0,0 +1,948 @@
|
||||
<?php
|
||||
/**
|
||||
* @package dompdf
|
||||
* @link https://github.com/dompdf/dompdf
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
*/
|
||||
namespace Dompdf\FrameReflower;
|
||||
|
||||
use Dompdf\FrameDecorator\AbstractFrameDecorator;
|
||||
use Dompdf\FrameDecorator\Block as BlockFrameDecorator;
|
||||
use Dompdf\FrameDecorator\TableCell as TableCellFrameDecorator;
|
||||
use Dompdf\FrameDecorator\Text as TextFrameDecorator;
|
||||
use Dompdf\Exception;
|
||||
use Dompdf\Css\Style;
|
||||
use Dompdf\Helpers;
|
||||
|
||||
/**
|
||||
* Reflows block frames
|
||||
*
|
||||
* @package dompdf
|
||||
*/
|
||||
class Block extends AbstractFrameReflower
|
||||
{
|
||||
// Minimum line width to justify, as fraction of available width
|
||||
const MIN_JUSTIFY_WIDTH = 0.80;
|
||||
|
||||
/**
|
||||
* Frame for this reflower
|
||||
*
|
||||
* @var BlockFrameDecorator
|
||||
*/
|
||||
protected $_frame;
|
||||
|
||||
function __construct(BlockFrameDecorator $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)
|
||||
{
|
||||
$frame = $this->_frame;
|
||||
$style = $frame->get_style();
|
||||
$absolute = $frame->is_absolute();
|
||||
|
||||
$cb = $frame->get_containing_block();
|
||||
$w = $cb["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 = [$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 ($absolute) {
|
||||
$dims[] = $left !== "auto" ? $left : 0;
|
||||
$dims[] = $right !== "auto" ? $right : 0;
|
||||
}
|
||||
|
||||
$sum = (float)$style->length_in_pt($dims, $w);
|
||||
|
||||
// Compare to the containing block
|
||||
$diff = $w - $sum;
|
||||
|
||||
if ($absolute) {
|
||||
// Absolutely positioned
|
||||
// http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-width
|
||||
|
||||
if ($width === "auto" || $left === "auto" || $right === "auto") {
|
||||
// "all of the three are 'auto'" logic + otherwise case
|
||||
if ($lm === "auto") {
|
||||
$lm = 0;
|
||||
}
|
||||
if ($rm === "auto") {
|
||||
$rm = 0;
|
||||
}
|
||||
|
||||
$block_parent = $frame->find_block_parent();
|
||||
$parent_content = $block_parent->get_content_box();
|
||||
$line = $block_parent->get_current_line_box();
|
||||
|
||||
// TODO: This is the in-flow inline position. Use the in-flow
|
||||
// block position if the original display type is block-level
|
||||
$inflow_x = $parent_content["x"] - $cb["x"] + $line->left + $line->w;
|
||||
|
||||
if ($width === "auto" && $left === "auto" && $right === "auto") {
|
||||
// rule 3, per instruction preceding rule set
|
||||
// shrink-to-fit width
|
||||
$left = $inflow_x;
|
||||
[$min, $max] = $this->get_min_max_child_width();
|
||||
$width = min(max($min, $diff - $left), $max);
|
||||
$right = $diff - $left - $width;
|
||||
} elseif ($width === "auto" && $left === "auto") {
|
||||
// rule 1
|
||||
// shrink-to-fit width
|
||||
[$min, $max] = $this->get_min_max_child_width();
|
||||
$width = min(max($min, $diff), $max);
|
||||
$left = $diff - $width;
|
||||
} elseif ($width === "auto" && $right === "auto") {
|
||||
// rule 3
|
||||
// shrink-to-fit width
|
||||
[$min, $max] = $this->get_min_max_child_width();
|
||||
$width = min(max($min, $diff), $max);
|
||||
$right = $diff - $width;
|
||||
} elseif ($left === "auto" && $right === "auto") {
|
||||
// rule 2
|
||||
$left = $inflow_x;
|
||||
$right = $diff - $left;
|
||||
} elseif ($left === "auto") {
|
||||
// rule 4
|
||||
$left = $diff;
|
||||
} elseif ($width === "auto") {
|
||||
// rule 5
|
||||
$width = max($diff, 0);
|
||||
} else {
|
||||
// $right === "auto"
|
||||
// rule 6
|
||||
$right = $diff;
|
||||
}
|
||||
} else {
|
||||
// "none of the three are 'auto'" logic described in paragraph preceding the rules
|
||||
if ($diff >= 0) {
|
||||
if ($lm === "auto" && $rm === "auto") {
|
||||
$lm = $rm = $diff / 2;
|
||||
} elseif ($lm === "auto") {
|
||||
$lm = $diff;
|
||||
} elseif ($rm === "auto") {
|
||||
$rm = $diff;
|
||||
}
|
||||
} else {
|
||||
// over-constrained, solve for right
|
||||
$right = $right + $diff;
|
||||
|
||||
if ($lm === "auto") {
|
||||
$lm = 0;
|
||||
}
|
||||
if ($rm === "auto") {
|
||||
$rm = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
} elseif ($style->float !== "none" || $style->display === "inline-block") {
|
||||
// Shrink-to-fit width for float and inline block
|
||||
// https://www.w3.org/TR/CSS21/visudet.html#float-width
|
||||
// https://www.w3.org/TR/CSS21/visudet.html#inlineblock-width
|
||||
|
||||
if ($width === "auto") {
|
||||
[$min, $max] = $this->get_min_max_child_width();
|
||||
$width = min(max($min, $diff), $max);
|
||||
}
|
||||
if ($lm === "auto") {
|
||||
$lm = 0;
|
||||
}
|
||||
if ($rm === "auto") {
|
||||
$rm = 0;
|
||||
}
|
||||
} else {
|
||||
// Block-level, normal flow
|
||||
// https://www.w3.org/TR/CSS21/visudet.html#blockwidth
|
||||
|
||||
if ($diff >= 0) {
|
||||
// Find auto properties and get them to take up the slack
|
||||
if ($width === "auto") {
|
||||
$width = $diff;
|
||||
|
||||
if ($lm === "auto") {
|
||||
$lm = 0;
|
||||
}
|
||||
if ($rm === "auto") {
|
||||
$rm = 0;
|
||||
}
|
||||
} elseif ($lm === "auto" && $rm === "auto") {
|
||||
$lm = $rm = $diff / 2;
|
||||
} elseif ($lm === "auto") {
|
||||
$lm = $diff;
|
||||
} elseif ($rm === "auto") {
|
||||
$rm = $diff;
|
||||
}
|
||||
} else {
|
||||
// We are over constrained--set margin-right to the difference
|
||||
$rm = (float) $rm + $diff;
|
||||
|
||||
if ($width === "auto") {
|
||||
$width = 0;
|
||||
}
|
||||
if ($lm === "auto") {
|
||||
$lm = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
"width" => $width,
|
||||
"margin_left" => $lm,
|
||||
"margin_right" => $rm,
|
||||
"left" => $left,
|
||||
"right" => $right,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Call the above function, but resolve max/min widths
|
||||
*
|
||||
* @throws Exception
|
||||
* @return array
|
||||
*/
|
||||
protected function _calculate_restricted_width()
|
||||
{
|
||||
$frame = $this->_frame;
|
||||
$style = $frame->get_style();
|
||||
$cb = $frame->get_containing_block();
|
||||
|
||||
if (!isset($cb["w"])) {
|
||||
throw new Exception("Box property calculation requires containing block width");
|
||||
}
|
||||
|
||||
$width = $style->length_in_pt($style->width, $cb["w"]);
|
||||
|
||||
$values = $this->_calculate_width($width);
|
||||
$margin_left = $values["margin_left"];
|
||||
$margin_right = $values["margin_right"];
|
||||
$width = $values["width"];
|
||||
$left = $values["left"];
|
||||
$right = $values["right"];
|
||||
|
||||
// Handle min/max width
|
||||
// https://www.w3.org/TR/CSS21/visudet.html#min-max-widths
|
||||
$min_width = $this->resolve_min_width($cb["w"]);
|
||||
$max_width = $this->resolve_max_width($cb["w"]);
|
||||
|
||||
if ($width > $max_width) {
|
||||
$values = $this->_calculate_width($max_width);
|
||||
$margin_left = $values["margin_left"];
|
||||
$margin_right = $values["margin_right"];
|
||||
$width = $values["width"];
|
||||
$left = $values["left"];
|
||||
$right = $values["right"];
|
||||
}
|
||||
|
||||
if ($width < $min_width) {
|
||||
$values = $this->_calculate_width($min_width);
|
||||
$margin_left = $values["margin_left"];
|
||||
$margin_right = $values["margin_right"];
|
||||
$width = $values["width"];
|
||||
$left = $values["left"];
|
||||
$right = $values["right"];
|
||||
}
|
||||
|
||||
return [$width, $margin_left, $margin_right, $left, $right];
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the unrestricted height of content within the block
|
||||
* not by adding each line's height, but by getting the last line's position.
|
||||
* This because lines could have been pushed lower by a clearing element.
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
protected function _calculate_content_height()
|
||||
{
|
||||
$height = 0;
|
||||
$lines = $this->_frame->get_line_boxes();
|
||||
if (count($lines) > 0) {
|
||||
$last_line = end($lines);
|
||||
$content_box = $this->_frame->get_content_box();
|
||||
$height = $last_line->y + $last_line->h - $content_box["y"];
|
||||
}
|
||||
return $height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the frame's restricted height
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function _calculate_restricted_height()
|
||||
{
|
||||
$frame = $this->_frame;
|
||||
$style = $frame->get_style();
|
||||
$content_height = $this->_calculate_content_height();
|
||||
$cb = $frame->get_containing_block();
|
||||
|
||||
$height = $style->length_in_pt($style->height, $cb["h"]);
|
||||
$margin_top = $style->length_in_pt($style->margin_top, $cb["w"]);
|
||||
$margin_bottom = $style->length_in_pt($style->margin_bottom, $cb["w"]);
|
||||
|
||||
$top = $style->length_in_pt($style->top, $cb["h"]);
|
||||
$bottom = $style->length_in_pt($style->bottom, $cb["h"]);
|
||||
|
||||
if ($frame->is_absolute()) {
|
||||
// Absolutely positioned
|
||||
// http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-height
|
||||
|
||||
$h_dims = [
|
||||
$top !== "auto" ? $top : 0,
|
||||
$height !== "auto" ? $height : 0,
|
||||
$bottom !== "auto" ? $bottom : 0
|
||||
];
|
||||
$w_dims = [
|
||||
$style->margin_top !== "auto" ? $style->margin_top : 0,
|
||||
$style->padding_top,
|
||||
$style->border_top_width,
|
||||
$style->border_bottom_width,
|
||||
$style->padding_bottom,
|
||||
$style->margin_bottom !== "auto" ? $style->margin_bottom : 0
|
||||
];
|
||||
|
||||
$sum = (float)$style->length_in_pt($h_dims, $cb["h"])
|
||||
+ (float)$style->length_in_pt($w_dims, $cb["w"]);
|
||||
|
||||
$diff = $cb["h"] - $sum;
|
||||
|
||||
if ($height === "auto" || $top === "auto" || $bottom === "auto") {
|
||||
// "all of the three are 'auto'" logic + otherwise case
|
||||
if ($margin_top === "auto") {
|
||||
$margin_top = 0;
|
||||
}
|
||||
if ($margin_bottom === "auto") {
|
||||
$margin_bottom = 0;
|
||||
}
|
||||
|
||||
$block_parent = $frame->find_block_parent();
|
||||
$current_line = $block_parent->get_current_line_box();
|
||||
|
||||
// TODO: This is the in-flow inline position. Use the in-flow
|
||||
// block position if the original display type is block-level
|
||||
$inflow_y = $current_line->y - $cb["y"];
|
||||
|
||||
if ($height === "auto" && $top === "auto" && $bottom === "auto") {
|
||||
// rule 3, per instruction preceding rule set
|
||||
$top = $inflow_y;
|
||||
$height = $content_height;
|
||||
$bottom = $diff - $top - $height;
|
||||
} elseif ($height === "auto" && $top === "auto") {
|
||||
// rule 1
|
||||
$height = $content_height;
|
||||
$top = $diff - $height;
|
||||
} elseif ($height === "auto" && $bottom === "auto") {
|
||||
// rule 3
|
||||
$height = $content_height;
|
||||
$bottom = $diff - $height;
|
||||
} elseif ($top === "auto" && $bottom === "auto") {
|
||||
// rule 2
|
||||
$top = $inflow_y;
|
||||
$bottom = $diff - $top;
|
||||
} elseif ($top === "auto") {
|
||||
// rule 4
|
||||
$top = $diff;
|
||||
} elseif ($height === "auto") {
|
||||
// rule 5
|
||||
$height = max($diff, 0);
|
||||
} else {
|
||||
// $bottom === "auto"
|
||||
// rule 6
|
||||
$bottom = $diff;
|
||||
}
|
||||
} else {
|
||||
// "none of the three are 'auto'" logic described in paragraph preceding the rules
|
||||
if ($diff >= 0) {
|
||||
if ($margin_top === "auto" && $margin_bottom === "auto") {
|
||||
$margin_top = $margin_bottom = $diff / 2;
|
||||
} elseif ($margin_top === "auto") {
|
||||
$margin_top = $diff;
|
||||
} elseif ($margin_bottom === "auto") {
|
||||
$margin_bottom = $diff;
|
||||
}
|
||||
} else {
|
||||
// over-constrained, solve for bottom
|
||||
$bottom = $bottom + $diff;
|
||||
|
||||
if ($margin_top === "auto") {
|
||||
$margin_top = 0;
|
||||
}
|
||||
if ($margin_bottom === "auto") {
|
||||
$margin_bottom = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// https://www.w3.org/TR/CSS21/visudet.html#normal-block
|
||||
// https://www.w3.org/TR/CSS21/visudet.html#block-root-margin
|
||||
|
||||
if ($height === "auto") {
|
||||
$height = $content_height;
|
||||
}
|
||||
if ($margin_top === "auto") {
|
||||
$margin_top = 0;
|
||||
}
|
||||
if ($margin_bottom === "auto") {
|
||||
$margin_bottom = 0;
|
||||
}
|
||||
|
||||
// Handle min/max height
|
||||
// https://www.w3.org/TR/CSS21/visudet.html#min-max-heights
|
||||
$min_height = $this->resolve_min_height($cb["h"]);
|
||||
$max_height = $this->resolve_max_height($cb["h"]);
|
||||
$height = Helpers::clamp($height, $min_height, $max_height);
|
||||
}
|
||||
|
||||
// TODO: Need to also take min/max height into account for absolute
|
||||
// positioning, using similar logic to the `_calculate_width`/
|
||||
// `calculate_restricted_width` split above. The non-absolute case
|
||||
// can simply clamp height within min/max, as margins and offsets are
|
||||
// not affected
|
||||
|
||||
return [$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 = (float)$style->length_in_pt($style->width, $w);
|
||||
$text_indent = (float)$style->length_in_pt($style->text_indent, $w);
|
||||
|
||||
switch ($style->text_align) {
|
||||
default:
|
||||
case "left":
|
||||
foreach ($this->_frame->get_line_boxes() as $line) {
|
||||
if (!$line->inline) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$line->trim_trailing_ws();
|
||||
|
||||
if ($line->left) {
|
||||
foreach ($line->frames_to_align() as $frame) {
|
||||
$frame->move($line->left, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case "right":
|
||||
foreach ($this->_frame->get_line_boxes() as $i => $line) {
|
||||
if (!$line->inline) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$line->trim_trailing_ws();
|
||||
|
||||
$indent = $i === 0 ? $text_indent : 0;
|
||||
$dx = $width - $line->w - $line->right - $indent;
|
||||
|
||||
foreach ($line->frames_to_align() as $frame) {
|
||||
$frame->move($dx, 0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case "justify":
|
||||
// We justify all lines except the last one, unless the frame
|
||||
// has been split, in which case the actual last line is part of
|
||||
// the split-off frame
|
||||
$lines = $this->_frame->get_line_boxes();
|
||||
$last_line_index = $this->_frame->is_split ? null : count($lines) - 1;
|
||||
|
||||
foreach ($lines as $i => $line) {
|
||||
if (!$line->inline) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$line->trim_trailing_ws();
|
||||
|
||||
if ($line->left) {
|
||||
foreach ($line->frames_to_align() as $frame) {
|
||||
$frame->move($line->left, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if ($line->br || $i === $last_line_index) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$frames = $line->get_frames();
|
||||
$other_frame_count = 0;
|
||||
|
||||
foreach ($frames as $frame) {
|
||||
if (!($frame instanceof TextFrameDecorator)) {
|
||||
$other_frame_count++;
|
||||
}
|
||||
}
|
||||
|
||||
$word_count = $line->wc + $other_frame_count;
|
||||
|
||||
// Set the spacing for each child
|
||||
if ($word_count > 1) {
|
||||
$indent = $i === 0 ? $text_indent : 0;
|
||||
$spacing = ($width - $line->get_width() - $indent) / ($word_count - 1);
|
||||
} else {
|
||||
$spacing = 0;
|
||||
}
|
||||
|
||||
$dx = 0;
|
||||
foreach ($frames as $frame) {
|
||||
if ($frame instanceof TextFrameDecorator) {
|
||||
$text = $frame->get_text();
|
||||
$spaces = mb_substr_count($text, " ");
|
||||
|
||||
$frame->move($dx, 0);
|
||||
$frame->set_text_spacing($spacing);
|
||||
|
||||
$dx += $spaces * $spacing;
|
||||
} else {
|
||||
$frame->move($dx, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// The line (should) now occupy the entire width
|
||||
$line->w = $width;
|
||||
}
|
||||
break;
|
||||
|
||||
case "center":
|
||||
case "centre":
|
||||
foreach ($this->_frame->get_line_boxes() as $i => $line) {
|
||||
if (!$line->inline) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$line->trim_trailing_ws();
|
||||
|
||||
$indent = $i === 0 ? $text_indent : 0;
|
||||
$dx = ($width + $line->left - $line->w - $line->right - $indent) / 2;
|
||||
|
||||
foreach ($line->frames_to_align() as $frame) {
|
||||
$frame->move($dx, 0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Align inline children vertically.
|
||||
* Aligns each child vertically after each line is reflowed
|
||||
*/
|
||||
function vertical_align()
|
||||
{
|
||||
$fontMetrics = $this->get_dompdf()->getFontMetrics();
|
||||
|
||||
foreach ($this->_frame->get_line_boxes() as $line) {
|
||||
$height = $line->h;
|
||||
|
||||
// Move all markers to the top of the line box
|
||||
foreach ($line->get_list_markers() as $marker) {
|
||||
$x = $marker->get_position("x");
|
||||
$marker->set_position($x, $line->y);
|
||||
}
|
||||
|
||||
foreach ($line->frames_to_align() as $frame) {
|
||||
$style = $frame->get_style();
|
||||
$isInlineBlock = $style->display !== "inline"
|
||||
&& $style->display !== "-dompdf-list-bullet";
|
||||
|
||||
$baseline = $fontMetrics->getFontBaseline($style->font_family, $style->font_size);
|
||||
$y_offset = 0;
|
||||
|
||||
//FIXME: The 0.8 ratio applied to the height is arbitrary (used to accommodate descenders?)
|
||||
if ($isInlineBlock) {
|
||||
// Workaround: Skip vertical alignment if the frame is the
|
||||
// only one one the line, excluding empty text frames, which
|
||||
// may be the result of trailing white space
|
||||
// FIXME: This special case should be removed once vertical
|
||||
// alignment is properly fixed
|
||||
$skip = true;
|
||||
|
||||
foreach ($line->get_frames() as $other) {
|
||||
if ($other !== $frame
|
||||
&& !($other->is_text_node() && $other->get_node()->nodeValue === "")
|
||||
) {
|
||||
$skip = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($skip) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$marginHeight = $frame->get_margin_height();
|
||||
$imageHeightDiff = $height * 0.8 - $marginHeight;
|
||||
|
||||
$align = $frame->get_style()->vertical_align;
|
||||
if (in_array($align, Style::VERTICAL_ALIGN_KEYWORDS, true)) {
|
||||
switch ($align) {
|
||||
case "middle":
|
||||
$y_offset = $imageHeightDiff / 2;
|
||||
break;
|
||||
|
||||
case "sub":
|
||||
$y_offset = 0.3 * $height + $imageHeightDiff;
|
||||
break;
|
||||
|
||||
case "super":
|
||||
$y_offset = -0.2 * $height + $imageHeightDiff;
|
||||
break;
|
||||
|
||||
case "text-top": // FIXME: this should be the height of the frame minus the height of the text
|
||||
$y_offset = $height - $style->line_height;
|
||||
break;
|
||||
|
||||
case "top":
|
||||
break;
|
||||
|
||||
case "text-bottom": // FIXME: align bottom of image with the descender?
|
||||
case "bottom":
|
||||
$y_offset = 0.3 * $height + $imageHeightDiff;
|
||||
break;
|
||||
|
||||
case "baseline":
|
||||
default:
|
||||
$y_offset = $imageHeightDiff;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
$y_offset = $baseline - (float)$style->length_in_pt($align, $style->font_size) - $marginHeight;
|
||||
}
|
||||
} else {
|
||||
$parent = $frame->get_parent();
|
||||
if ($parent instanceof TableCellFrameDecorator) {
|
||||
$align = "baseline";
|
||||
} else {
|
||||
$align = $parent->get_style()->vertical_align;
|
||||
}
|
||||
if (in_array($align, Style::VERTICAL_ALIGN_KEYWORDS, true)) {
|
||||
switch ($align) {
|
||||
case "middle":
|
||||
$y_offset = ($height * 0.8 - $baseline) / 2;
|
||||
break;
|
||||
|
||||
case "sub":
|
||||
$y_offset = $height * 0.8 - $baseline * 0.5;
|
||||
break;
|
||||
|
||||
case "super":
|
||||
$y_offset = $height * 0.8 - $baseline * 1.4;
|
||||
break;
|
||||
|
||||
case "text-top":
|
||||
case "top": // Not strictly accurate, but good enough for now
|
||||
break;
|
||||
|
||||
case "text-bottom":
|
||||
case "bottom":
|
||||
$y_offset = $height * 0.8 - $baseline;
|
||||
break;
|
||||
|
||||
case "baseline":
|
||||
default:
|
||||
$y_offset = $height * 0.8 - $baseline;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
$y_offset = $height * 0.8 - $baseline - (float)$style->length_in_pt($align, $style->font_size);
|
||||
}
|
||||
}
|
||||
|
||||
if ($y_offset !== 0) {
|
||||
$frame->move(0, $y_offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AbstractFrameDecorator $child
|
||||
*/
|
||||
function process_clear(AbstractFrameDecorator $child)
|
||||
{
|
||||
$child_style = $child->get_style();
|
||||
$root = $this->_frame->get_root();
|
||||
|
||||
// Handle "clear"
|
||||
if ($child_style->clear !== "none") {
|
||||
//TODO: this is a WIP for handling clear/float frames that are in between inline frames
|
||||
if ($child->get_prev_sibling() !== null) {
|
||||
$this->_frame->add_line();
|
||||
}
|
||||
if ($child_style->float !== "none" && $child->get_next_sibling()) {
|
||||
$this->_frame->set_current_line_number($this->_frame->get_current_line_number() - 1);
|
||||
}
|
||||
|
||||
$lowest_y = $root->get_lowest_float_offset($child);
|
||||
|
||||
// If a float is still applying, we handle it
|
||||
if ($lowest_y) {
|
||||
if ($child->is_in_flow()) {
|
||||
$line_box = $this->_frame->get_current_line_box();
|
||||
$line_box->y = $lowest_y + $child->get_margin_height();
|
||||
$line_box->left = 0;
|
||||
$line_box->right = 0;
|
||||
}
|
||||
|
||||
$child->move(0, $lowest_y - $child->get_position("y"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AbstractFrameDecorator $child
|
||||
* @param float $cb_x
|
||||
* @param float $cb_w
|
||||
*/
|
||||
function process_float(AbstractFrameDecorator $child, $cb_x, $cb_w)
|
||||
{
|
||||
$child_style = $child->get_style();
|
||||
$root = $this->_frame->get_root();
|
||||
|
||||
// Handle "float"
|
||||
if ($child_style->float !== "none") {
|
||||
$root->add_floating_frame($child);
|
||||
|
||||
// Remove next frame's beginning whitespace
|
||||
$next = $child->get_next_sibling();
|
||||
if ($next && $next instanceof TextFrameDecorator) {
|
||||
$next->set_text(ltrim($next->get_text()));
|
||||
}
|
||||
|
||||
$line_box = $this->_frame->get_current_line_box();
|
||||
list($old_x, $old_y) = $child->get_position();
|
||||
|
||||
$float_x = $cb_x;
|
||||
$float_y = $old_y;
|
||||
$float_w = $child->get_margin_width();
|
||||
|
||||
if ($child_style->clear === "none") {
|
||||
switch ($child_style->float) {
|
||||
case "left":
|
||||
$float_x += $line_box->left;
|
||||
break;
|
||||
case "right":
|
||||
$float_x += ($cb_w - $line_box->right - $float_w);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if ($child_style->float === "right") {
|
||||
$float_x += ($cb_w - $float_w);
|
||||
}
|
||||
}
|
||||
|
||||
if ($cb_w < $float_x + $float_w - $old_x) {
|
||||
// TODO handle when floating elements don't fit
|
||||
}
|
||||
|
||||
$line_box->get_float_offsets();
|
||||
|
||||
if ($child->_float_next_line) {
|
||||
$float_y += $line_box->h;
|
||||
}
|
||||
|
||||
$child->set_position($float_x, $float_y);
|
||||
$child->move($float_x - $old_x, $float_y - $old_y, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param BlockFrameDecorator $block
|
||||
*/
|
||||
function reflow(BlockFrameDecorator $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;
|
||||
}
|
||||
|
||||
$this->determine_absolute_containing_block();
|
||||
|
||||
// Counters and generated content
|
||||
$this->_set_content();
|
||||
|
||||
// Inherit any dangling list markers
|
||||
if ($block && $this->_frame->is_in_flow()) {
|
||||
$this->_frame->inherit_dangling_markers($block);
|
||||
}
|
||||
|
||||
// Collapse margins if required
|
||||
$this->_collapse_margins();
|
||||
|
||||
$style = $this->_frame->get_style();
|
||||
$cb = $this->_frame->get_containing_block();
|
||||
|
||||
// Determine the constraints imposed by this frame: calculate the width
|
||||
// of the content area:
|
||||
[$width, $margin_left, $margin_right, $left, $right] = $this->_calculate_restricted_width();
|
||||
|
||||
// Store the calculated properties
|
||||
$style->set_used("width", $width);
|
||||
$style->set_used("margin_left", $margin_left);
|
||||
$style->set_used("margin_right", $margin_right);
|
||||
$style->set_used("left", $left);
|
||||
$style->set_used("right", $right);
|
||||
|
||||
$margin_top = $style->length_in_pt($style->margin_top, $cb["w"]);
|
||||
$margin_bottom = $style->length_in_pt($style->margin_bottom, $cb["w"]);
|
||||
|
||||
$auto_top = $style->top === "auto";
|
||||
$auto_margin_top = $margin_top === "auto";
|
||||
|
||||
// Update the position
|
||||
$this->_frame->position();
|
||||
[$x, $y] = $this->_frame->get_position();
|
||||
|
||||
// Adjust the first line based on the text-indent property
|
||||
$indent = (float)$style->length_in_pt($style->text_indent, $cb["w"]);
|
||||
$this->_frame->increase_line_width($indent);
|
||||
|
||||
// Determine the content edge
|
||||
$top = (float)$style->length_in_pt([
|
||||
$margin_top !== "auto" ? $margin_top : 0,
|
||||
$style->border_top_width,
|
||||
$style->padding_top
|
||||
], $cb["w"]);
|
||||
$bottom = (float)$style->length_in_pt([
|
||||
$margin_bottom !== "auto" ? $margin_bottom : 0,
|
||||
$style->border_bottom_width,
|
||||
$style->padding_bottom
|
||||
], $cb["w"]);
|
||||
|
||||
$cb_x = $x + (float)$margin_left + (float)$style->length_in_pt([$style->border_left_width,
|
||||
$style->padding_left], $cb["w"]);
|
||||
|
||||
$cb_y = $y + $top;
|
||||
|
||||
$height = $style->length_in_pt($style->height, $cb["h"]);
|
||||
if ($height === "auto") {
|
||||
$height = ($cb["h"] + $cb["y"]) - $bottom - $cb_y;
|
||||
}
|
||||
|
||||
// Set the y position of the first line in this block
|
||||
$line_box = $this->_frame->get_current_line_box();
|
||||
$line_box->y = $cb_y;
|
||||
$line_box->get_float_offsets();
|
||||
|
||||
// Set the containing blocks and reflow each child
|
||||
foreach ($this->_frame->get_children() as $child) {
|
||||
$child->set_containing_block($cb_x, $cb_y, $width, $height);
|
||||
$this->process_clear($child);
|
||||
$child->reflow($this->_frame);
|
||||
|
||||
// Check for a page break before the child
|
||||
$page->check_page_break($child);
|
||||
|
||||
// Don't add the child to the line if a page break has occurred
|
||||
// before it (possibly via a descendant), in which case it has been
|
||||
// reset, including its position
|
||||
if ($page->is_full() && $child->get_position("x") === null) {
|
||||
break;
|
||||
}
|
||||
|
||||
$this->process_float($child, $cb_x, $width);
|
||||
}
|
||||
|
||||
// Stop reflow if a page break has occurred before the frame, in which
|
||||
// case it has been reset, including its position
|
||||
if ($page->is_full() && $this->_frame->get_position("x") === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine our height
|
||||
[$height, $margin_top, $margin_bottom, $top, $bottom] = $this->_calculate_restricted_height();
|
||||
|
||||
$style->set_used("height", $height);
|
||||
$style->set_used("margin_top", $margin_top);
|
||||
$style->set_used("margin_bottom", $margin_bottom);
|
||||
$style->set_used("top", $top);
|
||||
$style->set_used("bottom", $bottom);
|
||||
|
||||
if ($this->_frame->is_absolute()) {
|
||||
if ($auto_top) {
|
||||
$this->_frame->move(0, $top);
|
||||
}
|
||||
if ($auto_margin_top) {
|
||||
$this->_frame->move(0, $margin_top, true);
|
||||
}
|
||||
}
|
||||
|
||||
$this->_text_align();
|
||||
$this->vertical_align();
|
||||
|
||||
// Handle relative positioning
|
||||
foreach ($this->_frame->get_children() as $child) {
|
||||
$this->position_relative($child);
|
||||
}
|
||||
|
||||
if ($block && $this->_frame->is_in_flow()) {
|
||||
$block->add_frame_to_line($this->_frame);
|
||||
|
||||
if ($this->_frame->is_block_level()) {
|
||||
$block->add_line();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function get_min_max_content_width(): array
|
||||
{
|
||||
// TODO: While the containing block is not set yet on the frame, it can
|
||||
// already be determined in some cases due to fixed dimensions on the
|
||||
// ancestor forming the containing block. In such cases, percentage
|
||||
// values could be resolved here
|
||||
$style = $this->_frame->get_style();
|
||||
$width = $style->width;
|
||||
$fixed_width = $width !== "auto" && !Helpers::is_percent($width);
|
||||
|
||||
// If the frame has a specified width, then we don't need to check
|
||||
// its children
|
||||
if ($fixed_width) {
|
||||
$min = (float) $style->length_in_pt($width, 0);
|
||||
$max = $min;
|
||||
} else {
|
||||
[$min, $max] = $this->get_min_max_child_width();
|
||||
}
|
||||
|
||||
// Handle min/max width style properties
|
||||
$min_width = $this->resolve_min_width(null);
|
||||
$max_width = $this->resolve_max_width(null);
|
||||
$min = Helpers::clamp($min, $min_width, $max_width);
|
||||
$max = Helpers::clamp($max, $min_width, $max_width);
|
||||
|
||||
return [$min, $max];
|
||||
}
|
||||
}
|
||||
213
system/vendor/pancakeapp/dompdf/src/FrameReflower/Image.php
vendored
Executable file
213
system/vendor/pancakeapp/dompdf/src/FrameReflower/Image.php
vendored
Executable file
@@ -0,0 +1,213 @@
|
||||
<?php
|
||||
/**
|
||||
* @package dompdf
|
||||
* @link https://github.com/dompdf/dompdf
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
*/
|
||||
namespace Dompdf\FrameReflower;
|
||||
|
||||
use Dompdf\Helpers;
|
||||
use Dompdf\FrameDecorator\Block as BlockFrameDecorator;
|
||||
use Dompdf\FrameDecorator\Image as ImageFrameDecorator;
|
||||
|
||||
/**
|
||||
* Image reflower class
|
||||
*
|
||||
* @package dompdf
|
||||
*/
|
||||
class Image extends AbstractFrameReflower
|
||||
{
|
||||
|
||||
/**
|
||||
* Image constructor.
|
||||
* @param ImageFrameDecorator $frame
|
||||
*/
|
||||
function __construct(ImageFrameDecorator $frame)
|
||||
{
|
||||
parent::__construct($frame);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param BlockFrameDecorator|null $block
|
||||
*/
|
||||
function reflow(BlockFrameDecorator $block = null)
|
||||
{
|
||||
$this->determine_absolute_containing_block();
|
||||
|
||||
// Counters and generated content
|
||||
$this->_set_content();
|
||||
|
||||
//FLOAT
|
||||
//$frame = $this->_frame;
|
||||
//$page = $frame->get_root();
|
||||
|
||||
//if ($frame->get_style()->float !== "none" ) {
|
||||
// $page->add_floating_frame($this);
|
||||
//}
|
||||
|
||||
$this->resolve_dimensions();
|
||||
$this->resolve_margins();
|
||||
|
||||
$frame = $this->_frame;
|
||||
$frame->position();
|
||||
|
||||
if ($block && $frame->is_in_flow()) {
|
||||
$block->add_frame_to_line($frame);
|
||||
}
|
||||
}
|
||||
|
||||
public function get_min_max_content_width(): array
|
||||
{
|
||||
// TODO: While the containing block is not set yet on the frame, it can
|
||||
// already be determined in some cases due to fixed dimensions on the
|
||||
// ancestor forming the containing block. In such cases, percentage
|
||||
// values could be resolved here
|
||||
$style = $this->_frame->get_style();
|
||||
|
||||
[$width] = $this->calculate_size(null, null);
|
||||
$min_width = $this->resolve_min_width(null);
|
||||
$percent_width = Helpers::is_percent($style->width)
|
||||
|| Helpers::is_percent($style->max_width)
|
||||
|| ($style->width === "auto"
|
||||
&& (Helpers::is_percent($style->height) || Helpers::is_percent($style->max_height)));
|
||||
|
||||
// Use the specified min width as minimum when width or max width depend
|
||||
// on the containing block and cannot be resolved yet. This mimics
|
||||
// browser behavior
|
||||
$min = $percent_width ? $min_width : $width;
|
||||
$max = $width;
|
||||
|
||||
return [$min, $max];
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate width and height, accounting for min/max constraints.
|
||||
*
|
||||
* * https://www.w3.org/TR/CSS21/visudet.html#inline-replaced-width
|
||||
* * https://www.w3.org/TR/CSS21/visudet.html#inline-replaced-height
|
||||
* * https://www.w3.org/TR/CSS21/visudet.html#min-max-widths
|
||||
* * https://www.w3.org/TR/CSS21/visudet.html#min-max-heights
|
||||
*
|
||||
* @param float|null $cbw Width of the containing block.
|
||||
* @param float|null $cbh Height of the containing block.
|
||||
*
|
||||
* @return float[]
|
||||
*/
|
||||
protected function calculate_size(?float $cbw, ?float $cbh): array
|
||||
{
|
||||
/** @var ImageFrameDecorator */
|
||||
$frame = $this->_frame;
|
||||
$style = $frame->get_style();
|
||||
|
||||
$computed_width = $style->width;
|
||||
$computed_height = $style->height;
|
||||
|
||||
$width = $cbw === null && Helpers::is_percent($computed_width)
|
||||
? "auto"
|
||||
: $style->length_in_pt($computed_width, $cbw ?? 0);
|
||||
$height = $cbh === null && Helpers::is_percent($computed_height)
|
||||
? "auto"
|
||||
: $style->length_in_pt($computed_height, $cbh ?? 0);
|
||||
$min_width = $this->resolve_min_width($cbw);
|
||||
$max_width = $this->resolve_max_width($cbw);
|
||||
$min_height = $this->resolve_min_height($cbh);
|
||||
$max_height = $this->resolve_max_height($cbh);
|
||||
|
||||
if ($width === "auto" && $height === "auto") {
|
||||
// Use intrinsic dimensions, resampled to pt
|
||||
[$img_width, $img_height] = $frame->get_intrinsic_dimensions();
|
||||
$w = $frame->resample($img_width);
|
||||
$h = $frame->resample($img_height);
|
||||
|
||||
// Resolve min/max constraints according to the constraint-violation
|
||||
// table in https://www.w3.org/TR/CSS21/visudet.html#min-max-widths
|
||||
$max_width = max($min_width, $max_width);
|
||||
$max_height = max($min_height, $max_height);
|
||||
|
||||
if (($w > $max_width && $h <= $max_height)
|
||||
|| ($w > $max_width && $h > $max_height && $max_width / $w <= $max_height / $h)
|
||||
|| ($w < $min_width && $h > $min_height)
|
||||
|| ($w < $min_width && $h < $min_height && $min_width / $w > $min_height / $h)
|
||||
) {
|
||||
$width = Helpers::clamp($w, $min_width, $max_width);
|
||||
$height = $width * ($img_height / $img_width);
|
||||
$height = Helpers::clamp($height, $min_height, $max_height);
|
||||
} else {
|
||||
$height = Helpers::clamp($h, $min_height, $max_height);
|
||||
$width = $height * ($img_width / $img_height);
|
||||
$width = Helpers::clamp($width, $min_width, $max_width);
|
||||
}
|
||||
} elseif ($height === "auto") {
|
||||
// Width is fixed, scale height according to aspect ratio
|
||||
[$img_width, $img_height] = $frame->get_intrinsic_dimensions();
|
||||
$width = Helpers::clamp((float) $width, $min_width, $max_width);
|
||||
$height = $width * ($img_height / $img_width);
|
||||
$height = Helpers::clamp($height, $min_height, $max_height);
|
||||
} elseif ($width === "auto") {
|
||||
// Height is fixed, scale width according to aspect ratio
|
||||
[$img_width, $img_height] = $frame->get_intrinsic_dimensions();
|
||||
$height = Helpers::clamp((float) $height, $min_height, $max_height);
|
||||
$width = $height * ($img_width / $img_height);
|
||||
$width = Helpers::clamp($width, $min_width, $max_width);
|
||||
} else {
|
||||
// Width and height are fixed
|
||||
$width = Helpers::clamp((float) $width, $min_width, $max_width);
|
||||
$height = Helpers::clamp((float) $height, $min_height, $max_height);
|
||||
}
|
||||
|
||||
return [$width, $height];
|
||||
}
|
||||
|
||||
protected function resolve_dimensions(): void
|
||||
{
|
||||
/** @var ImageFrameDecorator */
|
||||
$frame = $this->_frame;
|
||||
$style = $frame->get_style();
|
||||
|
||||
$debug_png = $this->get_dompdf()->getOptions()->getDebugPng();
|
||||
|
||||
if ($debug_png) {
|
||||
[$img_width, $img_height] = $frame->get_intrinsic_dimensions();
|
||||
print "resolve_dimensions() " .
|
||||
$frame->get_style()->width . " " .
|
||||
$frame->get_style()->height . ";" .
|
||||
$frame->get_parent()->get_style()->width . " " .
|
||||
$frame->get_parent()->get_style()->height . ";" .
|
||||
$frame->get_parent()->get_parent()->get_style()->width . " " .
|
||||
$frame->get_parent()->get_parent()->get_style()->height . ";" .
|
||||
$img_width . " " .
|
||||
$img_height . "|";
|
||||
}
|
||||
|
||||
[, , $cbw, $cbh] = $frame->get_containing_block();
|
||||
[$width, $height] = $this->calculate_size($cbw, $cbh);
|
||||
|
||||
if ($debug_png) {
|
||||
print $width . " " . $height . ";";
|
||||
}
|
||||
|
||||
$style->set_used("width", $width);
|
||||
$style->set_used("height", $height);
|
||||
}
|
||||
|
||||
protected function resolve_margins(): void
|
||||
{
|
||||
// Only handle the inline case for now
|
||||
// https://www.w3.org/TR/CSS21/visudet.html#inline-replaced-width
|
||||
// https://www.w3.org/TR/CSS21/visudet.html#inline-replaced-height
|
||||
$style = $this->_frame->get_style();
|
||||
|
||||
if ($style->margin_left === "auto") {
|
||||
$style->set_used("margin_left", 0.0);
|
||||
}
|
||||
if ($style->margin_right === "auto") {
|
||||
$style->set_used("margin_right", 0.0);
|
||||
}
|
||||
if ($style->margin_top === "auto") {
|
||||
$style->set_used("margin_top", 0.0);
|
||||
}
|
||||
if ($style->margin_bottom === "auto") {
|
||||
$style->set_used("margin_bottom", 0.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
188
system/vendor/pancakeapp/dompdf/src/FrameReflower/Inline.php
vendored
Executable file
188
system/vendor/pancakeapp/dompdf/src/FrameReflower/Inline.php
vendored
Executable file
@@ -0,0 +1,188 @@
|
||||
<?php
|
||||
/**
|
||||
* @package dompdf
|
||||
* @link https://github.com/dompdf/dompdf
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
*/
|
||||
namespace Dompdf\FrameReflower;
|
||||
|
||||
use Dompdf\FrameDecorator\Block as BlockFrameDecorator;
|
||||
use Dompdf\FrameDecorator\Inline as InlineFrameDecorator;
|
||||
use Dompdf\FrameDecorator\Text as TextFrameDecorator;
|
||||
|
||||
/**
|
||||
* Reflows inline frames
|
||||
*
|
||||
* @package dompdf
|
||||
*/
|
||||
class Inline extends AbstractFrameReflower
|
||||
{
|
||||
/**
|
||||
* Inline constructor.
|
||||
* @param InlineFrameDecorator $frame
|
||||
*/
|
||||
function __construct(InlineFrameDecorator $frame)
|
||||
{
|
||||
parent::__construct($frame);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle reflow of empty inline frames.
|
||||
*
|
||||
* Regular inline frames are positioned together with their text (or inline)
|
||||
* children after child reflow. Empty inline frames have no children that
|
||||
* could determine the positioning, so they need to be handled separately.
|
||||
*
|
||||
* @param BlockFrameDecorator $block
|
||||
*/
|
||||
protected function reflow_empty(BlockFrameDecorator $block): void
|
||||
{
|
||||
/** @var InlineFrameDecorator */
|
||||
$frame = $this->_frame;
|
||||
$style = $frame->get_style();
|
||||
|
||||
// Resolve width, so the margin width can be checked
|
||||
$style->set_used("width", 0.0);
|
||||
|
||||
$cb = $frame->get_containing_block();
|
||||
$line = $block->get_current_line_box();
|
||||
$width = $frame->get_margin_width();
|
||||
|
||||
if ($width > ($cb["w"] - $line->left - $line->w - $line->right)) {
|
||||
$block->add_line();
|
||||
|
||||
// Find the appropriate inline ancestor to split
|
||||
$child = $frame;
|
||||
$p = $child->get_parent();
|
||||
while ($p instanceof InlineFrameDecorator && !$child->get_prev_sibling()) {
|
||||
$child = $p;
|
||||
$p = $p->get_parent();
|
||||
}
|
||||
|
||||
if ($p instanceof InlineFrameDecorator) {
|
||||
// Split parent and stop current reflow. Reflow continues
|
||||
// via child-reflow loop of split parent
|
||||
$p->split($child);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$frame->position();
|
||||
$block->add_frame_to_line($frame);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param BlockFrameDecorator|null $block
|
||||
*/
|
||||
function reflow(BlockFrameDecorator $block = null)
|
||||
{
|
||||
/** @var InlineFrameDecorator */
|
||||
$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;
|
||||
}
|
||||
|
||||
// Counters and generated content
|
||||
$this->_set_content();
|
||||
|
||||
$style = $frame->get_style();
|
||||
|
||||
// Resolve auto margins
|
||||
// https://www.w3.org/TR/CSS21/visudet.html#inline-width
|
||||
// https://www.w3.org/TR/CSS21/visudet.html#inline-non-replaced
|
||||
if ($style->margin_left === "auto") {
|
||||
$style->set_used("margin_left", 0.0);
|
||||
}
|
||||
if ($style->margin_right === "auto") {
|
||||
$style->set_used("margin_right", 0.0);
|
||||
}
|
||||
if ($style->margin_top === "auto") {
|
||||
$style->set_used("margin_top", 0.0);
|
||||
}
|
||||
if ($style->margin_bottom === "auto") {
|
||||
$style->set_used("margin_bottom", 0.0);
|
||||
}
|
||||
|
||||
// Handle line breaks
|
||||
if ($frame->get_node()->nodeName === "br") {
|
||||
if ($block) {
|
||||
$line = $block->get_current_line_box();
|
||||
$frame->set_containing_line($line);
|
||||
$block->maximize_line_height($frame->get_margin_height(), $frame);
|
||||
$block->add_line(true);
|
||||
|
||||
$next = $frame->get_next_sibling();
|
||||
$p = $frame->get_parent();
|
||||
|
||||
if ($next && $p instanceof InlineFrameDecorator) {
|
||||
$p->split($next);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle empty inline frames
|
||||
if (!$frame->get_first_child()) {
|
||||
if ($block) {
|
||||
$this->reflow_empty($block);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Add our margin, padding & border to the first and last children
|
||||
if (($f = $frame->get_first_child()) && $f instanceof TextFrameDecorator) {
|
||||
$f_style = $f->get_style();
|
||||
$f_style->margin_left = $style->margin_left;
|
||||
$f_style->padding_left = $style->padding_left;
|
||||
$f_style->border_left_width = $style->border_left_width;
|
||||
$f_style->border_left_style = $style->border_left_style;
|
||||
$f_style->border_left_color = $style->border_left_color;
|
||||
}
|
||||
|
||||
if (($l = $frame->get_last_child()) && $l instanceof TextFrameDecorator) {
|
||||
$l_style = $l->get_style();
|
||||
$l_style->margin_right = $style->margin_right;
|
||||
$l_style->padding_right = $style->padding_right;
|
||||
$l_style->border_right_width = $style->border_right_width;
|
||||
$l_style->border_right_style = $style->border_right_style;
|
||||
$l_style->border_right_color = $style->border_right_color;
|
||||
}
|
||||
|
||||
$cb = $frame->get_containing_block();
|
||||
|
||||
// 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);
|
||||
|
||||
// Stop reflow if the frame has been reset by a line or page break
|
||||
// due to child reflow
|
||||
if (!$frame->content_set) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$frame->get_first_child()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Assume the position of the first child
|
||||
[$x, $y] = $frame->get_first_child()->get_position();
|
||||
$frame->set_position($x, $y);
|
||||
|
||||
// Handle relative positioning
|
||||
foreach ($frame->get_children() as $child) {
|
||||
$this->position_relative($child);
|
||||
}
|
||||
|
||||
if ($block) {
|
||||
$block->add_frame_to_line($frame);
|
||||
}
|
||||
}
|
||||
}
|
||||
51
system/vendor/pancakeapp/dompdf/src/FrameReflower/ListBullet.php
vendored
Executable file
51
system/vendor/pancakeapp/dompdf/src/FrameReflower/ListBullet.php
vendored
Executable file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
/**
|
||||
* @package dompdf
|
||||
* @link https://github.com/dompdf/dompdf
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
*/
|
||||
namespace Dompdf\FrameReflower;
|
||||
|
||||
use Dompdf\FrameDecorator\Block as BlockFrameDecorator;
|
||||
use Dompdf\FrameDecorator\ListBullet as ListBulletFrameDecorator;
|
||||
|
||||
/**
|
||||
* Reflows list bullets
|
||||
*
|
||||
* @package dompdf
|
||||
*/
|
||||
class ListBullet extends AbstractFrameReflower
|
||||
{
|
||||
|
||||
/**
|
||||
* ListBullet constructor.
|
||||
* @param ListBulletFrameDecorator $frame
|
||||
*/
|
||||
function __construct(ListBulletFrameDecorator $frame)
|
||||
{
|
||||
parent::__construct($frame);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param BlockFrameDecorator|null $block
|
||||
*/
|
||||
function reflow(BlockFrameDecorator $block = null)
|
||||
{
|
||||
if ($block === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var ListBulletFrameDecorator */
|
||||
$frame = $this->_frame;
|
||||
$style = $frame->get_style();
|
||||
|
||||
$style->set_used("width", $frame->get_width());
|
||||
$frame->position();
|
||||
|
||||
if ($style->list_style_position === "inside") {
|
||||
$block->add_frame_to_line($frame);
|
||||
} else {
|
||||
$block->add_dangling_marker($frame);
|
||||
}
|
||||
}
|
||||
}
|
||||
37
system/vendor/pancakeapp/dompdf/src/FrameReflower/NullFrameReflower.php
vendored
Executable file
37
system/vendor/pancakeapp/dompdf/src/FrameReflower/NullFrameReflower.php
vendored
Executable file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
/**
|
||||
* @package dompdf
|
||||
* @link https://github.com/dompdf/dompdf
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
*/
|
||||
namespace Dompdf\FrameReflower;
|
||||
|
||||
use Dompdf\Frame;
|
||||
use Dompdf\FrameDecorator\Block as BlockFrameDecorator;
|
||||
|
||||
/**
|
||||
* Dummy reflower
|
||||
*
|
||||
* @package dompdf
|
||||
*/
|
||||
class NullFrameReflower extends AbstractFrameReflower
|
||||
{
|
||||
|
||||
/**
|
||||
* NullFrameReflower constructor.
|
||||
* @param Frame $frame
|
||||
*/
|
||||
function __construct(Frame $frame)
|
||||
{
|
||||
parent::__construct($frame);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param BlockFrameDecorator|null $block
|
||||
*/
|
||||
function reflow(BlockFrameDecorator $block = null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
199
system/vendor/pancakeapp/dompdf/src/FrameReflower/Page.php
vendored
Executable file
199
system/vendor/pancakeapp/dompdf/src/FrameReflower/Page.php
vendored
Executable file
@@ -0,0 +1,199 @@
|
||||
<?php
|
||||
/**
|
||||
* @package dompdf
|
||||
* @link https://github.com/dompdf/dompdf
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
*/
|
||||
namespace Dompdf\FrameReflower;
|
||||
|
||||
use Dompdf\Frame;
|
||||
use Dompdf\FrameDecorator\Block as BlockFrameDecorator;
|
||||
use Dompdf\FrameDecorator\Page as PageFrameDecorator;
|
||||
|
||||
/**
|
||||
* Reflows pages
|
||||
*
|
||||
* @package dompdf
|
||||
*/
|
||||
class Page extends AbstractFrameReflower
|
||||
{
|
||||
|
||||
/**
|
||||
* Cache of the callbacks array
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $_callbacks;
|
||||
|
||||
/**
|
||||
* Cache of the canvas
|
||||
*
|
||||
* @var \Dompdf\Canvas
|
||||
*/
|
||||
private $_canvas;
|
||||
|
||||
/**
|
||||
* Page constructor.
|
||||
* @param PageFrameDecorator $frame
|
||||
*/
|
||||
function __construct(PageFrameDecorator $frame)
|
||||
{
|
||||
parent::__construct($frame);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param PageFrameDecorator $frame
|
||||
* @param int $page_number
|
||||
*/
|
||||
function apply_page_style(Frame $frame, $page_number)
|
||||
{
|
||||
$style = $frame->get_style();
|
||||
$page_styles = $style->get_stylesheet()->get_page_styles();
|
||||
|
||||
// http://www.w3.org/TR/CSS21/page.html#page-selectors
|
||||
if (count($page_styles) > 1) {
|
||||
$odd = $page_number % 2 == 1;
|
||||
$first = $page_number == 1;
|
||||
|
||||
$style = clone $page_styles["base"];
|
||||
|
||||
// FIXME RTL
|
||||
if ($odd && isset($page_styles[":right"])) {
|
||||
$style->merge($page_styles[":right"]);
|
||||
}
|
||||
|
||||
if ($odd && isset($page_styles[":odd"])) {
|
||||
$style->merge($page_styles[":odd"]);
|
||||
}
|
||||
|
||||
// FIXME RTL
|
||||
if (!$odd && isset($page_styles[":left"])) {
|
||||
$style->merge($page_styles[":left"]);
|
||||
}
|
||||
|
||||
if (!$odd && isset($page_styles[":even"])) {
|
||||
$style->merge($page_styles[":even"]);
|
||||
}
|
||||
|
||||
if ($first && isset($page_styles[":first"])) {
|
||||
$style->merge($page_styles[":first"]);
|
||||
}
|
||||
|
||||
$frame->set_style($style);
|
||||
}
|
||||
|
||||
$frame->calculate_bottom_page_edge();
|
||||
}
|
||||
|
||||
/**
|
||||
* Paged layout:
|
||||
* http://www.w3.org/TR/CSS21/page.html
|
||||
*
|
||||
* @param BlockFrameDecorator|null $block
|
||||
*/
|
||||
function reflow(BlockFrameDecorator $block = null)
|
||||
{
|
||||
/** @var PageFrameDecorator $frame */
|
||||
$frame = $this->_frame;
|
||||
$child = $frame->get_first_child();
|
||||
$fixed_children = [];
|
||||
$prev_child = null;
|
||||
$current_page = 0;
|
||||
|
||||
while ($child) {
|
||||
$this->apply_page_style($frame, $current_page + 1);
|
||||
|
||||
$style = $frame->get_style();
|
||||
|
||||
// Pages are only concerned with margins
|
||||
$cb = $frame->get_containing_block();
|
||||
$left = (float)$style->length_in_pt($style->margin_left, $cb["w"]);
|
||||
$right = (float)$style->length_in_pt($style->margin_right, $cb["w"]);
|
||||
$top = (float)$style->length_in_pt($style->margin_top, $cb["h"]);
|
||||
$bottom = (float)$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;
|
||||
|
||||
// Only if it's the first page, we save the nodes with a fixed position
|
||||
if ($current_page == 0) {
|
||||
foreach ($child->get_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);
|
||||
|
||||
// Render the page
|
||||
$frame->get_renderer()->render($child);
|
||||
|
||||
// Check for end render callback
|
||||
$this->_check_callbacks("end_page_render", $child);
|
||||
|
||||
if ($next_child) {
|
||||
$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(string $event, Frame $frame): void
|
||||
{
|
||||
if (!isset($this->_callbacks)) {
|
||||
$dompdf = $this->get_dompdf();
|
||||
$this->_callbacks = $dompdf->getCallbacks();
|
||||
$this->_canvas = $dompdf->getCanvas();
|
||||
}
|
||||
|
||||
if (isset($this->_callbacks[$event])) {
|
||||
$fs = $this->_callbacks[$event];
|
||||
$canvas = $this->_canvas;
|
||||
$fontMetrics = $this->get_dompdf()->getFontMetrics();
|
||||
|
||||
foreach ($fs as $f) {
|
||||
$f($frame, $canvas, $fontMetrics);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
523
system/vendor/pancakeapp/dompdf/src/FrameReflower/Table.php
vendored
Executable file
523
system/vendor/pancakeapp/dompdf/src/FrameReflower/Table.php
vendored
Executable file
@@ -0,0 +1,523 @@
|
||||
<?php
|
||||
/**
|
||||
* @package dompdf
|
||||
* @link https://github.com/dompdf/dompdf
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
*/
|
||||
namespace Dompdf\FrameReflower;
|
||||
|
||||
use Dompdf\FrameDecorator\Block as BlockFrameDecorator;
|
||||
use Dompdf\FrameDecorator\Table as TableFrameDecorator;
|
||||
use Dompdf\Helpers;
|
||||
|
||||
/**
|
||||
* Reflows tables
|
||||
*
|
||||
* @package dompdf
|
||||
*/
|
||||
class Table extends AbstractFrameReflower
|
||||
{
|
||||
/**
|
||||
* Frame for this reflower
|
||||
*
|
||||
* @var TableFrameDecorator
|
||||
*/
|
||||
protected $_frame;
|
||||
|
||||
/**
|
||||
* Cache of results between call to get_min_max_width and assign_widths
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $_state;
|
||||
|
||||
/**
|
||||
* Table constructor.
|
||||
* @param TableFrameDecorator $frame
|
||||
*/
|
||||
function __construct(TableFrameDecorator $frame)
|
||||
{
|
||||
$this->_state = null;
|
||||
parent::__construct($frame);
|
||||
}
|
||||
|
||||
/**
|
||||
* State is held here so it needs to be reset along with the decorator
|
||||
*/
|
||||
public function reset(): void
|
||||
{
|
||||
parent::reset();
|
||||
$this->_state = 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
|
||||
$delta = $this->_state["width_delta"];
|
||||
$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 (excluding borders and
|
||||
// padding)
|
||||
$cb = $this->_frame->get_containing_block();
|
||||
$columns =& $this->_frame->get_cellmap()->get_columns();
|
||||
|
||||
$width = $style->width;
|
||||
$min_table_width = $this->resolve_min_width($cb["w"]) - $delta;
|
||||
|
||||
if ($width !== "auto") {
|
||||
$preferred_width = (float) $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;
|
||||
} elseif ($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->set_used("width", $width);
|
||||
|
||||
$cellmap = $this->_frame->get_cellmap();
|
||||
|
||||
if ($cellmap->is_columns_locked()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the whole table fits on the page, then assign each column it's max width
|
||||
if ($width == $max_width) {
|
||||
foreach ($columns as $i => $col) {
|
||||
$cellmap->set_column_width($i, $col["max-width"]);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine leftover and assign it evenly to all columns
|
||||
if ($width > $min_width) {
|
||||
// We have three cases to deal with:
|
||||
//
|
||||
// 1. All columns are auto or absolute width. In this case we
|
||||
// distribute extra space across all auto columns weighted by the
|
||||
// difference between their max and min width, or by max width only
|
||||
// if the width of the table is larger than the max width for all
|
||||
// columns.
|
||||
//
|
||||
// 2. Only absolute widths have been specified, no auto columns. In
|
||||
// this case we distribute extra space across all columns weighted
|
||||
// by their absolute width.
|
||||
//
|
||||
// 3. Percentage widths have been specified. In this case we normalize
|
||||
// the percentage values and try to assign widths as fractions of
|
||||
// the table width. Absolute column widths are fully satisfied and
|
||||
// any remaining space is evenly distributed among all auto columns.
|
||||
|
||||
// Case 1:
|
||||
if ($percent_used == 0 && count($auto)) {
|
||||
foreach ($absolute as $i) {
|
||||
$w = $columns[$i]["min-width"];
|
||||
$cellmap->set_column_width($i, $w);
|
||||
}
|
||||
|
||||
if ($width < $max_width) {
|
||||
$increment = $width - $min_width;
|
||||
$table_delta = $max_width - $min_width;
|
||||
|
||||
foreach ($auto as $i) {
|
||||
$min = $columns[$i]["min-width"];
|
||||
$max = $columns[$i]["max-width"];
|
||||
$col_delta = $max - $min;
|
||||
$w = $min + $increment * ($col_delta / $table_delta);
|
||||
$cellmap->set_column_width($i, $w);
|
||||
}
|
||||
} else {
|
||||
$increment = $width - $max_width;
|
||||
$auto_max = $max_width - $absolute_used;
|
||||
|
||||
foreach ($auto as $i) {
|
||||
$max = $columns[$i]["max-width"];
|
||||
$f = $auto_max > 0 ? $max / $auto_max : 1 / count($auto);
|
||||
$w = $max + $increment * $f;
|
||||
$cellmap->set_column_width($i, $w);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Case 2:
|
||||
if ($percent_used == 0 && !count($auto)) {
|
||||
$increment = $width - $absolute_used;
|
||||
|
||||
foreach ($absolute as $i) {
|
||||
$abs = $columns[$i]["min-width"];
|
||||
$f = $absolute_used > 0 ? $abs / $absolute_used : 1 / count($absolute);
|
||||
$w = $abs + $increment * $f;
|
||||
$cellmap->set_column_width($i, $w);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Case 3:
|
||||
if ($percent_used > 0) {
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Account for the minimum space used by the unassigned auto
|
||||
// columns, by the columns with absolute widths, and the
|
||||
// percentage columns following the current one
|
||||
$used_width = $auto_min + $absolute_used;
|
||||
|
||||
foreach ($absolute as $i) {
|
||||
$w = $columns[$i]["min-width"];
|
||||
$cellmap->set_column_width($i, $w);
|
||||
}
|
||||
|
||||
$percent_min = 0;
|
||||
|
||||
foreach ($percent as $i) {
|
||||
$percent_min += $columns[$i]["min-width"];
|
||||
}
|
||||
|
||||
// First-come, first served
|
||||
foreach ($percent as $i) {
|
||||
$min = $columns[$i]["min-width"];
|
||||
$percent_min -= $min;
|
||||
$slack = $width - $used_width - $percent_min;
|
||||
|
||||
$columns[$i]["percent"] *= $scale;
|
||||
$w = min($columns[$i]["percent"] * $width / 100, $slack);
|
||||
|
||||
if ($w < $min) {
|
||||
$w = $min;
|
||||
}
|
||||
|
||||
$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) {
|
||||
$w = $columns[$i]["min-width"] + $increment;
|
||||
$cellmap->set_column_width($i, $w);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// We are over-constrained:
|
||||
// Each column gets its minimum width
|
||||
foreach ($columns as $i => $col) {
|
||||
$cellmap->set_column_width($i, $col["min-width"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the frame's height based on min/max height
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
protected function _calculate_height()
|
||||
{
|
||||
$frame = $this->_frame;
|
||||
$style = $frame->get_style();
|
||||
$cb = $frame->get_containing_block();
|
||||
|
||||
$height = $style->length_in_pt($style->height, $cb["h"]);
|
||||
|
||||
$cellmap = $frame->get_cellmap();
|
||||
$cellmap->assign_frame_heights();
|
||||
$rows = $cellmap->get_rows();
|
||||
|
||||
// Determine our content height
|
||||
$content_height = 0.0;
|
||||
foreach ($rows as $r) {
|
||||
$content_height += $r["height"];
|
||||
}
|
||||
|
||||
if ($height === "auto") {
|
||||
$height = $content_height;
|
||||
}
|
||||
|
||||
// Handle min/max height
|
||||
// https://www.w3.org/TR/CSS21/visudet.html#min-max-heights
|
||||
$min_height = $this->resolve_min_height($cb["h"]);
|
||||
$max_height = $this->resolve_max_height($cb["h"]);
|
||||
$height = Helpers::clamp($height, $min_height, $max_height);
|
||||
|
||||
// Use the content height or the height value, whichever is greater
|
||||
if ($height <= $content_height) {
|
||||
$height = $content_height;
|
||||
} else {
|
||||
// FIXME: Borders and row positions are not properly updated by this
|
||||
// $cellmap->set_frame_heights($height, $content_height);
|
||||
}
|
||||
|
||||
return $height;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param BlockFrameDecorator|null $block
|
||||
*/
|
||||
function reflow(BlockFrameDecorator $block = null)
|
||||
{
|
||||
/** @var TableFrameDecorator */
|
||||
$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();
|
||||
|
||||
$this->determine_absolute_containing_block();
|
||||
|
||||
// Counters and generated content
|
||||
$this->_set_content();
|
||||
|
||||
// Collapse vertical margins, if required
|
||||
$this->_collapse_margins();
|
||||
|
||||
// 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") {
|
||||
[$h, $v] = $style->border_spacing;
|
||||
$v = $v / 2;
|
||||
$h = $h / 2;
|
||||
|
||||
$style->set_used("padding_left", (float)$style->length_in_pt($style->padding_left, $cb["w"]) + $h);
|
||||
$style->set_used("padding_right", (float)$style->length_in_pt($style->padding_right, $cb["w"]) + $h);
|
||||
$style->set_used("padding_top", (float)$style->length_in_pt($style->padding_top, $cb["w"]) + $v);
|
||||
$style->set_used("padding_bottom", (float)$style->length_in_pt($style->padding_bottom, $cb["w"]) + $v);
|
||||
}
|
||||
|
||||
$this->_assign_widths();
|
||||
|
||||
// Adjust left & right margins, if they are auto
|
||||
$delta = $this->_state["width_delta"];
|
||||
$width = $style->width;
|
||||
$left = $style->length_in_pt($style->margin_left, $cb["w"]);
|
||||
$right = $style->length_in_pt($style->margin_right, $cb["w"]);
|
||||
|
||||
$diff = (float) $cb["w"] - (float) $width - $delta;
|
||||
|
||||
if ($left === "auto" && $right === "auto") {
|
||||
if ($diff < 0) {
|
||||
$left = 0;
|
||||
$right = $diff;
|
||||
} else {
|
||||
$left = $right = $diff / 2;
|
||||
}
|
||||
} else {
|
||||
if ($left === "auto") {
|
||||
$left = max($diff - $right, 0);
|
||||
}
|
||||
if ($right === "auto") {
|
||||
$right = max($diff - $left, 0);
|
||||
}
|
||||
}
|
||||
|
||||
$style->set_used("margin_left", $left);
|
||||
$style->set_used("margin_right", $right);
|
||||
|
||||
$frame->position();
|
||||
[$x, $y] = $frame->get_position();
|
||||
|
||||
// Determine the content edge
|
||||
$offset_x = (float)$left + (float)$style->length_in_pt([
|
||||
$style->padding_left,
|
||||
$style->border_left_width
|
||||
], $cb["w"]);
|
||||
$offset_y = (float)$style->length_in_pt([
|
||||
$style->margin_top,
|
||||
$style->border_top_width,
|
||||
$style->padding_top
|
||||
], $cb["w"]);
|
||||
$content_x = $x + $offset_x;
|
||||
$content_y = $y + $offset_y;
|
||||
|
||||
if (isset($cb["h"])) {
|
||||
$h = $cb["h"];
|
||||
} else {
|
||||
$h = null;
|
||||
}
|
||||
|
||||
$cellmap = $frame->get_cellmap();
|
||||
$col =& $cellmap->get_column(0);
|
||||
$col["x"] = $offset_x;
|
||||
|
||||
$row =& $cellmap->get_row(0);
|
||||
$row["y"] = $offset_y;
|
||||
|
||||
$cellmap->assign_x_positions();
|
||||
|
||||
// Set the containing block of each child & reflow
|
||||
foreach ($frame->get_children() as $child) {
|
||||
$child->set_containing_block($content_x, $content_y, $width, $h);
|
||||
$child->reflow();
|
||||
|
||||
if (!$page->in_nested_table()) {
|
||||
// Check if a split has occurred
|
||||
$page->check_page_break($child);
|
||||
|
||||
if ($page->is_full()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Stop reflow if a page break has occurred before the frame, in which
|
||||
// case it has been reset, including its position
|
||||
if ($page->is_full() && $frame->get_position("x") === null) {
|
||||
$page->table_reflow_end();
|
||||
return;
|
||||
}
|
||||
|
||||
// Assign heights to our cells:
|
||||
$style->set_used("height", $this->_calculate_height());
|
||||
|
||||
$page->table_reflow_end();
|
||||
|
||||
if ($block && $frame->is_in_flow()) {
|
||||
$block->add_frame_to_line($frame);
|
||||
|
||||
if ($frame->is_block_level()) {
|
||||
$block->add_line();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function get_min_max_width(): array
|
||||
{
|
||||
if (!is_null($this->_min_max_cache)) {
|
||||
return $this->_min_max_cache;
|
||||
}
|
||||
|
||||
$style = $this->_frame->get_style();
|
||||
$cellmap = $this->_frame->get_cellmap();
|
||||
|
||||
$this->_frame->normalize();
|
||||
|
||||
// Add the cells to the cellmap (this will calculate column widths as
|
||||
// frames are added)
|
||||
$cellmap->add_frame($this->_frame);
|
||||
|
||||
// Find the min/max width of the table and sort the columns into
|
||||
// absolute/percent/auto arrays
|
||||
$this->_state = [];
|
||||
$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"] = [];
|
||||
$this->_state["percent"] = [];
|
||||
$this->_state["auto"] = [];
|
||||
|
||||
$columns =& $cellmap->get_columns();
|
||||
foreach ($columns as $i => $col) {
|
||||
$this->_state["min_width"] += $col["min-width"];
|
||||
$this->_state["max_width"] += $col["max-width"];
|
||||
|
||||
if ($col["absolute"] > 0) {
|
||||
$this->_state["absolute"][] = $i;
|
||||
$this->_state["absolute_used"] += $col["min-width"];
|
||||
} elseif ($col["percent"] > 0) {
|
||||
$this->_state["percent"][] = $i;
|
||||
$this->_state["percent_used"] += $col["percent"];
|
||||
} else {
|
||||
$this->_state["auto"][] = $i;
|
||||
$this->_state["auto_min"] += $col["min-width"];
|
||||
}
|
||||
}
|
||||
|
||||
// Account for margins, borders, padding, and border spacing
|
||||
$cb_w = $this->_frame->get_containing_block("w");
|
||||
$lm = (float) $style->length_in_pt($style->margin_left, $cb_w);
|
||||
$rm = (float) $style->length_in_pt($style->margin_right, $cb_w);
|
||||
|
||||
$dims = [
|
||||
$style->border_left_width,
|
||||
$style->border_right_width,
|
||||
$style->padding_left,
|
||||
$style->padding_right
|
||||
];
|
||||
|
||||
if ($style->border_collapse !== "collapse") {
|
||||
list($dims[]) = $style->border_spacing;
|
||||
}
|
||||
|
||||
$delta = (float) $style->length_in_pt($dims, $cb_w);
|
||||
|
||||
$this->_state["width_delta"] = $delta;
|
||||
|
||||
$min_width = $this->_state["min_width"] + $delta + $lm + $rm;
|
||||
$max_width = $this->_state["max_width"] + $delta + $lm + $rm;
|
||||
|
||||
return $this->_min_max_cache = [
|
||||
$min_width,
|
||||
$max_width,
|
||||
"min" => $min_width,
|
||||
"max" => $max_width
|
||||
];
|
||||
}
|
||||
}
|
||||
161
system/vendor/pancakeapp/dompdf/src/FrameReflower/TableCell.php
vendored
Executable file
161
system/vendor/pancakeapp/dompdf/src/FrameReflower/TableCell.php
vendored
Executable file
@@ -0,0 +1,161 @@
|
||||
<?php
|
||||
/**
|
||||
* @package dompdf
|
||||
* @link https://github.com/dompdf/dompdf
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
*/
|
||||
namespace Dompdf\FrameReflower;
|
||||
|
||||
use Dompdf\FrameDecorator\Block as BlockFrameDecorator;
|
||||
use Dompdf\FrameDecorator\Table as TableFrameDecorator;
|
||||
use Dompdf\Helpers;
|
||||
|
||||
/**
|
||||
* Reflows table cells
|
||||
*
|
||||
* @package dompdf
|
||||
*/
|
||||
class TableCell extends Block
|
||||
{
|
||||
/**
|
||||
* TableCell constructor.
|
||||
* @param BlockFrameDecorator $frame
|
||||
*/
|
||||
function __construct(BlockFrameDecorator $frame)
|
||||
{
|
||||
parent::__construct($frame);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param BlockFrameDecorator|null $block
|
||||
*/
|
||||
function reflow(BlockFrameDecorator $block = null)
|
||||
{
|
||||
// Counters and generated content
|
||||
$this->_set_content();
|
||||
|
||||
$style = $this->_frame->get_style();
|
||||
|
||||
$table = TableFrameDecorator::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 = (float)$style->length_in_pt([$style->margin_left,
|
||||
$style->padding_left,
|
||||
$style->border_left_width],
|
||||
$w);
|
||||
|
||||
$right_space = (float)$style->length_in_pt([$style->padding_right,
|
||||
$style->margin_right,
|
||||
$style->border_right_width],
|
||||
$w);
|
||||
|
||||
$top_space = (float)$style->length_in_pt([$style->margin_top,
|
||||
$style->padding_top,
|
||||
$style->border_top_width],
|
||||
$h);
|
||||
$bottom_space = (float)$style->length_in_pt([$style->margin_bottom,
|
||||
$style->padding_bottom,
|
||||
$style->border_bottom_width],
|
||||
$h);
|
||||
|
||||
$cb_w = $w - $left_space - $right_space;
|
||||
$style->set_used("width", $cb_w);
|
||||
|
||||
$content_x = $x + $left_space;
|
||||
$content_y = $line_y = $y + $top_space;
|
||||
|
||||
// Adjust the first line based on the text-indent property
|
||||
$indent = (float)$style->length_in_pt($style->text_indent, $w);
|
||||
$this->_frame->increase_line_width($indent);
|
||||
|
||||
$page = $this->_frame->get_root();
|
||||
|
||||
// Set the y position of the first line in the cell
|
||||
$line_box = $this->_frame->get_current_line_box();
|
||||
$line_box->y = $line_y;
|
||||
|
||||
// Set the containing blocks and reflow each child
|
||||
foreach ($this->_frame->get_children() as $child) {
|
||||
$child->set_containing_block($content_x, $content_y, $cb_w, $h);
|
||||
$this->process_clear($child);
|
||||
$child->reflow($this->_frame);
|
||||
$this->process_float($child, $content_x, $cb_w);
|
||||
|
||||
if ($page->is_full()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Determine our height
|
||||
$style_height = (float)$style->length_in_pt($style->height, $h);
|
||||
|
||||
/** @var FrameDecorator\TableCell */
|
||||
$frame = $this->_frame;
|
||||
|
||||
$frame->set_content_height($this->_calculate_content_height());
|
||||
|
||||
$height = max($style_height, (float)$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->set_used("height", $height);
|
||||
|
||||
$this->_text_align();
|
||||
$this->vertical_align();
|
||||
|
||||
// Handle relative positioning
|
||||
foreach ($this->_frame->get_children() as $child) {
|
||||
$this->position_relative($child);
|
||||
}
|
||||
}
|
||||
|
||||
public function get_min_max_content_width(): array
|
||||
{
|
||||
// Ignore percentage values for a specified width here, as they are
|
||||
// relative to the table width, which is not determined yet
|
||||
$style = $this->_frame->get_style();
|
||||
$width = $style->width;
|
||||
$fixed_width = $width !== "auto" && !Helpers::is_percent($width);
|
||||
|
||||
[$min, $max] = $this->get_min_max_child_width();
|
||||
|
||||
// For table cells: Use specified width if it is greater than the
|
||||
// minimum defined by the content
|
||||
if ($fixed_width) {
|
||||
$width = (float) $style->length_in_pt($width, 0);
|
||||
$min = max($width, $min);
|
||||
$max = $min;
|
||||
}
|
||||
|
||||
// Handle min/max width style properties
|
||||
$min_width = $this->resolve_min_width(null);
|
||||
$max_width = $this->resolve_max_width(null);
|
||||
$min = Helpers::clamp($min, $min_width, $max_width);
|
||||
$max = Helpers::clamp($max, $min_width, $max_width);
|
||||
|
||||
return [$min, $max];
|
||||
}
|
||||
}
|
||||
82
system/vendor/pancakeapp/dompdf/src/FrameReflower/TableRow.php
vendored
Executable file
82
system/vendor/pancakeapp/dompdf/src/FrameReflower/TableRow.php
vendored
Executable file
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
/**
|
||||
* @package dompdf
|
||||
* @link https://github.com/dompdf/dompdf
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
*/
|
||||
namespace Dompdf\FrameReflower;
|
||||
|
||||
use Dompdf\FrameDecorator\Block as BlockFrameDecorator;
|
||||
use Dompdf\FrameDecorator\Table as TableFrameDecorator;
|
||||
use Dompdf\FrameDecorator\TableRow as TableRowFrameDecorator;
|
||||
use Dompdf\Exception;
|
||||
|
||||
/**
|
||||
* Reflows table rows
|
||||
*
|
||||
* @package dompdf
|
||||
*/
|
||||
class TableRow extends AbstractFrameReflower
|
||||
{
|
||||
/**
|
||||
* TableRow constructor.
|
||||
* @param TableRowFrameDecorator $frame
|
||||
*/
|
||||
function __construct(TableRowFrameDecorator $frame)
|
||||
{
|
||||
parent::__construct($frame);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param BlockFrameDecorator|null $block
|
||||
*/
|
||||
function reflow(BlockFrameDecorator $block = null)
|
||||
{
|
||||
/** @var TableRowFrameDecorator */
|
||||
$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;
|
||||
}
|
||||
|
||||
// Counters and generated content
|
||||
$this->_set_content();
|
||||
|
||||
$this->_frame->position();
|
||||
$style = $this->_frame->get_style();
|
||||
$cb = $this->_frame->get_containing_block();
|
||||
|
||||
foreach ($this->_frame->get_children() as $child) {
|
||||
$child->set_containing_block($cb);
|
||||
$child->reflow();
|
||||
|
||||
if ($page->is_full()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($page->is_full()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$table = TableFrameDecorator::find_parent_table($this->_frame);
|
||||
$cellmap = $table->get_cellmap();
|
||||
$style->set_used("width", $cellmap->get_frame_width($this->_frame));
|
||||
$style->set_used("height", $cellmap->get_frame_height($this->_frame));
|
||||
|
||||
$this->_frame->set_position($cellmap->get_frame_position($this->_frame));
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
public function get_min_max_width(): array
|
||||
{
|
||||
throw new Exception("Min/max width is undefined for table rows");
|
||||
}
|
||||
}
|
||||
71
system/vendor/pancakeapp/dompdf/src/FrameReflower/TableRowGroup.php
vendored
Executable file
71
system/vendor/pancakeapp/dompdf/src/FrameReflower/TableRowGroup.php
vendored
Executable file
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
/**
|
||||
* @package dompdf
|
||||
* @link https://github.com/dompdf/dompdf
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
*/
|
||||
namespace Dompdf\FrameReflower;
|
||||
|
||||
use Dompdf\FrameDecorator\Block as BlockFrameDecorator;
|
||||
use Dompdf\FrameDecorator\Table as TableFrameDecorator;
|
||||
use Dompdf\FrameDecorator\TableRowGroup as TableRowGroupFrameDecorator;
|
||||
|
||||
/**
|
||||
* Reflows table row groups (e.g. tbody tags)
|
||||
*
|
||||
* @package dompdf
|
||||
*/
|
||||
class TableRowGroup extends AbstractFrameReflower
|
||||
{
|
||||
|
||||
/**
|
||||
* TableRowGroup constructor.
|
||||
* @param TableRowGroupFrameDecorator $frame
|
||||
*/
|
||||
function __construct(TableRowGroupFrameDecorator $frame)
|
||||
{
|
||||
parent::__construct($frame);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param BlockFrameDecorator|null $block
|
||||
*/
|
||||
function reflow(BlockFrameDecorator $block = null)
|
||||
{
|
||||
/** @var TableRowGroupFrameDecorator */
|
||||
$frame = $this->_frame;
|
||||
$page = $frame->get_root();
|
||||
|
||||
// Counters and generated content
|
||||
$this->_set_content();
|
||||
|
||||
$style = $frame->get_style();
|
||||
$cb = $frame->get_containing_block();
|
||||
|
||||
foreach ($frame->get_children() as $child) {
|
||||
$child->set_containing_block($cb["x"], $cb["y"], $cb["w"], $cb["h"]);
|
||||
$child->reflow();
|
||||
|
||||
// Check if a split has occurred
|
||||
$page->check_page_break($child);
|
||||
|
||||
if ($page->is_full()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$table = TableFrameDecorator::find_parent_table($frame);
|
||||
$cellmap = $table->get_cellmap();
|
||||
|
||||
// Stop reflow if a page break has occurred before the frame, in which
|
||||
// case it is not part of its parent table's cell map yet
|
||||
if ($page->is_full() && !$cellmap->frame_exists_in_cellmap($frame)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$style->set_used("width", $cellmap->get_frame_width($frame));
|
||||
$style->set_used("height", $cellmap->get_frame_height($frame));
|
||||
|
||||
$frame->set_position($cellmap->get_frame_position($frame));
|
||||
}
|
||||
}
|
||||
611
system/vendor/pancakeapp/dompdf/src/FrameReflower/Text.php
vendored
Executable file
611
system/vendor/pancakeapp/dompdf/src/FrameReflower/Text.php
vendored
Executable file
@@ -0,0 +1,611 @@
|
||||
<?php
|
||||
/**
|
||||
* @package dompdf
|
||||
* @link https://github.com/dompdf/dompdf
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
*/
|
||||
namespace Dompdf\FrameReflower;
|
||||
|
||||
use Dompdf\FrameDecorator\Block as BlockFrameDecorator;
|
||||
use Dompdf\FrameDecorator\Inline as InlineFrameDecorator;
|
||||
use Dompdf\FrameDecorator\Text as TextFrameDecorator;
|
||||
use Dompdf\FontMetrics;
|
||||
use Dompdf\Helpers;
|
||||
|
||||
/**
|
||||
* Reflows text frames.
|
||||
*
|
||||
* @package dompdf
|
||||
*/
|
||||
class Text extends AbstractFrameReflower
|
||||
{
|
||||
/**
|
||||
* PHP string representation of HTML entity <shy>
|
||||
*/
|
||||
const SOFT_HYPHEN = "\xC2\xAD";
|
||||
|
||||
/**
|
||||
* The regex splits on everything that's a separator (^\S double negative),
|
||||
* excluding the following non-breaking space characters:
|
||||
* * nbsp (\xA0)
|
||||
* * narrow nbsp (\x{202F})
|
||||
* * figure space (\x{2007})
|
||||
*/
|
||||
public static $_whitespace_pattern = '/([^\S\xA0\x{202F}\x{2007}]+)/u';
|
||||
|
||||
/**
|
||||
* The regex splits on everything that's a separator (^\S double negative)
|
||||
* plus dashes, excluding the following non-breaking space characters:
|
||||
* * nbsp (\xA0)
|
||||
* * narrow nbsp (\x{202F})
|
||||
* * figure space (\x{2007})
|
||||
*/
|
||||
public static $_wordbreak_pattern = '/([^\S\xA0\x{202F}\x{2007}\n]+|\R|\-+|\xAD+)/u';
|
||||
|
||||
/**
|
||||
* Frame for this reflower
|
||||
*
|
||||
* @var TextFrameDecorator
|
||||
*/
|
||||
protected $_frame;
|
||||
|
||||
/**
|
||||
* Saves trailing whitespace trimmed after a line break, so it can be
|
||||
* restored when needed.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected $trailingWs = null;
|
||||
|
||||
/**
|
||||
* @var FontMetrics
|
||||
*/
|
||||
private $fontMetrics;
|
||||
|
||||
/**
|
||||
* @param TextFrameDecorator $frame
|
||||
* @param FontMetrics $fontMetrics
|
||||
*/
|
||||
public function __construct(TextFrameDecorator $frame, FontMetrics $fontMetrics)
|
||||
{
|
||||
parent::__construct($frame);
|
||||
$this->setFontMetrics($fontMetrics);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply text transform and white-space collapse according to style.
|
||||
*
|
||||
* * http://www.w3.org/TR/CSS21/text.html#propdef-text-transform
|
||||
* * http://www.w3.org/TR/CSS21/text.html#propdef-white-space
|
||||
*
|
||||
* @param string $text
|
||||
* @return string
|
||||
*/
|
||||
protected function pre_process_text(string $text): string
|
||||
{
|
||||
$style = $this->_frame->get_style();
|
||||
|
||||
// Handle text transform
|
||||
switch ($style->text_transform) {
|
||||
case "capitalize":
|
||||
$text = Helpers::mb_ucwords($text);
|
||||
break;
|
||||
case "uppercase":
|
||||
$text = mb_convert_case($text, MB_CASE_UPPER);
|
||||
break;
|
||||
case "lowercase":
|
||||
$text = mb_convert_case($text, MB_CASE_LOWER);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Handle white-space collapse
|
||||
switch ($style->white_space) {
|
||||
default:
|
||||
case "normal":
|
||||
case "nowrap":
|
||||
$text = preg_replace(self::$_whitespace_pattern, " ", $text) ?? "";
|
||||
break;
|
||||
|
||||
case "pre-line":
|
||||
// Collapse white space except for line breaks
|
||||
$text = preg_replace('/([^\S\xA0\x{202F}\x{2007}\n]+)/u', " ", $text) ?? "";
|
||||
break;
|
||||
|
||||
case "pre":
|
||||
case "pre-wrap":
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
return $text;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $text
|
||||
* @param BlockFrameDecorator $block
|
||||
* @param bool $nowrap
|
||||
*
|
||||
* @return bool|int
|
||||
*/
|
||||
protected function line_break(string $text, BlockFrameDecorator $block, bool $nowrap = false)
|
||||
{
|
||||
$fontMetrics = $this->getFontMetrics();
|
||||
$frame = $this->_frame;
|
||||
$style = $frame->get_style();
|
||||
$font = $style->font_family;
|
||||
$size = $style->font_size;
|
||||
$word_spacing = $style->word_spacing;
|
||||
$letter_spacing = $style->letter_spacing;
|
||||
|
||||
// Determine the available width
|
||||
$current_line = $block->get_current_line_box();
|
||||
$line_width = $frame->get_containing_block("w");
|
||||
$current_line_width = $current_line->left + $current_line->w + $current_line->right;
|
||||
$available_width = $line_width - $current_line_width;
|
||||
|
||||
// Determine the frame width including margin, padding & border
|
||||
$visible_text = preg_replace('/\xAD/u', "", $text);
|
||||
$text_width = $fontMetrics->getTextWidth($visible_text, $font, $size, $word_spacing, $letter_spacing);
|
||||
$mbp_width = (float) $style->length_in_pt([
|
||||
$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;
|
||||
|
||||
if (Helpers::lengthLessOrEqual($frame_width, $available_width)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($nowrap) {
|
||||
return $current_line_width == 0 ? false : 0;
|
||||
}
|
||||
|
||||
// Split the text into words
|
||||
$words = preg_split(self::$_wordbreak_pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE);
|
||||
$wc = count($words);
|
||||
|
||||
// Determine the split point
|
||||
$width = 0.0;
|
||||
$str = "";
|
||||
|
||||
$space_width = $fontMetrics->getTextWidth(" ", $font, $size, $word_spacing, $letter_spacing);
|
||||
$shy_width = $fontMetrics->getTextWidth(self::SOFT_HYPHEN, $font, $size);
|
||||
|
||||
// @todo support <wbr>
|
||||
for ($i = 0; $i < $wc; $i += 2) {
|
||||
// Allow trailing white space to overflow. White space is always
|
||||
// collapsed to the standard space character currently, so only
|
||||
// handle that for now
|
||||
$sep = $words[$i + 1] ?? "";
|
||||
$word = $sep === " " ? $words[$i] : $words[$i] . $sep;
|
||||
$word_width = $fontMetrics->getTextWidth($word, $font, $size, $word_spacing, $letter_spacing);
|
||||
$used_width = $width + $word_width + $mbp_width;
|
||||
|
||||
if (Helpers::lengthGreater($used_width, $available_width)) {
|
||||
// If the previous split happened by soft hyphen, we have to
|
||||
// append its width again because the last hyphen of a line
|
||||
// won't be removed
|
||||
if (isset($words[$i - 1]) && self::SOFT_HYPHEN === $words[$i - 1]) {
|
||||
$width += $shy_width;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// If the word is splitted by soft hyphen, but no line break is needed
|
||||
// we have to reduce the width. But the str is not modified, otherwise
|
||||
// the wrong offset is calculated at the end of this method.
|
||||
if ($sep === self::SOFT_HYPHEN) {
|
||||
$width += $word_width - $shy_width;
|
||||
$str .= $word;
|
||||
} elseif ($sep === " ") {
|
||||
$width += $word_width + $space_width;
|
||||
$str .= $word . $sep;
|
||||
} else {
|
||||
$width += $word_width;
|
||||
$str .= $word;
|
||||
}
|
||||
}
|
||||
|
||||
// The first word has overflowed. Force it onto the line, or as many
|
||||
// characters as fit if breaking words is allowed
|
||||
if ($current_line_width == 0 && $width === 0.0) {
|
||||
if ($sep === " ") {
|
||||
$word .= $sep;
|
||||
}
|
||||
|
||||
// https://www.w3.org/TR/css-text-3/#overflow-wrap-property
|
||||
$wrap = $style->overflow_wrap;
|
||||
$break_word = $wrap === "anywhere" || $wrap === "break-word";
|
||||
|
||||
if ($break_word) {
|
||||
$s = "";
|
||||
|
||||
for ($j = 0; $j < mb_strlen($word); $j++) {
|
||||
$c = mb_substr($word, $j, 1);
|
||||
$w = $fontMetrics->getTextWidth($s . $c, $font, $size, $word_spacing, $letter_spacing);
|
||||
|
||||
if (Helpers::lengthGreater($w, $available_width)) {
|
||||
break;
|
||||
}
|
||||
|
||||
$s .= $c;
|
||||
}
|
||||
|
||||
// Always force the first character onto the line
|
||||
$str = $j === 0 ? $s . $c : $s;
|
||||
} else {
|
||||
$str = $word;
|
||||
}
|
||||
}
|
||||
|
||||
$offset = mb_strlen($str);
|
||||
return $offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $text
|
||||
* @return bool|int
|
||||
*/
|
||||
protected function newline_break(string $text)
|
||||
{
|
||||
if (($i = mb_strpos($text, "\n")) === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $i + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param BlockFrameDecorator $block
|
||||
* @return bool|null Whether to add a new line at the end. `null` if reflow
|
||||
* should be stopped.
|
||||
*/
|
||||
protected function layout_line(BlockFrameDecorator $block): ?bool
|
||||
{
|
||||
$frame = $this->_frame;
|
||||
$style = $frame->get_style();
|
||||
$current_line = $block->get_current_line_box();
|
||||
$text = $frame->get_text();
|
||||
|
||||
// Trim leading white space if this is the first text on the line
|
||||
if ($current_line->w === 0.0 && !$frame->is_pre()) {
|
||||
$text = ltrim($text, " ");
|
||||
}
|
||||
|
||||
if ($text === "") {
|
||||
$frame->set_text("");
|
||||
$style->set_used("width", 0.0);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Determine the next line break
|
||||
// http://www.w3.org/TR/CSS21/text.html#propdef-white-space
|
||||
$white_space = $style->white_space;
|
||||
$nowrap = $white_space === "nowrap" || $white_space === "pre";
|
||||
|
||||
switch ($white_space) {
|
||||
default:
|
||||
case "normal":
|
||||
case "nowrap":
|
||||
$split = $this->line_break($text, $block, $nowrap);
|
||||
$add_line = false;
|
||||
break;
|
||||
|
||||
case "pre":
|
||||
case "pre-line":
|
||||
case "pre-wrap":
|
||||
$hard_split = $this->newline_break($text);
|
||||
$first_line = $hard_split !== false
|
||||
? mb_substr($text, 0, $hard_split)
|
||||
: $text;
|
||||
$soft_split = $this->line_break($first_line, $block, $nowrap);
|
||||
|
||||
$split = $soft_split !== false ? $soft_split : $hard_split;
|
||||
$add_line = $hard_split !== false;
|
||||
break;
|
||||
}
|
||||
|
||||
if ($split === 0) {
|
||||
// Make sure to move text when floating frames leave no space to
|
||||
// place anything onto the line
|
||||
// TODO: Would probably be better to move just below the current
|
||||
// floating frame instead of trying to place text in line-height
|
||||
// increments
|
||||
if ($current_line->h === 0.0) {
|
||||
// Line height might be 0
|
||||
$h = max($frame->get_margin_height(), 1.0);
|
||||
$block->maximize_line_height($h, $frame);
|
||||
}
|
||||
|
||||
// Break line and repeat layout
|
||||
$block->add_line();
|
||||
|
||||
// Find the appropriate inline ancestor to split
|
||||
$child = $frame;
|
||||
$p = $child->get_parent();
|
||||
while ($p instanceof InlineFrameDecorator && !$child->get_prev_sibling()) {
|
||||
$child = $p;
|
||||
$p = $p->get_parent();
|
||||
}
|
||||
|
||||
if ($p instanceof InlineFrameDecorator) {
|
||||
// Split parent and stop current reflow. Reflow continues
|
||||
// via child-reflow loop of split parent
|
||||
$p->split($child);
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->layout_line($block);
|
||||
}
|
||||
|
||||
// Final split point is determined
|
||||
if ($split !== false && $split < mb_strlen($text)) {
|
||||
// Split the line
|
||||
$frame->set_text($text);
|
||||
$frame->split_text($split);
|
||||
$add_line = true;
|
||||
|
||||
// Remove inner soft hyphens
|
||||
$t = $frame->get_text();
|
||||
$shyPosition = mb_strpos($t, self::SOFT_HYPHEN);
|
||||
if (false !== $shyPosition && $shyPosition < mb_strlen($t) - 1) {
|
||||
$t = str_replace(self::SOFT_HYPHEN, "", mb_substr($t, 0, -1)) . mb_substr($t, -1);
|
||||
$frame->set_text($t);
|
||||
}
|
||||
} else {
|
||||
// No split required
|
||||
// Remove soft hyphens
|
||||
$text = str_replace(self::SOFT_HYPHEN, "", $text);
|
||||
$frame->set_text($text);
|
||||
}
|
||||
|
||||
// Set our new width
|
||||
$frame->recalculate_width();
|
||||
|
||||
return $add_line;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param BlockFrameDecorator|null $block
|
||||
*/
|
||||
function reflow(BlockFrameDecorator $block = null)
|
||||
{
|
||||
$frame = $this->_frame;
|
||||
$page = $frame->get_root();
|
||||
$page->check_forced_page_break($frame);
|
||||
|
||||
if ($page->is_full()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine the text height
|
||||
$style = $frame->get_style();
|
||||
$size = $style->font_size;
|
||||
$font = $style->font_family;
|
||||
$font_height = $this->getFontMetrics()->getFontHeight($font, $size);
|
||||
$style->set_used("height", $font_height);
|
||||
|
||||
// Handle text transform and white space
|
||||
$text = $this->pre_process_text($frame->get_text());
|
||||
$frame->set_text($text);
|
||||
|
||||
if ($block === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$add_line = $this->layout_line($block);
|
||||
|
||||
if ($add_line === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$frame->position();
|
||||
|
||||
// Skip wrapped white space between block-level elements in case white
|
||||
// space is collapsed
|
||||
if ($frame->get_text() === "" && $frame->get_margin_width() === 0.0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$line = $block->add_frame_to_line($frame);
|
||||
$trimmed = trim($frame->get_text());
|
||||
|
||||
// Split the text into words (used to determine spacing between
|
||||
// words on justified lines)
|
||||
if ($trimmed !== "") {
|
||||
$words = preg_split(self::$_whitespace_pattern, $trimmed);
|
||||
$line->wc += count($words);
|
||||
}
|
||||
|
||||
if ($add_line) {
|
||||
$block->add_line();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Trim trailing white space from the frame text.
|
||||
*/
|
||||
public function trim_trailing_ws(): void
|
||||
{
|
||||
$frame = $this->_frame;
|
||||
$text = $frame->get_text();
|
||||
$trailing = mb_substr($text, -1);
|
||||
|
||||
// White space is always collapsed to the standard space character
|
||||
// currently, so only handle that for now
|
||||
if ($trailing === " ") {
|
||||
$this->trailingWs = $trailing;
|
||||
$frame->set_text(mb_substr($text, 0, -1));
|
||||
$frame->recalculate_width();
|
||||
}
|
||||
}
|
||||
|
||||
public function reset(): void
|
||||
{
|
||||
parent::reset();
|
||||
|
||||
// Restore trimmed trailing white space, as the frame will go through
|
||||
// another reflow and line breaks might be different after a split
|
||||
if ($this->trailingWs !== null) {
|
||||
$text = $this->_frame->get_text();
|
||||
$this->_frame->set_text($text . $this->trailingWs);
|
||||
$this->trailingWs = null;
|
||||
}
|
||||
}
|
||||
|
||||
//........................................................................
|
||||
|
||||
public function get_min_max_width(): array
|
||||
{
|
||||
$fontMetrics = $this->getFontMetrics();
|
||||
$frame = $this->_frame;
|
||||
$style = $frame->get_style();
|
||||
$text = $frame->get_text();
|
||||
$font = $style->font_family;
|
||||
$size = $style->font_size;
|
||||
$word_spacing = $style->word_spacing;
|
||||
$letter_spacing = $style->letter_spacing;
|
||||
|
||||
// Handle text transform and white space
|
||||
$text = $this->pre_process_text($frame->get_text());
|
||||
|
||||
if (!$frame->is_pre()) {
|
||||
// Determine whether the frame is at the start of its parent block.
|
||||
// Trim leading white space in that case
|
||||
$child = $frame;
|
||||
$p = $frame->get_parent();
|
||||
while (!$p->is_block() && !$child->get_prev_sibling()) {
|
||||
$child = $p;
|
||||
$p = $p->get_parent();
|
||||
}
|
||||
|
||||
if (!$child->get_prev_sibling()) {
|
||||
$text = ltrim($text, " ");
|
||||
}
|
||||
|
||||
// Determine whether the frame is at the end of its parent block.
|
||||
// Trim trailing white space in that case
|
||||
$child = $frame;
|
||||
$p = $frame->get_parent();
|
||||
while (!$p->is_block() && !$child->get_next_sibling()) {
|
||||
$child = $p;
|
||||
$p = $p->get_parent();
|
||||
}
|
||||
|
||||
if (!$child->get_next_sibling()) {
|
||||
$text = rtrim($text, " ");
|
||||
}
|
||||
}
|
||||
|
||||
// Strip soft hyphens for max-line-width calculations
|
||||
$visible_text = preg_replace('/\xAD/u', "", $text);
|
||||
|
||||
// Determine minimum text width
|
||||
switch ($style->white_space) {
|
||||
default:
|
||||
case "normal":
|
||||
case "pre-line":
|
||||
case "pre-wrap":
|
||||
// The min width is the longest word or, if breaking words is
|
||||
// allowed with the `anywhere` keyword, the widest character.
|
||||
// For performance reasons, we only check the first character in
|
||||
// the latter case.
|
||||
// https://www.w3.org/TR/css-text-3/#overflow-wrap-property
|
||||
if ($style->overflow_wrap === "anywhere") {
|
||||
$char = mb_substr($visible_text, 0, 1);
|
||||
$min = $fontMetrics->getTextWidth($char, $font, $size, $word_spacing, $letter_spacing);
|
||||
} else {
|
||||
// Find the longest word
|
||||
$words = preg_split(self::$_wordbreak_pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE);
|
||||
$lengths = array_map(function ($chunk) use ($fontMetrics, $font, $size, $word_spacing, $letter_spacing) {
|
||||
// Allow trailing white space to overflow. As in actual
|
||||
// layout above, only handle a single space for now
|
||||
$sep = $chunk[1] ?? "";
|
||||
$word = $sep === " " ? $chunk[0] : $chunk[0] . $sep;
|
||||
return $fontMetrics->getTextWidth($word, $font, $size, $word_spacing, $letter_spacing);
|
||||
}, array_chunk($words, 2));
|
||||
$min = max($lengths);
|
||||
}
|
||||
break;
|
||||
|
||||
case "pre":
|
||||
// Find the longest line
|
||||
$lines = array_flip(preg_split("/\R/u", $visible_text));
|
||||
array_walk($lines, function (&$chunked_text_width, $chunked_text) use ($fontMetrics, $font, $size, $word_spacing, $letter_spacing) {
|
||||
$chunked_text_width = $fontMetrics->getTextWidth($chunked_text, $font, $size, $word_spacing, $letter_spacing);
|
||||
});
|
||||
arsort($lines);
|
||||
$min = reset($lines);
|
||||
break;
|
||||
|
||||
case "nowrap":
|
||||
$min = $fontMetrics->getTextWidth($visible_text, $font, $size, $word_spacing, $letter_spacing);
|
||||
break;
|
||||
}
|
||||
|
||||
// Determine maximum text width
|
||||
switch ($style->white_space) {
|
||||
default:
|
||||
case "normal":
|
||||
$max = $fontMetrics->getTextWidth($visible_text, $font, $size, $word_spacing, $letter_spacing);
|
||||
break;
|
||||
|
||||
case "pre-line":
|
||||
case "pre-wrap":
|
||||
// Find the longest line
|
||||
$lines = array_flip(preg_split("/\R/u", $visible_text));
|
||||
array_walk($lines, function (&$chunked_text_width, $chunked_text) use ($fontMetrics, $font, $size, $word_spacing, $letter_spacing) {
|
||||
$chunked_text_width = $fontMetrics->getTextWidth($chunked_text, $font, $size, $word_spacing, $letter_spacing);
|
||||
});
|
||||
arsort($lines);
|
||||
$max = reset($lines);
|
||||
break;
|
||||
|
||||
case "pre":
|
||||
case "nowrap":
|
||||
$max = $min;
|
||||
break;
|
||||
}
|
||||
|
||||
// Account for margins, borders, and padding
|
||||
$dims = [
|
||||
$style->padding_left,
|
||||
$style->padding_right,
|
||||
$style->border_left_width,
|
||||
$style->border_right_width,
|
||||
$style->margin_left,
|
||||
$style->margin_right
|
||||
];
|
||||
|
||||
// The containing block is not defined yet, treat percentages as 0
|
||||
$delta = (float) $style->length_in_pt($dims, 0);
|
||||
$min += $delta;
|
||||
$max += $delta;
|
||||
|
||||
return [$min, $max, "min" => $min, "max" => $max];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param FontMetrics $fontMetrics
|
||||
* @return $this
|
||||
*/
|
||||
public function setFontMetrics(FontMetrics $fontMetrics)
|
||||
{
|
||||
$this->fontMetrics = $fontMetrics;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return FontMetrics
|
||||
*/
|
||||
public function getFontMetrics()
|
||||
{
|
||||
return $this->fontMetrics;
|
||||
}
|
||||
}
|
||||
1095
system/vendor/pancakeapp/dompdf/src/Helpers.php
vendored
Executable file
1095
system/vendor/pancakeapp/dompdf/src/Helpers.php
vendored
Executable file
File diff suppressed because it is too large
Load Diff
254
system/vendor/pancakeapp/dompdf/src/Image/Cache.php
vendored
Executable file
254
system/vendor/pancakeapp/dompdf/src/Image/Cache.php
vendored
Executable file
@@ -0,0 +1,254 @@
|
||||
<?php
|
||||
/**
|
||||
* @package dompdf
|
||||
* @link https://github.com/dompdf/dompdf
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
*/
|
||||
namespace Dompdf\Image;
|
||||
|
||||
use Dompdf\Options;
|
||||
use Dompdf\Helpers;
|
||||
use Dompdf\Exception\ImageException;
|
||||
|
||||
/**
|
||||
* Static class that resolves image urls and downloads and caches
|
||||
* remote images if required.
|
||||
*
|
||||
* @package dompdf
|
||||
*/
|
||||
class Cache
|
||||
{
|
||||
/**
|
||||
* Array of downloaded images. Cached so that identical images are
|
||||
* not needlessly downloaded.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $_cache = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected static $tempImages = [];
|
||||
|
||||
/**
|
||||
* The url to the "broken image" used when images can't be loaded
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
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";
|
||||
|
||||
/**
|
||||
* Resolve and fetch an image for use.
|
||||
*
|
||||
* @param string $url The url of the image
|
||||
* @param string $protocol 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
|
||||
* @param Options $options An instance of Dompdf\Options
|
||||
*
|
||||
* @return array An array with three elements: The local path to the image, the image
|
||||
* extension, and an error message if the image could not be cached
|
||||
*/
|
||||
static function resolve_url($url, $protocol, $host, $base_path, Options $options)
|
||||
{
|
||||
$tempfile = null;
|
||||
$resolved_url = null;
|
||||
$type = null;
|
||||
$message = null;
|
||||
|
||||
try {
|
||||
$full_url = Helpers::build_url($protocol, $host, $base_path, $url);
|
||||
|
||||
if ($full_url === null) {
|
||||
throw new ImageException("Unable to parse image URL $url.", E_WARNING);
|
||||
}
|
||||
|
||||
$parsed_url = Helpers::explode_url($full_url);
|
||||
$protocol = strtolower($parsed_url["protocol"]);
|
||||
$is_data_uri = strpos($protocol, "data:") === 0;
|
||||
|
||||
if (!$is_data_uri) {
|
||||
$allowed_protocols = $options->getAllowedProtocols();
|
||||
if (!array_key_exists($protocol, $allowed_protocols)) {
|
||||
throw new ImageException("Permission denied on $url. The communication protocol is not supported.", E_WARNING);
|
||||
}
|
||||
foreach ($allowed_protocols[$protocol]["rules"] as $rule) {
|
||||
[$result, $message] = $rule($full_url);
|
||||
if (!$result) {
|
||||
throw new ImageException("Error loading $url: $message", E_WARNING);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($protocol === "file://") {
|
||||
$resolved_url = $full_url;
|
||||
} elseif (isset(self::$_cache[$full_url])) {
|
||||
$resolved_url = self::$_cache[$full_url];
|
||||
} else {
|
||||
$tmp_dir = $options->getTempDir();
|
||||
if (($resolved_url = @tempnam($tmp_dir, "ca_dompdf_img_")) === false) {
|
||||
throw new ImageException("Unable to create temporary image in " . $tmp_dir, E_WARNING);
|
||||
}
|
||||
$tempfile = $resolved_url;
|
||||
|
||||
$image = null;
|
||||
if ($is_data_uri) {
|
||||
if (($parsed_data_uri = Helpers::parse_data_uri($url)) !== false) {
|
||||
$image = $parsed_data_uri["data"];
|
||||
}
|
||||
} else {
|
||||
list($image, $http_response_header) = Helpers::getFileContent($full_url, $options->getHttpContext());
|
||||
}
|
||||
|
||||
// Image not found or invalid
|
||||
if ($image === null) {
|
||||
$msg = ($is_data_uri ? "Data-URI could not be parsed" : "Image not found");
|
||||
throw new ImageException($msg, E_WARNING);
|
||||
}
|
||||
|
||||
if (@file_put_contents($resolved_url, $image) === false) {
|
||||
throw new ImageException("Unable to create temporary image in " . $tmp_dir, E_WARNING);
|
||||
}
|
||||
|
||||
self::$_cache[$full_url] = $resolved_url;
|
||||
}
|
||||
|
||||
// Check if the local file is readable
|
||||
if (!is_readable($resolved_url) || !filesize($resolved_url)) {
|
||||
throw new ImageException("Image not readable or empty", E_WARNING);
|
||||
}
|
||||
|
||||
list($width, $height, $type) = Helpers::dompdf_getimagesize($resolved_url, $options->getHttpContext());
|
||||
|
||||
if (($width && $height && in_array($type, ["gif", "png", "jpeg", "bmp", "svg","webp"], true)) === false) {
|
||||
throw new ImageException("Image type unknown", E_WARNING);
|
||||
}
|
||||
|
||||
if ($type === "svg") {
|
||||
$parser = xml_parser_create("utf-8");
|
||||
xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, false);
|
||||
xml_set_element_handler(
|
||||
$parser,
|
||||
function ($parser, $name, $attributes) use ($options, $parsed_url, $full_url) {
|
||||
if ($name === "image") {
|
||||
$attributes = array_change_key_case($attributes, CASE_LOWER);
|
||||
$url = $attributes["xlink:href"] ?? $attributes["href"];
|
||||
if (!empty($url)) {
|
||||
$inner_full_url = Helpers::build_url($parsed_url["protocol"], $parsed_url["host"], $parsed_url["path"], $url);
|
||||
if ($inner_full_url === $full_url) {
|
||||
throw new ImageException("SVG self-reference is not allowed", E_WARNING);
|
||||
}
|
||||
[$resolved_url, $type, $message] = self::resolve_url($url, $parsed_url["protocol"], $parsed_url["host"], $parsed_url["path"], $options);
|
||||
if (!empty($message)) {
|
||||
throw new ImageException("This SVG document references a restricted resource. $message", E_WARNING);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
false
|
||||
);
|
||||
|
||||
if (($fp = fopen($resolved_url, "r")) !== false) {
|
||||
while ($line = fread($fp, 8192)) {
|
||||
xml_parse($parser, $line, false);
|
||||
}
|
||||
fclose($fp);
|
||||
}
|
||||
xml_parser_free($parser);
|
||||
}
|
||||
} catch (ImageException $e) {
|
||||
if ($tempfile) {
|
||||
unlink($tempfile);
|
||||
}
|
||||
$resolved_url = self::$broken_image;
|
||||
list($width, $height, $type) = Helpers::dompdf_getimagesize($resolved_url, $options->getHttpContext());
|
||||
$message = self::$error_message;
|
||||
Helpers::record_warnings($e->getCode(), $e->getMessage() . " \n $url", $e->getFile(), $e->getLine());
|
||||
self::$_cache[$full_url] = $resolved_url;
|
||||
}
|
||||
|
||||
return [$resolved_url, $type, $message];
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a temp file for the given original image file.
|
||||
*
|
||||
* @param string $filePath The path of the original image.
|
||||
* @param string $tempPath The path of the temp file to register.
|
||||
* @param string $key An optional key to register the temp file at.
|
||||
*/
|
||||
static function addTempImage(string $filePath, string $tempPath, string $key = "default"): void
|
||||
{
|
||||
if (!isset(self::$tempImages[$filePath])) {
|
||||
self::$tempImages[$filePath] = [];
|
||||
}
|
||||
|
||||
self::$tempImages[$filePath][$key] = $tempPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path of a temp file registered for the given original image file.
|
||||
*
|
||||
* @param string $filePath The path of the original image.
|
||||
* @param string $key The key the temp file is registered at.
|
||||
*/
|
||||
static function getTempImage(string $filePath, string $key = "default"): ?string
|
||||
{
|
||||
return self::$tempImages[$filePath][$key] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlink all cached images (i.e. temporary images either downloaded
|
||||
* or converted) except for the bundled "broken image"
|
||||
*/
|
||||
static function clear(bool $debugPng = false)
|
||||
{
|
||||
foreach (self::$_cache as $file) {
|
||||
if ($file === self::$broken_image) {
|
||||
continue;
|
||||
}
|
||||
if ($debugPng) {
|
||||
print "[clear unlink $file]";
|
||||
}
|
||||
if (file_exists($file)) {
|
||||
unlink($file);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (self::$tempImages as $versions) {
|
||||
foreach ($versions as $file) {
|
||||
if ($file === self::$broken_image) {
|
||||
continue;
|
||||
}
|
||||
if ($debugPng) {
|
||||
print "[unlink temp image $file]";
|
||||
}
|
||||
if (file_exists($file)) {
|
||||
unlink($file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self::$_cache = [];
|
||||
self::$tempImages = [];
|
||||
}
|
||||
|
||||
static function detect_type($file, $context = null)
|
||||
{
|
||||
list(, , $type) = Helpers::dompdf_getimagesize($file, $context);
|
||||
|
||||
return $type;
|
||||
}
|
||||
|
||||
static function is_broken($url)
|
||||
{
|
||||
return $url === self::$broken_image;
|
||||
}
|
||||
}
|
||||
|
||||
if (file_exists(realpath(__DIR__ . "/../../lib/res/broken_image.svg"))) {
|
||||
Cache::$broken_image = realpath(__DIR__ . "/../../lib/res/broken_image.svg");
|
||||
}
|
||||
51
system/vendor/pancakeapp/dompdf/src/JavascriptEmbedder.php
vendored
Executable file
51
system/vendor/pancakeapp/dompdf/src/JavascriptEmbedder.php
vendored
Executable file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
/**
|
||||
* @package dompdf
|
||||
* @link https://github.com/dompdf/dompdf
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
*/
|
||||
namespace Dompdf;
|
||||
|
||||
/**
|
||||
* Embeds Javascript into the PDF document
|
||||
*
|
||||
* @package dompdf
|
||||
*/
|
||||
class JavascriptEmbedder
|
||||
{
|
||||
|
||||
/**
|
||||
* @var Dompdf
|
||||
*/
|
||||
protected $_dompdf;
|
||||
|
||||
/**
|
||||
* JavascriptEmbedder constructor.
|
||||
*
|
||||
* @param Dompdf $dompdf
|
||||
*/
|
||||
public function __construct(Dompdf $dompdf)
|
||||
{
|
||||
$this->_dompdf = $dompdf;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $script
|
||||
*/
|
||||
public function insert($script)
|
||||
{
|
||||
$this->_dompdf->getCanvas()->javascript($script);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Frame $frame
|
||||
*/
|
||||
public function render(Frame $frame)
|
||||
{
|
||||
if (!$this->_dompdf->getOptions()->getIsJavascriptEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->insert($frame->get_node()->nodeValue);
|
||||
}
|
||||
}
|
||||
412
system/vendor/pancakeapp/dompdf/src/LineBox.php
vendored
Executable file
412
system/vendor/pancakeapp/dompdf/src/LineBox.php
vendored
Executable file
@@ -0,0 +1,412 @@
|
||||
<?php
|
||||
/**
|
||||
* @package dompdf
|
||||
* @link https://github.com/dompdf/dompdf
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
*/
|
||||
namespace Dompdf;
|
||||
|
||||
use Dompdf\FrameDecorator\AbstractFrameDecorator;
|
||||
use Dompdf\FrameDecorator\Block;
|
||||
use Dompdf\FrameDecorator\ListBullet;
|
||||
use Dompdf\FrameDecorator\Page;
|
||||
use Dompdf\FrameReflower\Text as TextFrameReflower;
|
||||
use Dompdf\Positioner\Inline as InlinePositioner;
|
||||
|
||||
/**
|
||||
* The line box class
|
||||
*
|
||||
* This class represents a line box
|
||||
* http://www.w3.org/TR/CSS2/visuren.html#line-box
|
||||
*
|
||||
* @package dompdf
|
||||
*/
|
||||
class LineBox
|
||||
{
|
||||
|
||||
/**
|
||||
* @var Block
|
||||
*/
|
||||
protected $_block_frame;
|
||||
|
||||
/**
|
||||
* @var AbstractFrameDecorator[]
|
||||
*/
|
||||
protected $_frames = [];
|
||||
|
||||
/**
|
||||
* @var ListBullet[]
|
||||
*/
|
||||
protected $list_markers = [];
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
public $wc = 0;
|
||||
|
||||
/**
|
||||
* @var float
|
||||
*/
|
||||
public $y = null;
|
||||
|
||||
/**
|
||||
* @var float
|
||||
*/
|
||||
public $w = 0.0;
|
||||
|
||||
/**
|
||||
* @var float
|
||||
*/
|
||||
public $h = 0.0;
|
||||
|
||||
/**
|
||||
* @var float
|
||||
*/
|
||||
public $left = 0.0;
|
||||
|
||||
/**
|
||||
* @var float
|
||||
*/
|
||||
public $right = 0.0;
|
||||
|
||||
/**
|
||||
* @var AbstractFrameDecorator
|
||||
*/
|
||||
public $tallest_frame = null;
|
||||
|
||||
/**
|
||||
* @var bool[]
|
||||
*/
|
||||
public $floating_blocks = [];
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $br = false;
|
||||
|
||||
/**
|
||||
* Whether the line box contains any inline-positioned frames.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $inline = false;
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param Block $frame the Block containing this line
|
||||
* @param int $y
|
||||
*/
|
||||
public function __construct(Block $frame, $y = 0)
|
||||
{
|
||||
$this->_block_frame = $frame;
|
||||
$this->_frames = [];
|
||||
$this->y = $y;
|
||||
|
||||
$this->get_float_offsets();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the floating elements inside the first floating parent
|
||||
*
|
||||
* @param Page $root
|
||||
*
|
||||
* @return Frame[]
|
||||
*/
|
||||
public function get_floats_inside(Page $root)
|
||||
{
|
||||
$floating_frames = $root->get_floating_frames();
|
||||
|
||||
if (count($floating_frames) == 0) {
|
||||
return $floating_frames;
|
||||
}
|
||||
|
||||
// Find nearest floating element
|
||||
$p = $this->_block_frame;
|
||||
while ($p->get_style()->float === "none") {
|
||||
$parent = $p->get_parent();
|
||||
|
||||
if (!$parent) {
|
||||
break;
|
||||
}
|
||||
|
||||
$p = $parent;
|
||||
}
|
||||
|
||||
if ($p == $root) {
|
||||
return $floating_frames;
|
||||
}
|
||||
|
||||
$parent = $p;
|
||||
|
||||
$childs = [];
|
||||
|
||||
foreach ($floating_frames as $_floating) {
|
||||
$p = $_floating->get_parent();
|
||||
|
||||
while (($p = $p->get_parent()) && $p !== $parent);
|
||||
|
||||
if ($p) {
|
||||
$childs[] = $p;
|
||||
}
|
||||
}
|
||||
|
||||
return $childs;
|
||||
}
|
||||
|
||||
public function get_float_offsets()
|
||||
{
|
||||
static $anti_infinite_loop = 10000; // FIXME smelly hack
|
||||
|
||||
$reflower = $this->_block_frame->get_reflower();
|
||||
|
||||
if (!$reflower) {
|
||||
return;
|
||||
}
|
||||
|
||||
$cb_w = null;
|
||||
|
||||
$block = $this->_block_frame;
|
||||
$root = $block->get_root();
|
||||
|
||||
if (!$root) {
|
||||
return;
|
||||
}
|
||||
|
||||
$style = $this->_block_frame->get_style();
|
||||
$floating_frames = $this->get_floats_inside($root);
|
||||
$inside_left_floating_width = 0;
|
||||
$inside_right_floating_width = 0;
|
||||
$outside_left_floating_width = 0;
|
||||
$outside_right_floating_width = 0;
|
||||
|
||||
foreach ($floating_frames as $child_key => $floating_frame) {
|
||||
$floating_frame_parent = $floating_frame->get_parent();
|
||||
$id = $floating_frame->get_id();
|
||||
|
||||
if (isset($this->floating_blocks[$id])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$float = $floating_frame->get_style()->float;
|
||||
$floating_width = $floating_frame->get_margin_width();
|
||||
|
||||
if (!$cb_w) {
|
||||
$cb_w = $floating_frame->get_containing_block("w");
|
||||
}
|
||||
|
||||
$line_w = $this->get_width();
|
||||
|
||||
if (!$floating_frame->_float_next_line && ($cb_w <= $line_w + $floating_width) && ($cb_w > $line_w)) {
|
||||
$floating_frame->_float_next_line = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the child is still shifted by the floating element
|
||||
if ($anti_infinite_loop-- > 0 &&
|
||||
$floating_frame->get_position("y") + $floating_frame->get_margin_height() >= $this->y &&
|
||||
$block->get_position("x") + $block->get_margin_width() >= $floating_frame->get_position("x")
|
||||
) {
|
||||
if ($float === "left") {
|
||||
if ($floating_frame_parent === $this->_block_frame) {
|
||||
$inside_left_floating_width += $floating_width;
|
||||
} else {
|
||||
$outside_left_floating_width += $floating_width;
|
||||
}
|
||||
} elseif ($float === "right") {
|
||||
if ($floating_frame_parent === $this->_block_frame) {
|
||||
$inside_right_floating_width += $floating_width;
|
||||
} else {
|
||||
$outside_right_floating_width += $floating_width;
|
||||
}
|
||||
}
|
||||
|
||||
$this->floating_blocks[$id] = true;
|
||||
} // else, the floating element won't shift anymore
|
||||
else {
|
||||
$root->remove_floating_frame($child_key);
|
||||
}
|
||||
}
|
||||
|
||||
$this->left += $inside_left_floating_width;
|
||||
if ($outside_left_floating_width > 0 && $outside_left_floating_width > ((float)$style->length_in_pt($style->margin_left) + (float)$style->length_in_pt($style->padding_left))) {
|
||||
$this->left += $outside_left_floating_width - (float)$style->length_in_pt($style->margin_left) - (float)$style->length_in_pt($style->padding_left);
|
||||
}
|
||||
$this->right += $inside_right_floating_width;
|
||||
if ($outside_right_floating_width > 0 && $outside_right_floating_width > ((float)$style->length_in_pt($style->margin_left) + (float)$style->length_in_pt($style->padding_right))) {
|
||||
$this->right += $outside_right_floating_width - (float)$style->length_in_pt($style->margin_right) - (float)$style->length_in_pt($style->padding_right);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return float
|
||||
*/
|
||||
public function get_width()
|
||||
{
|
||||
return $this->left + $this->w + $this->right;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Block
|
||||
*/
|
||||
public function get_block_frame()
|
||||
{
|
||||
return $this->_block_frame;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return AbstractFrameDecorator[]
|
||||
*/
|
||||
function &get_frames()
|
||||
{
|
||||
return $this->_frames;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AbstractFrameDecorator $frame
|
||||
*/
|
||||
public function add_frame(Frame $frame): void
|
||||
{
|
||||
$this->_frames[] = $frame;
|
||||
|
||||
if ($frame->get_positioner() instanceof InlinePositioner) {
|
||||
$this->inline = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the frame at the given index and all following frames from the
|
||||
* line.
|
||||
*
|
||||
* @param int $index
|
||||
*/
|
||||
public function remove_frames(int $index): void
|
||||
{
|
||||
$lastIndex = count($this->_frames) - 1;
|
||||
|
||||
if ($index < 0 || $index > $lastIndex) {
|
||||
return;
|
||||
}
|
||||
|
||||
for ($i = $lastIndex; $i >= $index; $i--) {
|
||||
$f = $this->_frames[$i];
|
||||
unset($this->_frames[$i]);
|
||||
$this->w -= $f->get_margin_width();
|
||||
}
|
||||
|
||||
// Reset array indices
|
||||
$this->_frames = array_values($this->_frames);
|
||||
|
||||
// Recalculate the height of the line
|
||||
$h = 0.0;
|
||||
$this->inline = false;
|
||||
|
||||
foreach ($this->_frames as $f) {
|
||||
$h = max($h, $f->get_margin_height());
|
||||
|
||||
if ($f->get_positioner() instanceof InlinePositioner) {
|
||||
$this->inline = true;
|
||||
}
|
||||
}
|
||||
|
||||
$this->h = $h;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the `outside` positioned list markers to be vertically aligned with
|
||||
* the line box.
|
||||
*
|
||||
* @return ListBullet[]
|
||||
*/
|
||||
public function get_list_markers(): array
|
||||
{
|
||||
return $this->list_markers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a list marker to the line box.
|
||||
*
|
||||
* The list marker is only added for the purpose of vertical alignment, it
|
||||
* is not actually added to the list of frames of the line box.
|
||||
*/
|
||||
public function add_list_marker(ListBullet $marker): void
|
||||
{
|
||||
$this->list_markers[] = $marker;
|
||||
}
|
||||
|
||||
/**
|
||||
* An iterator of all list markers and inline positioned frames of the line
|
||||
* box.
|
||||
*
|
||||
* @return \Iterator<AbstractFrameDecorator>
|
||||
*/
|
||||
public function frames_to_align(): \Iterator
|
||||
{
|
||||
yield from $this->list_markers;
|
||||
|
||||
foreach ($this->_frames as $frame) {
|
||||
if ($frame->get_positioner() instanceof InlinePositioner) {
|
||||
yield $frame;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Trim trailing whitespace from the line.
|
||||
*/
|
||||
public function trim_trailing_ws(): void
|
||||
{
|
||||
$lastIndex = count($this->_frames) - 1;
|
||||
|
||||
if ($lastIndex < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$lastFrame = $this->_frames[$lastIndex];
|
||||
$reflower = $lastFrame->get_reflower();
|
||||
|
||||
if ($reflower instanceof TextFrameReflower && !$lastFrame->is_pre()) {
|
||||
$reflower->trim_trailing_ws();
|
||||
$this->recalculate_width();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Recalculate LineBox width based on the contained frames total width.
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function recalculate_width(): float
|
||||
{
|
||||
$width = 0.0;
|
||||
|
||||
foreach ($this->_frames as $frame) {
|
||||
$width += $frame->get_margin_width();
|
||||
}
|
||||
|
||||
return $this->w = $width;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
$props = ["wc", "y", "w", "h", "left", "right", "br"];
|
||||
$s = "";
|
||||
foreach ($props as $prop) {
|
||||
$s .= "$prop: " . $this->$prop . "\n";
|
||||
}
|
||||
$s .= count($this->_frames) . " frames\n";
|
||||
|
||||
return $s;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
class LineBoxList implements Iterator {
|
||||
private $_p = 0;
|
||||
private $_lines = array();
|
||||
|
||||
}
|
||||
*/
|
||||
1152
system/vendor/pancakeapp/dompdf/src/Options.php
vendored
Executable file
1152
system/vendor/pancakeapp/dompdf/src/Options.php
vendored
Executable file
File diff suppressed because it is too large
Load Diff
62
system/vendor/pancakeapp/dompdf/src/PhpEvaluator.php
vendored
Executable file
62
system/vendor/pancakeapp/dompdf/src/PhpEvaluator.php
vendored
Executable file
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
/**
|
||||
* @package dompdf
|
||||
* @link https://github.com/dompdf/dompdf
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
*/
|
||||
namespace Dompdf;
|
||||
|
||||
/**
|
||||
* Executes inline PHP code during the rendering process
|
||||
*
|
||||
* @package dompdf
|
||||
*/
|
||||
class PhpEvaluator
|
||||
{
|
||||
|
||||
/**
|
||||
* @var Canvas
|
||||
*/
|
||||
protected $_canvas;
|
||||
|
||||
/**
|
||||
* PhpEvaluator constructor.
|
||||
* @param Canvas $canvas
|
||||
*/
|
||||
public function __construct(Canvas $canvas)
|
||||
{
|
||||
$this->_canvas = $canvas;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $code
|
||||
* @param array $vars
|
||||
*/
|
||||
public function evaluate($code, $vars = [])
|
||||
{
|
||||
if (!$this->_canvas->get_dompdf()->getOptions()->getIsPhpEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set up some variables for the inline code
|
||||
$pdf = $this->_canvas;
|
||||
$fontMetrics = $pdf->get_dompdf()->getFontMetrics();
|
||||
$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($code);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Frame $frame
|
||||
*/
|
||||
public function render(Frame $frame)
|
||||
{
|
||||
$this->evaluate($frame->get_node()->nodeValue);
|
||||
}
|
||||
}
|
||||
128
system/vendor/pancakeapp/dompdf/src/Positioner/Absolute.php
vendored
Executable file
128
system/vendor/pancakeapp/dompdf/src/Positioner/Absolute.php
vendored
Executable file
@@ -0,0 +1,128 @@
|
||||
<?php
|
||||
/**
|
||||
* @package dompdf
|
||||
* @link https://github.com/dompdf/dompdf
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
*/
|
||||
namespace Dompdf\Positioner;
|
||||
|
||||
use Dompdf\FrameDecorator\AbstractFrameDecorator;
|
||||
use Dompdf\FrameReflower\Block;
|
||||
|
||||
/**
|
||||
* Positions absolutely positioned frames
|
||||
*/
|
||||
class Absolute extends AbstractPositioner
|
||||
{
|
||||
|
||||
/**
|
||||
* @param AbstractFrameDecorator $frame
|
||||
*/
|
||||
function position(AbstractFrameDecorator $frame): void
|
||||
{
|
||||
if ($frame->get_reflower() instanceof Block) {
|
||||
$style = $frame->get_style();
|
||||
[$cbx, $cby, $cbw, $cbh] = $frame->get_containing_block();
|
||||
|
||||
// If the `top` value is `auto`, the frame will be repositioned
|
||||
// after its height has been resolved
|
||||
$left = (float) $style->length_in_pt($style->left, $cbw);
|
||||
$top = (float) $style->length_in_pt($style->top, $cbh);
|
||||
|
||||
$frame->set_position($cbx + $left, $cby + $top);
|
||||
} else {
|
||||
// Legacy positioning logic for image and table frames
|
||||
// TODO: Resolve dimensions, margins, and offsets similar to the
|
||||
// block case in the reflowers and use the simplified logic above
|
||||
$style = $frame->get_style();
|
||||
$block_parent = $frame->find_block_parent();
|
||||
$current_line = $block_parent->get_current_line_box();
|
||||
|
||||
list($x, $y, $w, $h) = $frame->get_containing_block();
|
||||
$inflow_x = $block_parent->get_content_box()["x"] + $current_line->left + $current_line->w;
|
||||
$inflow_y = $current_line->y;
|
||||
|
||||
$top = $style->length_in_pt($style->top, $h);
|
||||
$right = $style->length_in_pt($style->right, $w);
|
||||
$bottom = $style->length_in_pt($style->bottom, $h);
|
||||
$left = $style->length_in_pt($style->left, $w);
|
||||
|
||||
list($width, $height) = [$frame->get_margin_width(), $frame->get_margin_height()];
|
||||
|
||||
$orig_width = $style->get_specified("width");
|
||||
$orig_height = $style->get_specified("height");
|
||||
|
||||
/****************************
|
||||
*
|
||||
* Width auto:
|
||||
* ____________| left=auto | left=fixed |
|
||||
* right=auto | A | B |
|
||||
* right=fixed | C | D |
|
||||
*
|
||||
* Width fixed:
|
||||
* ____________| left=auto | left=fixed |
|
||||
* right=auto | E | F |
|
||||
* right=fixed | G | H |
|
||||
*****************************/
|
||||
|
||||
if ($left === "auto") {
|
||||
if ($right === "auto") {
|
||||
// A or E - Keep the frame at the same position
|
||||
$x = $inflow_x;
|
||||
} else {
|
||||
if ($orig_width === "auto") {
|
||||
// C
|
||||
$x += $w - $width - $right;
|
||||
} else {
|
||||
// G
|
||||
$x += $w - $width - $right;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ($right === "auto") {
|
||||
// B or F
|
||||
$x += (float)$left;
|
||||
} else {
|
||||
if ($orig_width === "auto") {
|
||||
// D - TODO change width
|
||||
$x += (float)$left;
|
||||
} else {
|
||||
// H - Everything is fixed: left + width win
|
||||
$x += (float)$left;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The same vertically
|
||||
if ($top === "auto") {
|
||||
if ($bottom === "auto") {
|
||||
// A or E - Keep the frame at the same position
|
||||
$y = $inflow_y;
|
||||
} else {
|
||||
if ($orig_height === "auto") {
|
||||
// C
|
||||
$y += (float)$h - $height - (float)$bottom;
|
||||
} else {
|
||||
// G
|
||||
$y += (float)$h - $height - (float)$bottom;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ($bottom === "auto") {
|
||||
// B or F
|
||||
$y += (float)$top;
|
||||
} else {
|
||||
if ($orig_height === "auto") {
|
||||
// D - TODO change height
|
||||
$y += (float)$top;
|
||||
} else {
|
||||
// H - Everything is fixed: top + height win
|
||||
$y += (float)$top;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$frame->set_position($x, $y);
|
||||
}
|
||||
}
|
||||
}
|
||||
48
system/vendor/pancakeapp/dompdf/src/Positioner/AbstractPositioner.php
vendored
Executable file
48
system/vendor/pancakeapp/dompdf/src/Positioner/AbstractPositioner.php
vendored
Executable file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
/**
|
||||
* @package dompdf
|
||||
* @link https://github.com/dompdf/dompdf
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
*/
|
||||
namespace Dompdf\Positioner;
|
||||
|
||||
use Dompdf\FrameDecorator\AbstractFrameDecorator;
|
||||
|
||||
/**
|
||||
* Base AbstractPositioner class
|
||||
*
|
||||
* Defines positioner interface
|
||||
*
|
||||
* @package dompdf
|
||||
*/
|
||||
abstract class AbstractPositioner
|
||||
{
|
||||
|
||||
/**
|
||||
* @param AbstractFrameDecorator $frame
|
||||
*/
|
||||
abstract function position(AbstractFrameDecorator $frame): void;
|
||||
|
||||
/**
|
||||
* @param AbstractFrameDecorator $frame
|
||||
* @param float $offset_x
|
||||
* @param float $offset_y
|
||||
* @param bool $ignore_self
|
||||
*/
|
||||
function move(
|
||||
AbstractFrameDecorator $frame,
|
||||
float $offset_x,
|
||||
float $offset_y,
|
||||
bool $ignore_self = false
|
||||
): void {
|
||||
[$x, $y] = $frame->get_position();
|
||||
|
||||
if (!$ignore_self) {
|
||||
$frame->set_position($x + $offset_x, $y + $offset_y);
|
||||
}
|
||||
|
||||
foreach ($frame->get_children() as $child) {
|
||||
$child->move($offset_x, $offset_y);
|
||||
}
|
||||
}
|
||||
}
|
||||
40
system/vendor/pancakeapp/dompdf/src/Positioner/Block.php
vendored
Executable file
40
system/vendor/pancakeapp/dompdf/src/Positioner/Block.php
vendored
Executable file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
/**
|
||||
* @package dompdf
|
||||
* @link https://github.com/dompdf/dompdf
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
*/
|
||||
namespace Dompdf\Positioner;
|
||||
|
||||
use Dompdf\FrameDecorator\AbstractFrameDecorator;
|
||||
|
||||
/**
|
||||
* Positions block frames
|
||||
*
|
||||
* @package dompdf
|
||||
*/
|
||||
class Block extends AbstractPositioner
|
||||
{
|
||||
|
||||
function position(AbstractFrameDecorator $frame): void
|
||||
{
|
||||
$style = $frame->get_style();
|
||||
$cb = $frame->get_containing_block();
|
||||
$p = $frame->find_block_parent();
|
||||
|
||||
if ($p) {
|
||||
$float = $style->float;
|
||||
|
||||
if (!$float || $float === "none") {
|
||||
$p->add_line(true);
|
||||
}
|
||||
$y = $p->get_current_line_box()->y;
|
||||
} else {
|
||||
$y = $cb["y"];
|
||||
}
|
||||
|
||||
$x = $cb["x"];
|
||||
|
||||
$frame->set_position($x, $y);
|
||||
}
|
||||
}
|
||||
92
system/vendor/pancakeapp/dompdf/src/Positioner/Fixed.php
vendored
Executable file
92
system/vendor/pancakeapp/dompdf/src/Positioner/Fixed.php
vendored
Executable file
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
/**
|
||||
* @package dompdf
|
||||
* @link https://github.com/dompdf/dompdf
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
*/
|
||||
namespace Dompdf\Positioner;
|
||||
|
||||
use Dompdf\FrameDecorator\AbstractFrameDecorator;
|
||||
use Dompdf\FrameReflower\Block;
|
||||
|
||||
/**
|
||||
* Positions fixely positioned frames
|
||||
*/
|
||||
class Fixed extends Absolute
|
||||
{
|
||||
|
||||
/**
|
||||
* @param AbstractFrameDecorator $frame
|
||||
*/
|
||||
function position(AbstractFrameDecorator $frame): void
|
||||
{
|
||||
if ($frame->get_reflower() instanceof Block) {
|
||||
parent::position($frame);
|
||||
} else {
|
||||
// Legacy positioning logic for image and table frames
|
||||
// TODO: Resolve dimensions, margins, and offsets similar to the
|
||||
// block case in the reflowers and use the simplified logic above
|
||||
$style = $frame->get_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 = (float)$initialcb_style->length_in_pt($initialcb_style->margin_top, $initialcb["h"]);
|
||||
$margin_right = (float)$initialcb_style->length_in_pt($initialcb_style->margin_right, $initialcb["w"]);
|
||||
$margin_bottom = (float)$initialcb_style->length_in_pt($initialcb_style->margin_bottom, $initialcb["h"]);
|
||||
$margin_left = (float)$initialcb_style->length_in_pt($initialcb_style->margin_left, $initialcb["w"]);
|
||||
|
||||
// The needed computed style of the element
|
||||
$height = (float)$style->length_in_pt($style->get_specified("height"), $initialcb["h"]);
|
||||
$width = (float)$style->length_in_pt($style->get_specified("width"), $initialcb["w"]);
|
||||
|
||||
$top = $style->length_in_pt($style->get_specified("top"), $initialcb["h"]);
|
||||
$right = $style->length_in_pt($style->get_specified("right"), $initialcb["w"]);
|
||||
$bottom = $style->length_in_pt($style->get_specified("bottom"), $initialcb["h"]);
|
||||
$left = $style->length_in_pt($style->get_specified("left"), $initialcb["w"]);
|
||||
|
||||
$y = $margin_top;
|
||||
if (isset($top)) {
|
||||
$y = (float)$top + $margin_top;
|
||||
if ($top === "auto") {
|
||||
$y = $margin_top;
|
||||
if (isset($bottom) && $bottom !== "auto") {
|
||||
$y = $initialcb["h"] - $bottom - $margin_bottom;
|
||||
if ($frame->is_auto_height()) {
|
||||
$y -= $height;
|
||||
} else {
|
||||
$y -= $frame->get_margin_height();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$x = $margin_left;
|
||||
if (isset($left)) {
|
||||
$x = (float)$left + $margin_left;
|
||||
if ($left === "auto") {
|
||||
$x = $margin_left;
|
||||
if (isset($right) && $right !== "auto") {
|
||||
$x = $initialcb["w"] - $right - $margin_right;
|
||||
if ($frame->is_auto_width()) {
|
||||
$x -= $width;
|
||||
} else {
|
||||
$x -= $frame->get_margin_width();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$frame->set_position($x, $y);
|
||||
|
||||
foreach ($frame->get_children() as $child) {
|
||||
$child->set_position($x, $y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
52
system/vendor/pancakeapp/dompdf/src/Positioner/Inline.php
vendored
Executable file
52
system/vendor/pancakeapp/dompdf/src/Positioner/Inline.php
vendored
Executable file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
/**
|
||||
* @package dompdf
|
||||
* @link https://github.com/dompdf/dompdf
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
*/
|
||||
namespace Dompdf\Positioner;
|
||||
|
||||
use Dompdf\FrameDecorator\AbstractFrameDecorator;
|
||||
use Dompdf\FrameDecorator\Inline as InlineFrameDecorator;
|
||||
use Dompdf\Exception;
|
||||
use Dompdf\Helpers;
|
||||
|
||||
/**
|
||||
* Positions inline frames
|
||||
*
|
||||
* @package dompdf
|
||||
*/
|
||||
class Inline extends AbstractPositioner
|
||||
{
|
||||
|
||||
/**
|
||||
* @param AbstractFrameDecorator $frame
|
||||
* @throws Exception
|
||||
*/
|
||||
function position(AbstractFrameDecorator $frame): void
|
||||
{
|
||||
// Find our nearest block level parent and access its lines property
|
||||
$block = $frame->find_block_parent();
|
||||
|
||||
if (!$block) {
|
||||
throw new Exception("No block-level parent found. Not good.");
|
||||
}
|
||||
|
||||
$cb = $frame->get_containing_block();
|
||||
$line = $block->get_current_line_box();
|
||||
|
||||
if (!$frame->is_text_node() && !($frame instanceof InlineFrameDecorator)) {
|
||||
// Atomic inline boxes and replaced inline elements
|
||||
// (inline-block, inline-table, img etc.)
|
||||
$width = $frame->get_margin_width();
|
||||
$available_width = $cb["w"] - $line->left - $line->w - $line->right;
|
||||
|
||||
if (Helpers::lengthGreater($width, $available_width)) {
|
||||
$block->add_line();
|
||||
$line = $block->get_current_line_box();
|
||||
}
|
||||
}
|
||||
|
||||
$frame->set_position($cb["x"] + $line->w, $line->y);
|
||||
}
|
||||
}
|
||||
42
system/vendor/pancakeapp/dompdf/src/Positioner/ListBullet.php
vendored
Executable file
42
system/vendor/pancakeapp/dompdf/src/Positioner/ListBullet.php
vendored
Executable file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
/**
|
||||
* @package dompdf
|
||||
* @link https://github.com/dompdf/dompdf
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
*/
|
||||
namespace Dompdf\Positioner;
|
||||
|
||||
use Dompdf\FrameDecorator\AbstractFrameDecorator;
|
||||
use Dompdf\FrameDecorator\ListBullet as ListBulletFrameDecorator;
|
||||
|
||||
/**
|
||||
* Positions list bullets
|
||||
*
|
||||
* @package dompdf
|
||||
*/
|
||||
class ListBullet extends AbstractPositioner
|
||||
{
|
||||
/**
|
||||
* @param ListBulletFrameDecorator $frame
|
||||
*/
|
||||
function position(AbstractFrameDecorator $frame): void
|
||||
{
|
||||
// List markers are positioned to the left of the border edge of their
|
||||
// parent element (FIXME: right for RTL)
|
||||
$parent = $frame->get_parent();
|
||||
$style = $parent->get_style();
|
||||
$cbw = $parent->get_containing_block("w");
|
||||
$margin_left = (float) $style->length_in_pt($style->margin_left, $cbw);
|
||||
$border_edge = $parent->get_position("x") + $margin_left;
|
||||
|
||||
// This includes the marker indentation
|
||||
$x = $border_edge - $frame->get_margin_width();
|
||||
|
||||
// The marker is later vertically aligned with the corresponding line
|
||||
// box and its vertical position is fine-tuned in the renderer
|
||||
$p = $frame->find_block_parent();
|
||||
$y = $p->get_current_line_box()->y;
|
||||
|
||||
$frame->set_position($x, $y);
|
||||
}
|
||||
}
|
||||
26
system/vendor/pancakeapp/dompdf/src/Positioner/NullPositioner.php
vendored
Executable file
26
system/vendor/pancakeapp/dompdf/src/Positioner/NullPositioner.php
vendored
Executable file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
/**
|
||||
* @package dompdf
|
||||
* @link https://github.com/dompdf/dompdf
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
*/
|
||||
namespace Dompdf\Positioner;
|
||||
|
||||
use Dompdf\FrameDecorator\AbstractFrameDecorator;
|
||||
|
||||
/**
|
||||
* Dummy positioner
|
||||
*
|
||||
* @package dompdf
|
||||
*/
|
||||
class NullPositioner extends AbstractPositioner
|
||||
{
|
||||
|
||||
/**
|
||||
* @param AbstractFrameDecorator $frame
|
||||
*/
|
||||
function position(AbstractFrameDecorator $frame): void
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
29
system/vendor/pancakeapp/dompdf/src/Positioner/TableCell.php
vendored
Executable file
29
system/vendor/pancakeapp/dompdf/src/Positioner/TableCell.php
vendored
Executable file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
/**
|
||||
* @package dompdf
|
||||
* @link https://github.com/dompdf/dompdf
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
*/
|
||||
namespace Dompdf\Positioner;
|
||||
|
||||
use Dompdf\FrameDecorator\AbstractFrameDecorator;
|
||||
use Dompdf\FrameDecorator\Table;
|
||||
|
||||
/**
|
||||
* Positions table cells
|
||||
*
|
||||
* @package dompdf
|
||||
*/
|
||||
class TableCell extends AbstractPositioner
|
||||
{
|
||||
|
||||
/**
|
||||
* @param AbstractFrameDecorator $frame
|
||||
*/
|
||||
function position(AbstractFrameDecorator $frame): void
|
||||
{
|
||||
$table = Table::find_parent_table($frame);
|
||||
$cellmap = $table->get_cellmap();
|
||||
$frame->set_position($cellmap->get_frame_position($frame));
|
||||
}
|
||||
}
|
||||
34
system/vendor/pancakeapp/dompdf/src/Positioner/TableRow.php
vendored
Executable file
34
system/vendor/pancakeapp/dompdf/src/Positioner/TableRow.php
vendored
Executable file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
/**
|
||||
* @package dompdf
|
||||
* @link https://github.com/dompdf/dompdf
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
*/
|
||||
namespace Dompdf\Positioner;
|
||||
|
||||
use Dompdf\FrameDecorator\AbstractFrameDecorator;
|
||||
|
||||
/**
|
||||
* Positions table rows
|
||||
*
|
||||
* @package dompdf
|
||||
*/
|
||||
class TableRow extends AbstractPositioner
|
||||
{
|
||||
|
||||
/**
|
||||
* @param AbstractFrameDecorator $frame
|
||||
*/
|
||||
function position(AbstractFrameDecorator $frame): void
|
||||
{
|
||||
$cb = $frame->get_containing_block();
|
||||
$p = $frame->get_prev_sibling();
|
||||
|
||||
if ($p) {
|
||||
$y = $p->get_position("y") + $p->get_margin_height();
|
||||
} else {
|
||||
$y = $cb["y"];
|
||||
}
|
||||
$frame->set_position($cb["x"], $y);
|
||||
}
|
||||
}
|
||||
291
system/vendor/pancakeapp/dompdf/src/Renderer.php
vendored
Executable file
291
system/vendor/pancakeapp/dompdf/src/Renderer.php
vendored
Executable file
@@ -0,0 +1,291 @@
|
||||
<?php
|
||||
/**
|
||||
* @package dompdf
|
||||
* @link https://github.com/dompdf/dompdf
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
*/
|
||||
namespace Dompdf;
|
||||
|
||||
use Dompdf\Renderer\AbstractRenderer;
|
||||
use Dompdf\Renderer\Block;
|
||||
use Dompdf\Renderer\Image;
|
||||
use Dompdf\Renderer\ListBullet;
|
||||
use Dompdf\Renderer\TableCell;
|
||||
use Dompdf\Renderer\TableRowGroup;
|
||||
use Dompdf\Renderer\Text;
|
||||
|
||||
/**
|
||||
* Concrete renderer
|
||||
*
|
||||
* Instantiates several specific renderers in order to render any given frame.
|
||||
*
|
||||
* @package dompdf
|
||||
*/
|
||||
class Renderer extends AbstractRenderer
|
||||
{
|
||||
|
||||
/**
|
||||
* Array of renderers for specific frame types
|
||||
*
|
||||
* @var AbstractRenderer[]
|
||||
*/
|
||||
protected $_renderers;
|
||||
|
||||
/**
|
||||
* Cache of the callbacks array
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $_callbacks;
|
||||
|
||||
/**
|
||||
* Advance the canvas to the next page
|
||||
*/
|
||||
function new_page()
|
||||
{
|
||||
$this->_canvas->new_page();
|
||||
}
|
||||
|
||||
/**
|
||||
* Render frames recursively
|
||||
*
|
||||
* @param Frame $frame the frame to render
|
||||
*/
|
||||
public function render(Frame $frame)
|
||||
{
|
||||
global $_dompdf_debug;
|
||||
|
||||
$this->_check_callbacks("begin_frame", $frame);
|
||||
|
||||
if ($_dompdf_debug) {
|
||||
echo $frame;
|
||||
flush();
|
||||
}
|
||||
|
||||
$style = $frame->get_style();
|
||||
|
||||
if (in_array($style->visibility, ["hidden", "collapse"], true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$display = $style->display;
|
||||
$transformList = $style->transform;
|
||||
$hasTransform = $transformList !== [];
|
||||
|
||||
// Starts the CSS transformation
|
||||
if ($hasTransform) {
|
||||
$this->_canvas->save();
|
||||
list($x, $y) = $frame->get_padding_box();
|
||||
$origin = $style->transform_origin;
|
||||
|
||||
foreach ($transformList as $transform) {
|
||||
list($function, $values) = $transform;
|
||||
if ($function === "matrix") {
|
||||
$function = "transform";
|
||||
}
|
||||
|
||||
$values = array_map("floatval", $values);
|
||||
$values[] = $x + (float)$style->length_in_pt($origin[0], (float)$style->length_in_pt($style->width));
|
||||
$values[] = $y + (float)$style->length_in_pt($origin[1], (float)$style->length_in_pt($style->height));
|
||||
|
||||
call_user_func_array([$this->_canvas, $function], $values);
|
||||
}
|
||||
}
|
||||
|
||||
switch ($display) {
|
||||
|
||||
case "block":
|
||||
case "list-item":
|
||||
case "inline-block":
|
||||
case "table":
|
||||
case "inline-table":
|
||||
$this->_render_frame("block", $frame);
|
||||
break;
|
||||
|
||||
case "inline":
|
||||
if ($frame->is_text_node()) {
|
||||
$this->_render_frame("text", $frame);
|
||||
} else {
|
||||
$this->_render_frame("inline", $frame);
|
||||
}
|
||||
break;
|
||||
|
||||
case "table-cell":
|
||||
$this->_render_frame("table-cell", $frame);
|
||||
break;
|
||||
|
||||
case "table-row-group":
|
||||
case "table-header-group":
|
||||
case "table-footer-group":
|
||||
$this->_render_frame("table-row-group", $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;
|
||||
|
||||
}
|
||||
|
||||
// Starts the overflow: hidden box
|
||||
if ($style->overflow === "hidden") {
|
||||
$padding_box = $frame->get_padding_box();
|
||||
[$x, $y, $w, $h] = $padding_box;
|
||||
$style = $frame->get_style();
|
||||
|
||||
if ($style->has_border_radius()) {
|
||||
$border_box = $frame->get_border_box();
|
||||
[$tl, $tr, $br, $bl] = $style->resolve_border_radius($border_box, $padding_box);
|
||||
$this->_canvas->clipping_roundrectangle($x, $y, $w, $h, $tl, $tr, $br, $bl);
|
||||
} else {
|
||||
$this->_canvas->clipping_rectangle($x, $y, $w, $h);
|
||||
}
|
||||
}
|
||||
|
||||
$stack = [];
|
||||
|
||||
foreach ($frame->get_children() as $child) {
|
||||
// < 0 : negative z-index
|
||||
// = 0 : no z-index, no stacking context
|
||||
// = 1 : stacking context without z-index
|
||||
// > 1 : z-index
|
||||
$child_style = $child->get_style();
|
||||
$child_z_index = $child_style->z_index;
|
||||
$z_index = 0;
|
||||
|
||||
if ($child_z_index !== "auto") {
|
||||
$z_index = $child_z_index + 1;
|
||||
} elseif ($child_style->float !== "none" || $child->is_positioned()) {
|
||||
$z_index = 1;
|
||||
}
|
||||
|
||||
$stack[$z_index][] = $child;
|
||||
}
|
||||
|
||||
ksort($stack);
|
||||
|
||||
foreach ($stack as $by_index) {
|
||||
foreach ($by_index as $child) {
|
||||
$this->render($child);
|
||||
}
|
||||
}
|
||||
|
||||
// Ends the overflow: hidden box
|
||||
if ($style->overflow === "hidden") {
|
||||
$this->_canvas->clipping_end();
|
||||
}
|
||||
|
||||
if ($hasTransform) {
|
||||
$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(string $event, Frame $frame): void
|
||||
{
|
||||
if (!isset($this->_callbacks)) {
|
||||
$this->_callbacks = $this->_dompdf->getCallbacks();
|
||||
}
|
||||
|
||||
if (isset($this->_callbacks[$event])) {
|
||||
$fs = $this->_callbacks[$event];
|
||||
$canvas = $this->_canvas;
|
||||
$fontMetrics = $this->_dompdf->getFontMetrics();
|
||||
|
||||
foreach ($fs as $f) {
|
||||
$f($frame, $canvas, $fontMetrics);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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($this->_dompdf);
|
||||
break;
|
||||
|
||||
case "inline":
|
||||
$this->_renderers[$type] = new Renderer\Inline($this->_dompdf);
|
||||
break;
|
||||
|
||||
case "text":
|
||||
$this->_renderers[$type] = new Text($this->_dompdf);
|
||||
break;
|
||||
|
||||
case "image":
|
||||
$this->_renderers[$type] = new Image($this->_dompdf);
|
||||
break;
|
||||
|
||||
case "table-cell":
|
||||
$this->_renderers[$type] = new TableCell($this->_dompdf);
|
||||
break;
|
||||
|
||||
case "table-row-group":
|
||||
$this->_renderers[$type] = new TableRowGroup($this->_dompdf);
|
||||
break;
|
||||
|
||||
case "list-bullet":
|
||||
$this->_renderers[$type] = new ListBullet($this->_dompdf);
|
||||
break;
|
||||
|
||||
case "php":
|
||||
$this->_renderers[$type] = new PhpEvaluator($this->_canvas);
|
||||
break;
|
||||
|
||||
case "javascript":
|
||||
$this->_renderers[$type] = new JavascriptEmbedder($this->_dompdf);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
$this->_renderers[$type]->render($frame);
|
||||
}
|
||||
}
|
||||
1244
system/vendor/pancakeapp/dompdf/src/Renderer/AbstractRenderer.php
vendored
Executable file
1244
system/vendor/pancakeapp/dompdf/src/Renderer/AbstractRenderer.php
vendored
Executable file
File diff suppressed because it is too large
Load Diff
88
system/vendor/pancakeapp/dompdf/src/Renderer/Block.php
vendored
Executable file
88
system/vendor/pancakeapp/dompdf/src/Renderer/Block.php
vendored
Executable file
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
/**
|
||||
* @package dompdf
|
||||
* @link https://github.com/dompdf/dompdf
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
*/
|
||||
namespace Dompdf\Renderer;
|
||||
|
||||
use Dompdf\Frame;
|
||||
use Dompdf\FrameDecorator\Block as BlockFrameDecorator;
|
||||
use Dompdf\Helpers;
|
||||
|
||||
/**
|
||||
* Renders block frames
|
||||
*
|
||||
* @package dompdf
|
||||
*/
|
||||
class Block extends AbstractRenderer
|
||||
{
|
||||
|
||||
/**
|
||||
* @param Frame $frame
|
||||
*/
|
||||
function render(Frame $frame)
|
||||
{
|
||||
$style = $frame->get_style();
|
||||
$node = $frame->get_node();
|
||||
$dompdf = $this->_dompdf;
|
||||
|
||||
$this->_set_opacity($frame->get_opacity($style->opacity));
|
||||
|
||||
[$x, $y, $w, $h] = $frame->get_border_box();
|
||||
|
||||
if ($node->nodeName === "body") {
|
||||
// Margins should be fully resolved at this point
|
||||
$mt = $style->margin_top;
|
||||
$mb = $style->margin_bottom;
|
||||
$h = $frame->get_containing_block("h") - $mt - $mb;
|
||||
}
|
||||
|
||||
$border_box = [$x, $y, $w, $h];
|
||||
|
||||
// Draw our background, border and content
|
||||
$this->_render_background($frame, $border_box);
|
||||
$this->_render_border($frame, $border_box);
|
||||
$this->_render_outline($frame, $border_box);
|
||||
|
||||
// Handle anchors & links
|
||||
if ($node->nodeName === "a" && $href = $node->getAttribute("href")) {
|
||||
$href = Helpers::build_url($dompdf->getProtocol(), $dompdf->getBaseHost(), $dompdf->getBasePath(), $href) ?? $href;
|
||||
$this->_canvas->add_link($href, $x, $y, $w, $h);
|
||||
}
|
||||
|
||||
$id = $frame->get_node()->getAttribute("id");
|
||||
if (strlen($id) > 0) {
|
||||
$this->_canvas->add_named_dest($id);
|
||||
}
|
||||
|
||||
$this->debugBlockLayout($frame, "red", false);
|
||||
}
|
||||
|
||||
protected function debugBlockLayout(Frame $frame, ?string $color, bool $lines = false): void
|
||||
{
|
||||
$options = $this->_dompdf->getOptions();
|
||||
$debugLayout = $options->getDebugLayout();
|
||||
|
||||
if (!$debugLayout) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($color && $options->getDebugLayoutBlocks()) {
|
||||
$this->_debug_layout($frame->get_border_box(), $color);
|
||||
|
||||
if ($options->getDebugLayoutPaddingBox()) {
|
||||
$this->_debug_layout($frame->get_padding_box(), $color, [0.5, 0.5]);
|
||||
}
|
||||
}
|
||||
|
||||
if ($lines && $options->getDebugLayoutLines() && $frame instanceof BlockFrameDecorator) {
|
||||
[$cx, , $cw] = $frame->get_content_box();
|
||||
|
||||
foreach ($frame->get_line_boxes() as $line) {
|
||||
$lw = $cw - $line->left - $line->right;
|
||||
$this->_debug_layout([$cx + $line->left, $line->y, $lw, $line->h], "orange");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
90
system/vendor/pancakeapp/dompdf/src/Renderer/Image.php
vendored
Executable file
90
system/vendor/pancakeapp/dompdf/src/Renderer/Image.php
vendored
Executable file
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
/**
|
||||
* @package dompdf
|
||||
* @link https://github.com/dompdf/dompdf
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
*/
|
||||
namespace Dompdf\Renderer;
|
||||
|
||||
use Dompdf\Frame;
|
||||
use Dompdf\FrameDecorator\Image as ImageFrameDecorator;
|
||||
use Dompdf\Image\Cache;
|
||||
|
||||
/**
|
||||
* Image renderer
|
||||
*
|
||||
* @package dompdf
|
||||
*/
|
||||
class Image extends Block
|
||||
{
|
||||
/**
|
||||
* @param ImageFrameDecorator $frame
|
||||
*/
|
||||
function render(Frame $frame)
|
||||
{
|
||||
$style = $frame->get_style();
|
||||
$border_box = $frame->get_border_box();
|
||||
|
||||
$this->_set_opacity($frame->get_opacity($style->opacity));
|
||||
|
||||
// Render background & borders
|
||||
$this->_render_background($frame, $border_box);
|
||||
$this->_render_border($frame, $border_box);
|
||||
$this->_render_outline($frame, $border_box);
|
||||
|
||||
$content_box = $frame->get_content_box();
|
||||
[$x, $y, $w, $h] = $content_box;
|
||||
|
||||
$src = $frame->get_image_url();
|
||||
$alt = null;
|
||||
|
||||
if (Cache::is_broken($src) &&
|
||||
$alt = $frame->get_node()->getAttribute("alt")
|
||||
) {
|
||||
$font = $style->font_family;
|
||||
$size = $style->font_size;
|
||||
$word_spacing = $style->word_spacing;
|
||||
$letter_spacing = $style->letter_spacing;
|
||||
|
||||
$this->_canvas->text(
|
||||
$x,
|
||||
$y,
|
||||
$alt,
|
||||
$font,
|
||||
$size,
|
||||
$style->color,
|
||||
$word_spacing,
|
||||
$letter_spacing
|
||||
);
|
||||
} elseif ($w > 0 && $h > 0) {
|
||||
if ($style->has_border_radius()) {
|
||||
[$tl, $tr, $br, $bl] = $style->resolve_border_radius($border_box, $content_box);
|
||||
$this->_canvas->clipping_roundrectangle($x, $y, $w, $h, $tl, $tr, $br, $bl);
|
||||
}
|
||||
|
||||
$this->_canvas->image($src, $x, $y, $w, $h, $style->image_resolution);
|
||||
|
||||
if ($style->has_border_radius()) {
|
||||
$this->_canvas->clipping_end();
|
||||
}
|
||||
}
|
||||
|
||||
if ($msg = $frame->get_image_msg()) {
|
||||
$parts = preg_split("/\s*\n\s*/", $msg);
|
||||
$font = $style->font_family;
|
||||
$height = 10;
|
||||
$_y = $alt ? $y + $h - count($parts) * $height : $y;
|
||||
|
||||
foreach ($parts as $i => $_part) {
|
||||
$this->_canvas->text($x, $_y + $i * $height, $_part, $font, $height * 0.8, [0.5, 0.5, 0.5]);
|
||||
}
|
||||
}
|
||||
|
||||
$id = $frame->get_node()->getAttribute("id");
|
||||
if (strlen($id) > 0) {
|
||||
$this->_canvas->add_named_dest($id);
|
||||
}
|
||||
|
||||
$this->debugBlockLayout($frame, "blue");
|
||||
}
|
||||
}
|
||||
126
system/vendor/pancakeapp/dompdf/src/Renderer/Inline.php
vendored
Executable file
126
system/vendor/pancakeapp/dompdf/src/Renderer/Inline.php
vendored
Executable file
@@ -0,0 +1,126 @@
|
||||
<?php
|
||||
/**
|
||||
* @package dompdf
|
||||
* @link https://github.com/dompdf/dompdf
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
*/
|
||||
namespace Dompdf\Renderer;
|
||||
|
||||
use Dompdf\Frame;
|
||||
use Dompdf\Helpers;
|
||||
|
||||
/**
|
||||
* Renders inline frames
|
||||
*
|
||||
* @package dompdf
|
||||
*/
|
||||
class Inline extends AbstractRenderer
|
||||
{
|
||||
function render(Frame $frame)
|
||||
{
|
||||
if (!$frame->get_first_child()) {
|
||||
return; // No children, no service
|
||||
}
|
||||
|
||||
$style = $frame->get_style();
|
||||
$dompdf = $this->_dompdf;
|
||||
|
||||
$this->_set_opacity($frame->get_opacity($style->opacity));
|
||||
|
||||
$do_debug_layout_line = $dompdf->getOptions()->getDebugLayout()
|
||||
&& $dompdf->getOptions()->getDebugLayoutInline();
|
||||
|
||||
// Draw the background & border behind each child. To do this we need
|
||||
// to figure out just how much space each child takes:
|
||||
[$x, $y] = $frame->get_first_child()->get_position();
|
||||
[$w, $h] = $this->get_child_size($frame, $do_debug_layout_line);
|
||||
|
||||
[, , $cbw] = $frame->get_containing_block();
|
||||
$margin_left = $style->length_in_pt($style->margin_left, $cbw);
|
||||
$pt = $style->length_in_pt($style->padding_top, $cbw);
|
||||
$pb = $style->length_in_pt($style->padding_bottom, $cbw);
|
||||
|
||||
// Make sure that border and background start inside the left margin
|
||||
// Extend the drawn box by border and padding in vertical direction, as
|
||||
// these do not affect layout
|
||||
// FIXME: Using a small vertical offset of a fraction of the height here
|
||||
// to work around the vertical position being slightly off in general
|
||||
$x += $margin_left;
|
||||
$y -= $style->border_top_width + $pt - ($h * 0.1);
|
||||
$w += $style->border_left_width + $style->border_right_width;
|
||||
$h += $style->border_top_width + $pt + $style->border_bottom_width + $pb;
|
||||
|
||||
$border_box = [$x, $y, $w, $h];
|
||||
$this->_render_background($frame, $border_box);
|
||||
$this->_render_border($frame, $border_box);
|
||||
$this->_render_outline($frame, $border_box);
|
||||
|
||||
$node = $frame->get_node();
|
||||
$id = $node->getAttribute("id");
|
||||
if (strlen($id) > 0) {
|
||||
$this->_canvas->add_named_dest($id);
|
||||
}
|
||||
|
||||
// Only two levels of links frames
|
||||
$is_link_node = $node->nodeName === "a";
|
||||
if ($is_link_node) {
|
||||
if (($name = $node->getAttribute("name"))) {
|
||||
$this->_canvas->add_named_dest($name);
|
||||
}
|
||||
}
|
||||
|
||||
if ($frame->get_parent() && $frame->get_parent()->get_node()->nodeName === "a") {
|
||||
$link_node = $frame->get_parent()->get_node();
|
||||
}
|
||||
|
||||
// Handle anchors & links
|
||||
if ($is_link_node) {
|
||||
if ($href = $node->getAttribute("href")) {
|
||||
$href = Helpers::build_url($dompdf->getProtocol(), $dompdf->getBaseHost(), $dompdf->getBasePath(), $href) ?? $href;
|
||||
$this->_canvas->add_link($href, $x, $y, $w, $h);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function get_child_size(Frame $frame, bool $do_debug_layout_line): array
|
||||
{
|
||||
$w = 0.0;
|
||||
$h = 0.0;
|
||||
|
||||
foreach ($frame->get_children() as $child) {
|
||||
if ($child->get_node()->nodeValue === " " && $child->get_prev_sibling() && !$child->get_next_sibling()) {
|
||||
break;
|
||||
}
|
||||
|
||||
$style = $child->get_style();
|
||||
$auto_width = $style->width === "auto";
|
||||
$auto_height = $style->height === "auto";
|
||||
[, , $child_w, $child_h] = $child->get_padding_box();
|
||||
|
||||
if ($auto_width || $auto_height) {
|
||||
[$child_w2, $child_h2] = $this->get_child_size($child, $do_debug_layout_line);
|
||||
|
||||
if ($auto_width) {
|
||||
$child_w = $child_w2;
|
||||
}
|
||||
|
||||
if ($auto_height) {
|
||||
$child_h = $child_h2;
|
||||
}
|
||||
}
|
||||
|
||||
$w += $child_w;
|
||||
$h = max($h, $child_h);
|
||||
|
||||
if ($do_debug_layout_line) {
|
||||
$this->_debug_layout($child->get_border_box(), "blue");
|
||||
|
||||
if ($this->_dompdf->getOptions()->getDebugLayoutPaddingBox()) {
|
||||
$this->_debug_layout($child->get_padding_box(), "blue", [0.5, 0.5]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [$w, $h];
|
||||
}
|
||||
}
|
||||
235
system/vendor/pancakeapp/dompdf/src/Renderer/ListBullet.php
vendored
Executable file
235
system/vendor/pancakeapp/dompdf/src/Renderer/ListBullet.php
vendored
Executable file
@@ -0,0 +1,235 @@
|
||||
<?php
|
||||
/**
|
||||
* @package dompdf
|
||||
* @link https://github.com/dompdf/dompdf
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
*/
|
||||
namespace Dompdf\Renderer;
|
||||
|
||||
use Dompdf\Helpers;
|
||||
use Dompdf\Frame;
|
||||
use Dompdf\FrameDecorator\ListBullet as ListBulletFrameDecorator;
|
||||
use Dompdf\FrameDecorator\ListBulletImage;
|
||||
use Dompdf\Image\Cache;
|
||||
|
||||
/**
|
||||
* Renders list bullets
|
||||
*
|
||||
* @package dompdf
|
||||
*/
|
||||
class ListBullet extends AbstractRenderer
|
||||
{
|
||||
/**
|
||||
* @param $type
|
||||
* @return mixed|string
|
||||
*/
|
||||
static function get_counter_chars($type)
|
||||
{
|
||||
static $cache = [];
|
||||
|
||||
if (isset($cache[$type])) {
|
||||
return $cache[$type];
|
||||
}
|
||||
|
||||
$uppercase = false;
|
||||
$text = "";
|
||||
|
||||
switch ($type) {
|
||||
case "decimal-leading-zero":
|
||||
case "decimal":
|
||||
case "1":
|
||||
return "0123456789";
|
||||
|
||||
case "upper-alpha":
|
||||
case "upper-latin":
|
||||
case "A":
|
||||
$uppercase = true;
|
||||
case "lower-alpha":
|
||||
case "lower-latin":
|
||||
case "a":
|
||||
$text = "abcdefghijklmnopqrstuvwxyz";
|
||||
break;
|
||||
|
||||
case "upper-roman":
|
||||
case "I":
|
||||
$uppercase = true;
|
||||
case "lower-roman":
|
||||
case "i":
|
||||
$text = "ivxlcdm";
|
||||
break;
|
||||
|
||||
case "lower-greek":
|
||||
for ($i = 0; $i < 24; $i++) {
|
||||
$text .= Helpers::unichr($i + 944);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if ($uppercase) {
|
||||
$text = strtoupper($text);
|
||||
}
|
||||
|
||||
return $cache[$type] = "$text.";
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $n
|
||||
* @param string $type
|
||||
* @param int|null $pad
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
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 - 1) % 26) + ord('a'));
|
||||
break;
|
||||
|
||||
case "upper-roman":
|
||||
case "I":
|
||||
$uppercase = true;
|
||||
case "lower-roman":
|
||||
case "i":
|
||||
$text = Helpers::dec2roman($n);
|
||||
break;
|
||||
|
||||
case "lower-greek":
|
||||
$text = Helpers::unichr($n + 944);
|
||||
break;
|
||||
}
|
||||
|
||||
if ($uppercase) {
|
||||
$text = strtoupper($text);
|
||||
}
|
||||
|
||||
return "$text.";
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ListBulletFrameDecorator $frame
|
||||
*/
|
||||
function render(Frame $frame)
|
||||
{
|
||||
$li = $frame->get_parent();
|
||||
$style = $frame->get_style();
|
||||
|
||||
$this->_set_opacity($frame->get_opacity($style->opacity));
|
||||
|
||||
// Don't render bullets twice if the list item was split
|
||||
if ($li->is_split_off) {
|
||||
return;
|
||||
}
|
||||
|
||||
$font_family = $style->font_family;
|
||||
$font_size = $style->font_size;
|
||||
$baseline = $this->_canvas->get_font_baseline($font_family, $font_size);
|
||||
|
||||
// Handle list-style-image
|
||||
// If list style image is requested but missing, fall back to predefined types
|
||||
if ($frame instanceof ListBulletImage && !Cache::is_broken($img = $frame->get_image_url())) {
|
||||
[$x, $y] = $frame->get_position();
|
||||
$w = $frame->get_width();
|
||||
$h = $frame->get_height();
|
||||
$y += $baseline - $h;
|
||||
|
||||
$this->_canvas->image($img, $x, $y, $w, $h);
|
||||
} else {
|
||||
$bullet_style = $style->list_style_type;
|
||||
|
||||
switch ($bullet_style) {
|
||||
default:
|
||||
case "disc":
|
||||
case "circle":
|
||||
[$x, $y] = $frame->get_position();
|
||||
$offset = $font_size * ListBulletFrameDecorator::BULLET_OFFSET;
|
||||
$r = ($font_size * ListBulletFrameDecorator::BULLET_SIZE) / 2;
|
||||
$x += $r;
|
||||
$y += $baseline - $r - $offset;
|
||||
$o = $font_size * ListBulletFrameDecorator::BULLET_THICKNESS;
|
||||
$this->_canvas->circle($x, $y, $r, $style->color, $o, null, $bullet_style !== "circle");
|
||||
break;
|
||||
|
||||
case "square":
|
||||
[$x, $y] = $frame->get_position();
|
||||
$offset = $font_size * ListBulletFrameDecorator::BULLET_OFFSET;
|
||||
$w = $font_size * ListBulletFrameDecorator::BULLET_SIZE;
|
||||
$y += $baseline - $w - $offset;
|
||||
$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":
|
||||
$pad = null;
|
||||
if ($bullet_style === "decimal-leading-zero") {
|
||||
$pad = strlen($li->get_parent()->get_node()->getAttribute("dompdf-children-count"));
|
||||
}
|
||||
|
||||
$node = $frame->get_node();
|
||||
|
||||
if (!$node->hasAttribute("dompdf-counter")) {
|
||||
return;
|
||||
}
|
||||
|
||||
$index = $node->getAttribute("dompdf-counter");
|
||||
$text = $this->make_counter($index, $bullet_style, $pad);
|
||||
|
||||
if (trim($text) === "") {
|
||||
return;
|
||||
}
|
||||
|
||||
$word_spacing = $style->word_spacing;
|
||||
$letter_spacing = $style->letter_spacing;
|
||||
$text_width = $this->_dompdf->getFontMetrics()->getTextWidth($text, $font_family, $font_size, $word_spacing, $letter_spacing);
|
||||
|
||||
[$x, $y] = $frame->get_position();
|
||||
// Correct for static frame width applied by positioner
|
||||
$x += $frame->get_width() - $text_width;
|
||||
|
||||
$this->_canvas->text($x, $y, $text,
|
||||
$font_family, $font_size,
|
||||
$style->color, $word_spacing, $letter_spacing);
|
||||
|
||||
case "none":
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$id = $frame->get_node()->getAttribute("id");
|
||||
if (strlen($id) > 0) {
|
||||
$this->_canvas->add_named_dest($id);
|
||||
}
|
||||
}
|
||||
}
|
||||
188
system/vendor/pancakeapp/dompdf/src/Renderer/TableCell.php
vendored
Executable file
188
system/vendor/pancakeapp/dompdf/src/Renderer/TableCell.php
vendored
Executable file
@@ -0,0 +1,188 @@
|
||||
<?php
|
||||
/**
|
||||
* @package dompdf
|
||||
* @link https://github.com/dompdf/dompdf
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
*/
|
||||
namespace Dompdf\Renderer;
|
||||
|
||||
use Dompdf\Frame;
|
||||
use Dompdf\FrameDecorator\Table;
|
||||
|
||||
/**
|
||||
* Renders table cells
|
||||
*
|
||||
* @package dompdf
|
||||
*/
|
||||
class TableCell extends Block
|
||||
{
|
||||
|
||||
/**
|
||||
* @param Frame $frame
|
||||
*/
|
||||
function render(Frame $frame)
|
||||
{
|
||||
$style = $frame->get_style();
|
||||
|
||||
if (trim($frame->get_node()->nodeValue) === "" && $style->empty_cells === "hide") {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->_set_opacity($frame->get_opacity($style->opacity));
|
||||
|
||||
$border_box = $frame->get_border_box();
|
||||
$table = Table::find_parent_table($frame);
|
||||
|
||||
if ($table->get_style()->border_collapse !== "collapse") {
|
||||
$this->_render_background($frame, $border_box);
|
||||
$this->_render_border($frame, $border_box);
|
||||
$this->_render_outline($frame, $border_box);
|
||||
} else {
|
||||
// The collapsed case is slightly complicated...
|
||||
|
||||
$cells = $table->get_cellmap()->get_spanned_cells($frame);
|
||||
|
||||
if (is_null($cells)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Render the background to the padding box, as the cells are
|
||||
// rendered individually one after another, and we don't want the
|
||||
// background to overlap an adjacent border
|
||||
$padding_box = $frame->get_padding_box();
|
||||
|
||||
$this->_render_background($frame, $padding_box);
|
||||
$this->_render_collapsed_border($frame, $table);
|
||||
|
||||
// FIXME: Outline should be drawn over other cells
|
||||
$this->_render_outline($frame, $border_box);
|
||||
}
|
||||
|
||||
$id = $frame->get_node()->getAttribute("id");
|
||||
if (strlen($id) > 0) {
|
||||
$this->_canvas->add_named_dest($id);
|
||||
}
|
||||
|
||||
// $this->debugBlockLayout($frame, "red", false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Frame $frame
|
||||
* @param Table $table
|
||||
*/
|
||||
protected function _render_collapsed_border(Frame $frame, Table $table): void
|
||||
{
|
||||
$cellmap = $table->get_cellmap();
|
||||
$cells = $cellmap->get_spanned_cells($frame);
|
||||
$num_rows = $cellmap->get_num_rows();
|
||||
$num_cols = $cellmap->get_num_cols();
|
||||
|
||||
[$table_x, $table_y] = $table->get_position();
|
||||
|
||||
// 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);
|
||||
$col = $cellmap->get_column($j);
|
||||
|
||||
$x = $table_x + $col["x"] - $bp["left"]["width"] / 2;
|
||||
$y = $table_y + $top_row["y"] - $bp["top"]["width"] / 2;
|
||||
$w = $col["used-width"] + ($bp["left"]["width"] + $bp["right"]["width"]) / 2;
|
||||
|
||||
if ($bp["top"]["width"] > 0) {
|
||||
$widths = [
|
||||
(float)$bp["top"]["width"],
|
||||
(float)$bp["right"]["width"],
|
||||
(float)$bp["bottom"]["width"],
|
||||
(float)$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"]["width"] <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$widths = [
|
||||
(float)$bp["top"]["width"],
|
||||
(float)$bp["right"]["width"],
|
||||
(float)$bp["bottom"]["width"],
|
||||
(float)$bp["left"]["width"]
|
||||
];
|
||||
|
||||
$y = $table_y + $bottom_row["y"] + $bottom_row["height"] + $bp["bottom"]["width"] / 2;
|
||||
|
||||
$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);
|
||||
$row = $cellmap->get_row($i);
|
||||
|
||||
$x = $table_x + $left_col["x"] - $bp["left"]["width"] / 2;
|
||||
$y = $table_y + $row["y"] - $bp["top"]["width"] / 2;
|
||||
$h = $row["height"] + ($bp["top"]["width"] + $bp["bottom"]["width"]) / 2;
|
||||
|
||||
if ($bp["left"]["width"] > 0) {
|
||||
$widths = [
|
||||
(float)$bp["top"]["width"],
|
||||
(float)$bp["right"]["width"],
|
||||
(float)$bp["bottom"]["width"],
|
||||
(float)$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"]["width"] <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$widths = [
|
||||
(float)$bp["top"]["width"],
|
||||
(float)$bp["right"]["width"],
|
||||
(float)$bp["bottom"]["width"],
|
||||
(float)$bp["left"]["width"]
|
||||
];
|
||||
|
||||
$x = $table_x + $right_col["x"] + $right_col["used-width"] + $bp["right"]["width"] / 2;
|
||||
|
||||
$method = "_border_" . $bp["right"]["style"];
|
||||
$this->$method($x, $y, $h, $bp["right"]["color"], $widths, "right", "square");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
40
system/vendor/pancakeapp/dompdf/src/Renderer/TableRowGroup.php
vendored
Executable file
40
system/vendor/pancakeapp/dompdf/src/Renderer/TableRowGroup.php
vendored
Executable file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
/**
|
||||
* @package dompdf
|
||||
* @link https://github.com/dompdf/dompdf
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
*/
|
||||
namespace Dompdf\Renderer;
|
||||
|
||||
use Dompdf\Frame;
|
||||
|
||||
/**
|
||||
* Renders block frames
|
||||
*
|
||||
* @package dompdf
|
||||
*/
|
||||
class TableRowGroup extends Block
|
||||
{
|
||||
|
||||
/**
|
||||
* @param Frame $frame
|
||||
*/
|
||||
function render(Frame $frame)
|
||||
{
|
||||
$style = $frame->get_style();
|
||||
|
||||
$this->_set_opacity($frame->get_opacity($style->opacity));
|
||||
|
||||
$border_box = $frame->get_border_box();
|
||||
|
||||
$this->_render_border($frame, $border_box);
|
||||
$this->_render_outline($frame, $border_box);
|
||||
|
||||
$id = $frame->get_node()->getAttribute("id");
|
||||
if (strlen($id) > 0) {
|
||||
$this->_canvas->add_named_dest($id);
|
||||
}
|
||||
|
||||
$this->debugBlockLayout($frame, "red");
|
||||
}
|
||||
}
|
||||
158
system/vendor/pancakeapp/dompdf/src/Renderer/Text.php
vendored
Executable file
158
system/vendor/pancakeapp/dompdf/src/Renderer/Text.php
vendored
Executable file
@@ -0,0 +1,158 @@
|
||||
<?php
|
||||
/**
|
||||
* @package dompdf
|
||||
* @link https://github.com/dompdf/dompdf
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
*/
|
||||
namespace Dompdf\Renderer;
|
||||
|
||||
use Dompdf\Adapter\CPDF;
|
||||
use Dompdf\Frame;
|
||||
|
||||
/**
|
||||
* Renders text frames
|
||||
*
|
||||
* @package dompdf
|
||||
*/
|
||||
class Text extends AbstractRenderer
|
||||
{
|
||||
/** Thickness of underline. Screen: 0.08, print: better less, e.g. 0.04 */
|
||||
const DECO_THICKNESS = 0.02;
|
||||
|
||||
//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
|
||||
|
||||
/** Relative to bottom of text, as fraction of height */
|
||||
const UNDERLINE_OFFSET = 0.0;
|
||||
|
||||
/** Relative to top of text */
|
||||
const OVERLINE_OFFSET = 0.0;
|
||||
|
||||
/** Relative to centre of text. */
|
||||
const LINETHROUGH_OFFSET = 0.0;
|
||||
|
||||
/** How far to extend lines past either end, in pt */
|
||||
const DECO_EXTENSION = 0.0;
|
||||
|
||||
/**
|
||||
* @param \Dompdf\FrameDecorator\Text $frame
|
||||
*/
|
||||
function render(Frame $frame)
|
||||
{
|
||||
$style = $frame->get_style();
|
||||
$text = $frame->get_text();
|
||||
|
||||
if ($text === "") {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->_set_opacity($frame->get_opacity($style->opacity));
|
||||
|
||||
list($x, $y) = $frame->get_position();
|
||||
$cb = $frame->get_containing_block();
|
||||
|
||||
$ml = $style->margin_left;
|
||||
$pl = $style->padding_left;
|
||||
$bl = $style->border_left_width;
|
||||
$x += (float) $style->length_in_pt([$ml, $pl, $bl], $cb["w"]);
|
||||
|
||||
$font = $style->font_family;
|
||||
$size = $style->font_size;
|
||||
$frame_font_size = $frame->get_dompdf()->getFontMetrics()->getFontHeight($font, $size);
|
||||
$word_spacing = $frame->get_text_spacing() + $style->word_spacing;
|
||||
$letter_spacing = $style->letter_spacing;
|
||||
$width = (float) $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, $letter_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;
|
||||
}
|
||||
|
||||
$line_thickness = $size * self::DECO_THICKNESS;
|
||||
$underline_offset = $size * self::UNDERLINE_OFFSET;
|
||||
$overline_offset = $size * self::OVERLINE_OFFSET;
|
||||
$linethrough_offset = $size * self::LINETHROUGH_OFFSET;
|
||||
$underline_position = -0.08;
|
||||
|
||||
if ($this->_canvas instanceof CPDF) {
|
||||
$cpdf_font = $this->_canvas->get_cpdf()->fonts[$style->font_family];
|
||||
|
||||
if (isset($cpdf_font["UnderlinePosition"])) {
|
||||
$underline_position = $cpdf_font["UnderlinePosition"] / 1000;
|
||||
}
|
||||
|
||||
if (isset($cpdf_font["UnderlineThickness"])) {
|
||||
$line_thickness = $size * ($cpdf_font["UnderlineThickness"] / 1000);
|
||||
}
|
||||
}
|
||||
|
||||
$descent = $size * $underline_position;
|
||||
$base = $frame_font_size;
|
||||
|
||||
// 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 = [];
|
||||
while ($p = $p->get_parent()) {
|
||||
$stack[] = $p;
|
||||
}
|
||||
|
||||
while (isset($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 2;
|
||||
|
||||
case "underline":
|
||||
$deco_y += $base - $descent + $underline_offset + $line_thickness / 2;
|
||||
break;
|
||||
|
||||
case "overline":
|
||||
$deco_y += $overline_offset + $line_thickness / 2;
|
||||
break;
|
||||
|
||||
case "line-through":
|
||||
$deco_y += $base * 0.7 + $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, $line_thickness);
|
||||
}
|
||||
|
||||
if ($this->_dompdf->getOptions()->getDebugLayout() && $this->_dompdf->getOptions()->getDebugLayoutLines()) {
|
||||
$text_width = $this->_dompdf->getFontMetrics()->getTextWidth($text, $font, $size, $word_spacing, $letter_spacing);
|
||||
$this->_debug_layout([$x, $y, $text_width, $frame_font_size], "orange", [0.5, 0.5]);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user