Alpha for palette index 0: 1 byte
Alpha for palette index 1: 1 byte
...etc...
*/
// there will be one entry for each palette entry. up until the last non-opaque entry.
// set up an array, stretching over all palette entries which will be o (opaque) or 1 (transparent)
$transparency['type'] = 'indexed';
$trans = 0;
for ($i = $chunkLen; $i >= 0; $i--) {
if (ord($data[$p + 8 + $i]) == 0) {
$trans = $i;
}
}
$transparency['data'] = $trans;
break;
// grayscale
case 0:
/* corresponding to entries in the plte chunk
Gray: 2 bytes, range 0 .. (2^bitdepth)-1
*/
// $transparency['grayscale'] = $this->PRVT_getBytes($data,$p+8,2); // g = grayscale
$transparency['type'] = 'indexed';
$transparency['data'] = ord($data[$p + 8 + 1]);
break;
// truecolor
case 2:
/* corresponding to entries in the plte chunk
Red: 2 bytes, range 0 .. (2^bitdepth)-1
Green: 2 bytes, range 0 .. (2^bitdepth)-1
Blue: 2 bytes, range 0 .. (2^bitdepth)-1
*/
$transparency['r'] = $this->getBytes($data, $p + 8, 2);
// r from truecolor
$transparency['g'] = $this->getBytes($data, $p + 10, 2);
// g from truecolor
$transparency['b'] = $this->getBytes($data, $p + 12, 2);
// b from truecolor
$transparency['type'] = 'color-key';
break;
//unsupported transparency type
default:
if (defined("DEBUGPNG") && DEBUGPNG) {
print '[addPngFromFile unsupported transparency type ' . $file . ']';
}
break;
}
// KS End new code
break;
default:
break;
}
$p += $chunkLen + 12;
}
if (!$haveHeader) {
$error = 1;
//debugpng
if (defined("DEBUGPNG") && DEBUGPNG) {
print '[addPngFromFile information header is missing ' . $file . ']';
}
$errormsg = 'information header is missing';
}
if (isset($info['interlaceMethod']) && $info['interlaceMethod']) {
$error = 1;
//debugpng
if (defined("DEBUGPNG") && DEBUGPNG) {
print '[addPngFromFile no support for interlaced images in pdf ' . $file . ']';
}
$errormsg = 'There appears to be no support for interlaced images in pdf.';
}
}
if (!$error && $info['bitDepth'] > 8) {
$error = 1;
//debugpng
if (defined("DEBUGPNG") && DEBUGPNG) {
print '[addPngFromFile bit depth of 8 or less is supported ' . $file . ']';
}
$errormsg = 'only bit depth of 8 or less is supported';
}
if (!$error) {
switch ($info['colorType']) {
case 3:
$color = 'DeviceRGB';
$ncolor = 1;
break;
case 2:
$color = 'DeviceRGB';
$ncolor = 3;
break;
case 0:
$color = 'DeviceGray';
$ncolor = 1;
break;
default:
$error = 1;
//debugpng
if (defined("DEBUGPNG") && DEBUGPNG) {
print '[addPngFromFile alpha channel not supported: ' . $info['colorType'] . ' ' . $file . ']';
}
$errormsg = 'transparency alpha channel not supported, transparency only supported for palette images.';
}
}
if ($error) {
$this->addMessage('PNG error - (' . $file . ') ' . $errormsg);
return;
}
//print_r($info);
// so this image is ok... add it in.
$this->numImages++;
$im = $this->numImages;
$label = "I$im";
$this->numObj++;
// $this->o_image($this->numObj,'new',array('label' => $label,'data' => $idata,'iw' => $w,'ih' => $h,'type' => 'png','ic' => $info['width']));
$options = [
'label' => $label,
'data' => $idata,
'bitsPerComponent' => $info['bitDepth'],
'pdata' => $pdata,
'iw' => $info['width'],
'ih' => $info['height'],
'type' => 'png',
'color' => $color,
'ncolor' => $ncolor,
'masked' => $mask,
'isMask' => $is_mask
];
if (isset($transparency)) {
$options['transparency'] = $transparency;
}
$this->o_image($this->numObj, 'new', $options);
$this->imagelist[$file] = ['label' => $label, 'w' => $info['width'], 'h' => $info['height']];
}
if ($is_mask) {
return;
}
if ($w <= 0 && $h <= 0) {
$w = $info['width'];
$h = $info['height'];
}
if ($w <= 0) {
$w = $h / $info['height'] * $info['width'];
}
if ($h <= 0) {
$h = $w * $info['height'] / $info['width'];
}
$this->addContent(sprintf("\nq\n%.3F 0 0 %.3F %.3F %.3F cm /%s Do\nQ", $w, $h, $x, $y, $label));
}
/**
* add a JPEG image into the document, from a file
*
* @param $img
* @param $x
* @param $y
* @param int $w
* @param int $h
*/
function addJpegFromFile($img, $x, $y, $w = 0, $h = 0)
{
// attempt to add a jpeg image straight from a file, using no GD commands
// note that this function is unable to operate on a remote file.
if (!file_exists($img)) {
return;
}
if ($this->image_iscached($img)) {
$data = null;
$imageWidth = $this->imagelist[$img]['w'];
$imageHeight = $this->imagelist[$img]['h'];
$channels = $this->imagelist[$img]['c'];
} else {
$tmp = getimagesize($img);
$imageWidth = $tmp[0];
$imageHeight = $tmp[1];
if (isset($tmp['channels'])) {
$channels = $tmp['channels'];
} else {
$channels = 3;
}
$data = file_get_contents($img);
}
if ($w <= 0 && $h <= 0) {
$w = $imageWidth;
}
if ($w == 0) {
$w = $h / $imageHeight * $imageWidth;
}
if ($h == 0) {
$h = $w * $imageHeight / $imageWidth;
}
$this->addJpegImage_common($data, $img, $imageWidth, $imageHeight, $x, $y, $w, $h, $channels);
}
/**
* common code used by the two JPEG adding functions
* @param $data
* @param $imgname
* @param $imageWidth
* @param $imageHeight
* @param $x
* @param $y
* @param int $w
* @param int $h
* @param int $channels
*/
private function addJpegImage_common(
&$data,
$imgname,
$imageWidth,
$imageHeight,
$x,
$y,
$w = 0,
$h = 0,
$channels = 3
) {
if ($this->image_iscached($imgname)) {
$label = $this->imagelist[$imgname]['label'];
//debugpng
//if (DEBUGPNG) print '[addJpegImage_common Duplicate '.$imgname.']';
} else {
if ($data == null) {
$this->addMessage('addJpegImage_common error - (' . $imgname . ') data not present!');
return;
}
// note that this function is not to be called externally
// it is just the common code between the GD and the file options
$this->numImages++;
$im = $this->numImages;
$label = "I$im";
$this->numObj++;
$this->o_image(
$this->numObj,
'new',
[
'label' => $label,
'data' => &$data,
'iw' => $imageWidth,
'ih' => $imageHeight,
'channels' => $channels
]
);
$this->imagelist[$imgname] = [
'label' => $label,
'w' => $imageWidth,
'h' => $imageHeight,
'c' => $channels
];
}
$this->addContent(sprintf("\nq\n%.3F 0 0 %.3F %.3F %.3F cm /%s Do\nQ ", $w, $h, $x, $y, $label));
}
/**
* specify where the document should open when it first starts
*
* @param $style
* @param int $a
* @param int $b
* @param int $c
*/
function openHere($style, $a = 0, $b = 0, $c = 0)
{
// this function will open the document at a specified page, in a specified style
// the values for style, and the required parameters are:
// 'XYZ' left, top, zoom
// 'Fit'
// 'FitH' top
// 'FitV' left
// 'FitR' left,bottom,right
// 'FitB'
// 'FitBH' top
// 'FitBV' left
$this->numObj++;
$this->o_destination(
$this->numObj,
'new',
['page' => $this->currentPage, 'type' => $style, 'p1' => $a, 'p2' => $b, 'p3' => $c]
);
$id = $this->catalogId;
$this->o_catalog($id, 'openHere', $this->numObj);
}
/**
* Add JavaScript code to the PDF document
*
* @param string $code
*/
function addJavascript($code)
{
$this->javascript .= $code;
}
/**
* create a labelled destination within the document
*
* @param $label
* @param $style
* @param int $a
* @param int $b
* @param int $c
*/
function addDestination($label, $style, $a = 0, $b = 0, $c = 0)
{
// associates the given label with the destination, it is done this way so that a destination can be specified after
// it has been linked to
// styles are the same as the 'openHere' function
$this->numObj++;
$this->o_destination(
$this->numObj,
'new',
['page' => $this->currentPage, 'type' => $style, 'p1' => $a, 'p2' => $b, 'p3' => $c]
);
$id = $this->numObj;
// store the label->idf relationship, note that this means that labels can be used only once
$this->destinations["$label"] = $id;
}
/**
* define font families, this is used to initialize the font families for the default fonts
* and for the user to add new ones for their fonts. The default bahavious can be overridden should
* that be desired.
*
* @param $family
* @param string $options
*/
function setFontFamily($family, $options = '')
{
if (!is_array($options)) {
if ($family === 'init') {
// set the known family groups
// these font families will be used to enable bold and italic markers to be included
// within text streams. html forms will be used...
$this->fontFamilies['Helvetica.afm'] =
[
'b' => 'Helvetica-Bold.afm',
'i' => 'Helvetica-Oblique.afm',
'bi' => 'Helvetica-BoldOblique.afm',
'ib' => 'Helvetica-BoldOblique.afm'
];
$this->fontFamilies['Courier.afm'] =
[
'b' => 'Courier-Bold.afm',
'i' => 'Courier-Oblique.afm',
'bi' => 'Courier-BoldOblique.afm',
'ib' => 'Courier-BoldOblique.afm'
];
$this->fontFamilies['Times-Roman.afm'] =
[
'b' => 'Times-Bold.afm',
'i' => 'Times-Italic.afm',
'bi' => 'Times-BoldItalic.afm',
'ib' => 'Times-BoldItalic.afm'
];
}
} else {
// the user is trying to set a font family
// note that this can also be used to set the base ones to something else
if (mb_strlen($family)) {
$this->fontFamilies[$family] = $options;
}
}
}
/**
* used to add messages for use in debugging
*
* @param $message
*/
function addMessage($message)
{
$this->messages .= $message . "\n";
}
/**
* a few functions which should allow the document to be treated transactionally.
*
* @param $action
*/
function transaction($action)
{
switch ($action) {
case 'start':
// store all the data away into the checkpoint variable
$data = get_object_vars($this);
$this->checkpoint = $data;
unset($data);
break;
case 'commit':
if (is_array($this->checkpoint) && isset($this->checkpoint['checkpoint'])) {
$tmp = $this->checkpoint['checkpoint'];
$this->checkpoint = $tmp;
unset($tmp);
} else {
$this->checkpoint = '';
}
break;
case 'rewind':
// do not destroy the current checkpoint, but move us back to the state then, so that we can try again
if (is_array($this->checkpoint)) {
// can only abort if were inside a checkpoint
$tmp = $this->checkpoint;
foreach ($tmp as $k => $v) {
if ($k !== 'checkpoint') {
$this->$k = $v;
}
}
unset($tmp);
}
break;
case 'abort':
if (is_array($this->checkpoint)) {
// can only abort if were inside a checkpoint
$tmp = $this->checkpoint;
foreach ($tmp as $k => $v) {
$this->$k = $v;
}
unset($tmp);
}
break;
}
}
}