File: /storage/v6964/testingff/public_html/fdfctr/wp-content/plugins/wp-event-solution/utils/tfpdf.php
<?php
namespace Etn\Utils;
use Etn\Utils\Helper;
/*******************************************************************************
* tFPDF (based on FPDF 1.82) *
* *
* Version: 1.32 *
* Date: 2020-08-29 *
* Authors: Ian Back <ianb@bpm1.com> *
* Tycho Veltmeijer <tfpdf@tychoveltmeijer.nl> (versions 1.30+) *
* License: LGPL *
*******************************************************************************/
define( 'tFPDF_VERSION', '1.32' );
class tFPDF {
protected $unifontSubset;
protected $page; // current page number
protected $n; // current object number
protected $offsets; // array of object offsets
protected $buffer; // buffer holding in-memory PDF
protected $pages; // array containing pages
protected $state; // current document state
protected $compress; // compression flag
protected $k; // scale factor (number of points in user unit)
protected $DefOrientation; // default orientation
protected $CurOrientation; // current orientation
protected $StdPageSizes; // standard page sizes
protected $DefPageSize; // default page size
protected $CurPageSize; // current page size
protected $CurRotation; // current page rotation
protected $PageInfo; // page-related data
protected $wPt, $hPt; // dimensions of current page in points
protected $w, $h; // dimensions of current page in user unit
protected $lMargin; // left margin
protected $tMargin; // top margin
protected $rMargin; // right margin
protected $bMargin; // page break margin
protected $cMargin; // cell margin
protected $x, $y; // current position in user unit
protected $lasth; // height of last printed cell
protected $LineWidth; // line width in user unit
protected $fontpath; // path containing fonts
protected $CoreFonts; // array of core font names
protected $fonts; // array of used fonts
protected $FontFiles; // array of font files
protected $encodings; // array of encodings
protected $cmaps; // array of ToUnicode CMaps
protected $FontFamily; // current font family
protected $FontStyle; // current font style
protected $underline; // underlining flag
protected $CurrentFont; // current font info
protected $FontSizePt; // current font size in points
protected $FontSize; // current font size in user unit
protected $DrawColor; // commands for drawing color
protected $FillColor; // commands for filling color
protected $TextColor; // commands for text color
protected $ColorFlag; // indicates whether fill and text colors are different
protected $WithAlpha; // indicates whether alpha channel is used
protected $ws; // word spacing
protected $images; // array of used images
protected $PageLinks; // array of links in pages
protected $links; // array of internal links
protected $AutoPageBreak; // automatic page breaking
protected $PageBreakTrigger; // threshold used to trigger page breaks
protected $InHeader; // flag set when processing header
protected $InFooter; // flag set when processing footer
protected $AliasNbPages; // alias for total number of pages
protected $ZoomMode; // zoom display mode
protected $LayoutMode; // layout display mode
protected $metadata; // document properties
protected $PDFVersion;
// PDF version number
/*******************************************************************************
* Public methods *
*******************************************************************************/
function __construct( $orientation = 'P', $unit = 'mm', $size = 'A4' ) {
// Some checks
$this->_dochecks();
// Initialization of properties
$this->state = 0;
$this->page = 0;
$this->n = 2;
$this->buffer = '';
$this->pages = [];
$this->PageInfo = [];
$this->fonts = [];
$this->FontFiles = [];
$this->encodings = [];
$this->cmaps = [];
$this->images = [];
$this->links = [];
$this->InHeader = false;
$this->InFooter = false;
$this->lasth = 0;
$this->FontFamily = '';
$this->FontStyle = '';
$this->FontSizePt = 12;
$this->underline = false;
$this->DrawColor = '0 G';
$this->FillColor = '0 g';
$this->TextColor = '0 g';
$this->ColorFlag = false;
$this->WithAlpha = false;
$this->ws = 0;
// Font path
if ( defined( 'FPDF_FONTPATH' ) ) {
$this->fontpath = FPDF_FONTPATH;
if ( substr( $this->fontpath, -1 ) != '/' && substr( $this->fontpath, -1 ) != '\\' ) {
$this->fontpath .= '/';
}
} elseif ( is_dir( dirname( __FILE__ ) . '/font' ) ) {
$this->fontpath = dirname( __FILE__ ) . '/font/';
} else {
$this->fontpath = '';
}
// Core fonts
$this->CoreFonts = [ 'courier', 'helvetica', 'times', 'symbol', 'zapfdingbats' ];
// Scale factor
if ( $unit == 'pt' ) {
$this->k = 1;
} elseif ( $unit == 'mm' ) {
$this->k = 72 / 25.4;
} elseif ( $unit == 'cm' ) {
$this->k = 72 / 2.54;
} elseif ( $unit == 'in' ) {
$this->k = 72;
} else {
$this->Error( 'Incorrect unit: ' . $unit );
}
// Page sizes
$this->StdPageSizes = [ 'a3' => [ 841.89, 1190.55 ], 'a4' => [ 595.28, 841.89 ], 'a5' => [ 420.94, 595.28 ],
'letter' => [ 612, 792 ], 'legal' => [ 612, 1008 ] ];
$size = $this->_getpagesize( $size );
$this->DefPageSize = $size;
$this->CurPageSize = $size;
// Page orientation
$orientation = strtolower( $orientation );
if ( $orientation == 'p' || $orientation == 'portrait' ) {
$this->DefOrientation = 'P';
$this->w = $size[0];
$this->h = $size[1];
} elseif ( $orientation == 'l' || $orientation == 'landscape' ) {
$this->DefOrientation = 'L';
$this->w = $size[1];
$this->h = $size[0];
} else {
$this->Error( 'Incorrect orientation: ' . $orientation );
}
$this->CurOrientation = $this->DefOrientation;
$this->wPt = $this->w * $this->k;
$this->hPt = $this->h * $this->k;
// Page rotation
$this->CurRotation = 0;
// Page margins (1 cm)
$margin = 28.35 / $this->k;
$this->SetMargins( $margin, $margin );
// Interior cell margin (1 mm)
$this->cMargin = $margin / 10;
// Line width (0.2 mm)
$this->LineWidth = .567 / $this->k;
// Automatic page break
$this->SetAutoPageBreak( true, 2 * $margin );
// Default display mode
$this->SetDisplayMode( 'default' );
// Enable compression
$this->SetCompression( true );
// Set default PDF version number
$this->PDFVersion = '1.3';
}
function SetMargins( $left, $top, $right = null ) {
// Set left, top and right margins
$this->lMargin = $left;
$this->tMargin = $top;
if ( $right === null ) {
$right = $left;
}
$this->rMargin = $right;
}
function SetLeftMargin( $margin ) {
// Set left margin
$this->lMargin = $margin;
if ( $this->page > 0 && $this->x < $margin ) {
$this->x = $margin;
}
}
function SetTopMargin( $margin ) {
// Set top margin
$this->tMargin = $margin;
}
function SetRightMargin( $margin ) {
// Set right margin
$this->rMargin = $margin;
}
function SetAutoPageBreak( $auto, $margin = 0 ) {
// Set auto page break mode and triggering margin
$this->AutoPageBreak = $auto;
$this->bMargin = $margin;
$this->PageBreakTrigger = $this->h - $margin;
}
function SetDisplayMode( $zoom, $layout = 'default' ) {
// Set display mode in viewer
if ( $zoom == 'fullpage' || $zoom == 'fullwidth' || $zoom == 'real' || $zoom == 'default' || !is_string( $zoom ) ) {
$this->ZoomMode = $zoom;
} else {
$this->Error( 'Incorrect zoom display mode: ' . $zoom );
}
if ( $layout == 'single' || $layout == 'continuous' || $layout == 'two' || $layout == 'default' ) {
$this->LayoutMode = $layout;
} else {
$this->Error( 'Incorrect layout display mode: ' . $layout );
}
}
function SetCompression( $compress ) {
// Set page compression
if ( function_exists( 'gzcompress' ) ) {
$this->compress = $compress;
} else {
$this->compress = false;
}
}
function SetTitle( $title, $isUTF8 = false ) {
// Title of document
$this->metadata['Title'] = $isUTF8 ? $title : utf8_encode( $title );
}
function SetAuthor( $author, $isUTF8 = false ) {
// Author of document
$this->metadata['Author'] = $isUTF8 ? $author : utf8_encode( $author );
}
function SetSubject( $subject, $isUTF8 = false ) {
// Subject of document
$this->metadata['Subject'] = $isUTF8 ? $subject : utf8_encode( $subject );
}
function SetKeywords( $keywords, $isUTF8 = false ) {
// Keywords of document
$this->metadata['Keywords'] = $isUTF8 ? $keywords : utf8_encode( $keywords );
}
function SetCreator( $creator, $isUTF8 = false ) {
// Creator of document
$this->metadata['Creator'] = $isUTF8 ? $creator : utf8_encode( $creator );
}
function AliasNbPages( $alias = '{nb}' ) {
// Define an alias for total number of pages
$this->AliasNbPages = $alias;
}
function Error( $msg ) {
// Fatal error
throw new \Exception( 'FPDF error: ' . $msg );
}
function Close() {
// Terminate document
if ( $this->state == 3 ) {
return;
}
if ( $this->page == 0 ) {
$this->AddPage();
}
// Page footer
$this->InFooter = true;
$this->Footer();
$this->InFooter = false;
// Close page
$this->_endpage();
// Close document
$this->_enddoc();
}
function AddPage( $orientation = '', $size = '', $rotation = 0 ) {
// Start a new page
if ( $this->state == 3 ) {
$this->Error( 'The document is closed' );
}
$family = $this->FontFamily;
$style = $this->FontStyle . ( $this->underline ? 'U' : '' );
$fontsize = $this->FontSizePt;
$lw = $this->LineWidth;
$dc = $this->DrawColor;
$fc = $this->FillColor;
$tc = $this->TextColor;
$cf = $this->ColorFlag;
if ( $this->page > 0 ) {
// Page footer
$this->InFooter = true;
$this->Footer();
$this->InFooter = false;
// Close page
$this->_endpage();
}
// Start new page
$this->_beginpage( $orientation, $size, $rotation );
// Set line cap style to square
$this->_out( '2 J' );
// Set line width
$this->LineWidth = $lw;
$this->_out( sprintf( '%.2F w', $lw * $this->k ) );
// Set font
if ( $family ) {
$this->SetFont( $family, $style, $fontsize );
}
// Set colors
$this->DrawColor = $dc;
if ( $dc != '0 G' ) {
$this->_out( $dc );
}
$this->FillColor = $fc;
if ( $fc != '0 g' ) {
$this->_out( $fc );
}
$this->TextColor = $tc;
$this->ColorFlag = $cf;
// Page header
$this->InHeader = true;
$this->Header();
$this->InHeader = false;
// Restore line width
if ( $this->LineWidth != $lw ) {
$this->LineWidth = $lw;
$this->_out( sprintf( '%.2F w', $lw * $this->k ) );
}
// Restore font
if ( $family ) {
$this->SetFont( $family, $style, $fontsize );
}
// Restore colors
if ( $this->DrawColor != $dc ) {
$this->DrawColor = $dc;
$this->_out( $dc );
}
if ( $this->FillColor != $fc ) {
$this->FillColor = $fc;
$this->_out( $fc );
}
$this->TextColor = $tc;
$this->ColorFlag = $cf;
}
function Header() {
// To be implemented in your own inherited class
}
function Footer() {
// To be implemented in your own inherited class
}
function PageNo() {
// Get current page number
return $this->page;
}
function SetDrawColor( $r, $g = null, $b = null ) {
// Set color for all stroking operations
if ( ( $r == 0 && $g == 0 && $b == 0 ) || $g === null ) {
$this->DrawColor = sprintf( '%.3F G', $r / 255 );
} else {
$this->DrawColor = sprintf( '%.3F %.3F %.3F RG', $r / 255, $g / 255, $b / 255 );
}
if ( $this->page > 0 ) {
$this->_out( $this->DrawColor );
}
}
function SetFillColor( $r, $g = null, $b = null ) {
// Set color for all filling operations
if ( ( $r == 0 && $g == 0 && $b == 0 ) || $g === null ) {
$this->FillColor = sprintf( '%.3F g', $r / 255 );
} else {
$this->FillColor = sprintf( '%.3F %.3F %.3F rg', $r / 255, $g / 255, $b / 255 );
}
$this->ColorFlag = ( $this->FillColor != $this->TextColor );
if ( $this->page > 0 ) {
$this->_out( $this->FillColor );
}
}
function SetTextColor( $r, $g = null, $b = null ) {
// Set color for text
if ( ( $r == 0 && $g == 0 && $b == 0 ) || $g === null ) {
$this->TextColor = sprintf( '%.3F g', $r / 255 );
} else {
$this->TextColor = sprintf( '%.3F %.3F %.3F rg', $r / 255, $g / 255, $b / 255 );
}
$this->ColorFlag = ( $this->FillColor != $this->TextColor );
}
function GetStringWidth( $s ) {
// Get width of a string in the current font
$s = (string) $s;
$cw = &$this->CurrentFont['cw'];
$w = 0;
if ( $this->unifontSubset ) {
$unicode = $this->UTF8StringToArray( $s );
foreach ( $unicode as $char ) {
if ( isset( $cw[2 * $char] ) ) {$w += ( ord( $cw[2 * $char] ) << 8 ) + ord( $cw[2 * $char + 1] );} else
if ( $char > 0 && $char < 128 && isset( $cw[chr( $char )] ) ) {$w += $cw[chr( $char )];} else
if ( isset( $this->CurrentFont['desc']['MissingWidth'] ) ) {$w += $this->CurrentFont['desc']['MissingWidth'];} else
if ( isset( $this->CurrentFont['MissingWidth'] ) ) {$w += $this->CurrentFont['MissingWidth'];} else { $w += 500;}
}
} else {
$l = strlen( $s );
for ( $i = 0; $i < $l; $i++ ) {
$w += $cw[$s[$i]];
}
}
return $w * $this->FontSize / 1000;
}
function SetLineWidth( $width ) {
// Set line width
$this->LineWidth = $width;
if ( $this->page > 0 ) {
$this->_out( sprintf( '%.2F w', $width * $this->k ) );
}
}
function Line( $x1, $y1, $x2, $y2 ) {
// Draw a line
$this->_out( sprintf( '%.2F %.2F m %.2F %.2F l S', $x1 * $this->k, ( $this->h - $y1 ) * $this->k, $x2 * $this->k, ( $this->h - $y2 ) * $this->k ) );
}
function Rect( $x, $y, $w, $h, $style = '' ) {
// Draw a rectangle
if ( $style == 'F' ) {
$op = 'f';
} elseif ( $style == 'FD' || $style == 'DF' ) {
$op = 'B';
} else {
$op = 'S';
}
$this->_out( sprintf( '%.2F %.2F %.2F %.2F re %s', $x * $this->k, ( $this->h - $y ) * $this->k, $w * $this->k, -$h * $this->k, $op ) );
}
function AddFont( $family, $style = '', $file = '', $uni = false ) {
// Add a TrueType, OpenType or Type1 font
$family = strtolower( $family );
$style = strtoupper( $style );
if ( $style == 'IB' ) {
$style = 'BI';
}
if ( $file == '' ) {
if ( $uni ) {
$file = str_replace( ' ', '', $family ) . strtolower( $style ) . '.ttf';
} else {
$file = str_replace( ' ', '', $family ) . strtolower( $style ) . '.php';
}
}
$fontkey = $family . $style;
if ( isset( $this->fonts[$fontkey] ) ) {
return;
}
if ( $uni ) {
if ( defined( "_SYSTEM_TTFONTS" ) && file_exists( _SYSTEM_TTFONTS . $file ) ) {$ttffilename = _SYSTEM_TTFONTS . $file;} else { $ttffilename = $this->fontpath . 'unifont/' . $file;}
$unifilename = $this->fontpath . 'unifont/' . strtolower( substr( $file, 0, ( strpos( $file, '.' ) ) ) );
$name = '';
$originalsize = 0;
$ttfstat = stat( $ttffilename );
if ( file_exists( $unifilename . '.mtx.php' ) ) {
include $unifilename . '.mtx.php';
}
if ( !isset( $type ) || !isset( $name ) || $originalsize != $ttfstat['size'] ) {
$ttffile = $ttffilename;
require_once $this->fontpath . 'unifont/ttfonts.php';
$ttf = new \Etn\Utils\Font\Unifont\TTFontFile();
$ttf->getMetrics( $ttffile );
$cw = $ttf->charWidths;
$name = preg_replace( '/[ ()]/', '', $ttf->fullName );
$desc = [ 'Ascent' => round( $ttf->ascent ),
'Descent' => round( $ttf->descent ),
'CapHeight' => round( $ttf->capHeight ),
'Flags' => $ttf->flags,
'FontBBox' => '[' . round( $ttf->bbox[0] ) . " " . round( $ttf->bbox[1] ) . " " . round( $ttf->bbox[2] ) . " " . round( $ttf->bbox[3] ) . ']',
'ItalicAngle' => $ttf->italicAngle,
'StemV' => round( $ttf->stemV ),
'MissingWidth' => round( $ttf->defaultWidth ) ];
$up = round( $ttf->underlinePosition );
$ut = round( $ttf->underlineThickness );
$originalsize = $ttfstat['size'] + 0;
$type = 'TTF';
// Generate metrics .php file
$s = '<?php' . "\n";
$s .= '$name=\'' . $name . "';\n";
$s .= '$type=\'' . $type . "';\n";
$s .= '$desc=' . var_export( $desc, true ) . ";\n";
$s .= '$up=' . $up . ";\n";
$s .= '$ut=' . $ut . ";\n";
$s .= '$ttffile=\'' . $ttffile . "';\n";
$s .= '$originalsize=' . $originalsize . ";\n";
$s .= '$fontkey=\'' . $fontkey . "';\n";
$s .= "?>";
if ( is_writable( dirname( $this->fontpath . 'unifont/' . 'x' ) ) ) {
$fh = fopen( $unifilename . '.mtx.php', "w" );
fwrite( $fh, $s, strlen( $s ) );
fclose( $fh );
$fh = fopen( $unifilename . '.cw.dat', "wb" );
fwrite( $fh, $cw, strlen( $cw ) );
fclose( $fh );
@unlink( $unifilename . '.cw127.php' );
}
unset( $ttf );
} else {
$cw = @file_get_contents( $unifilename . '.cw.dat' );
}
$i = count( $this->fonts ) + 1;
if ( !empty( $this->AliasNbPages ) ) {
$sbarr = range( 0, 57 );
} else {
$sbarr = range( 0, 32 );
}
$this->fonts[$fontkey] = [ 'i' => $i, 'type' => $type, 'name' => $name, 'desc' => $desc, 'up' => $up, 'ut' => $ut, 'cw' => $cw, 'ttffile' => $ttffile, 'fontkey' => $fontkey, 'subset' => $sbarr, 'unifilename' => $unifilename ];
$this->FontFiles[$fontkey] = [ 'length1' => $originalsize, 'type' => "TTF", 'ttffile' => $ttffile ];
$this->FontFiles[$file] = [ 'type' => "TTF" ];
unset( $cw );
} else {
$info = $this->_loadfont( $file );
$info['i'] = count( $this->fonts ) + 1;
if ( !empty( $info['file'] ) ) {
// Embedded font
if ( $info['type'] == 'TrueType' ) {
$this->FontFiles[$info['file']] = [ 'length1' => $info['originalsize'] ];
} else {
$this->FontFiles[$info['file']] = [ 'length1' => $info['size1'], 'length2' => $info['size2'] ];
}
}
$this->fonts[$fontkey] = $info;
}
}
function SetFont( $family, $style = '', $size = 0 ) {
// Select a font; size given in points
if ( $family == '' ) {
$family = $this->FontFamily;
} else {
$family = strtolower( $family );
}
$style = strtoupper( $style );
if ( strpos( $style, 'U' ) !== false ) {
$this->underline = true;
$style = str_replace( 'U', '', $style );
} else {
$this->underline = false;
}
if ( $style == 'IB' ) {
$style = 'BI';
}
if ( $size == 0 ) {
$size = $this->FontSizePt;
}
// Test if font is already selected
if ( $this->FontFamily == $family && $this->FontStyle == $style && $this->FontSizePt == $size ) {
return;
}
// Test if font is already loaded
$fontkey = $family . $style;
if ( !isset( $this->fonts[$fontkey] ) ) {
// Test if one of the core fonts
if ( $family == 'arial' ) {
$family = 'helvetica';
}
if ( in_array( $family, $this->CoreFonts ) ) {
if ( $family == 'symbol' || $family == 'zapfdingbats' ) {
$style = '';
}
$fontkey = $family . $style;
if ( !isset( $this->fonts[$fontkey] ) ) {
$this->AddFont( $family, $style );
}
} else {
$this->Error( 'Undefined font: ' . $family . ' ' . $style );
}
}
// Select it
$this->FontFamily = $family;
$this->FontStyle = $style;
$this->FontSizePt = $size;
$this->FontSize = $size / $this->k;
$this->CurrentFont = &$this->fonts[$fontkey];
if ( $this->fonts[$fontkey]['type'] == 'TTF' ) {$this->unifontSubset = true;} else { $this->unifontSubset = false;}
if ( $this->page > 0 ) {
$this->_out( sprintf( 'BT /F%d %.2F Tf ET', $this->CurrentFont['i'], $this->FontSizePt ) );
}
}
function SetFontSize( $size ) {
// Set font size in points
if ( $this->FontSizePt == $size ) {
return;
}
$this->FontSizePt = $size;
$this->FontSize = $size / $this->k;
if ( $this->page > 0 ) {
$this->_out( sprintf( 'BT /F%d %.2F Tf ET', $this->CurrentFont['i'], $this->FontSizePt ) );
}
}
function AddLink() {
// Create a new internal link
$n = count( $this->links ) + 1;
$this->links[$n] = [ 0, 0 ];
return $n;
}
function SetLink( $link, $y = 0, $page = -1 ) {
// Set destination of internal link
if ( $y == -1 ) {
$y = $this->y;
}
if ( $page == -1 ) {
$page = $this->page;
}
$this->links[$link] = [ $page, $y ];
}
function Link( $x, $y, $w, $h, $link ) {
// Put a link on the page
$this->PageLinks[$this->page][] = [ $x * $this->k, $this->hPt - $y * $this->k, $w * $this->k, $h * $this->k, $link ];
}
function Text( $x, $y, $txt ) {
// Output a string
$txt = (string) $txt;
if ( !isset( $this->CurrentFont ) ) {
$this->Error( 'No font has been set' );
}
if ( $this->unifontSubset ) {
$txt2 = '(' . $this->_escape( $this->UTF8ToUTF16BE( $txt, false ) ) . ')';
foreach ( $this->UTF8StringToArray( $txt ) as $uni ) {
$this->CurrentFont['subset'][$uni] = $uni;
}
} else {
$txt2 = '(' . $this->_escape( $txt ) . ')';
}
$s = sprintf( 'BT %.2F %.2F Td %s Tj ET', $x * $this->k, ( $this->h - $y ) * $this->k, $txt2 );
if ( $this->underline && $txt != '' ) {
$s .= ' ' . $this->_dounderline( $x, $y, $txt );
}
if ( $this->ColorFlag ) {
$s = 'q ' . $this->TextColor . ' ' . $s . ' Q';
}
$this->_out( $s );
}
function AcceptPageBreak() {
// Accept automatic page break or not
return $this->AutoPageBreak;
}
function Cell( $w, $h = 0, $txt = '', $border = 0, $ln = 0, $align = '', $fill = false, $link = '' ) {
// Output a cell
$txt = (string) $txt;
$k = $this->k;
if ( $this->y + $h > $this->PageBreakTrigger && !$this->InHeader && !$this->InFooter && $this->AcceptPageBreak() ) {
// Automatic page break
$x = $this->x;
$ws = $this->ws;
if ( $ws > 0 ) {
$this->ws = 0;
$this->_out( '0 Tw' );
}
$this->AddPage( $this->CurOrientation, $this->CurPageSize, $this->CurRotation );
$this->x = $x;
if ( $ws > 0 ) {
$this->ws = $ws;
$this->_out( sprintf( '%.3F Tw', $ws * $k ) );
}
}
if ( $w == 0 ) {
$w = $this->w - $this->rMargin - $this->x;
}
$s = '';
if ( $fill || $border == 1 ) {
if ( $fill ) {
$op = ( $border == 1 ) ? 'B' : 'f';
} else {
$op = 'S';
}
$s = sprintf( '%.2F %.2F %.2F %.2F re %s ', $this->x * $k, ( $this->h - $this->y ) * $k, $w * $k, -$h * $k, $op );
}
if ( is_string( $border ) ) {
$x = $this->x;
$y = $this->y;
if ( strpos( $border, 'L' ) !== false ) {
$s .= sprintf( '%.2F %.2F m %.2F %.2F l S ', $x * $k, ( $this->h - $y ) * $k, $x * $k, ( $this->h - ( $y + $h ) ) * $k );
}
if ( strpos( $border, 'T' ) !== false ) {
$s .= sprintf( '%.2F %.2F m %.2F %.2F l S ', $x * $k, ( $this->h - $y ) * $k, ( $x + $w ) * $k, ( $this->h - $y ) * $k );
}
if ( strpos( $border, 'R' ) !== false ) {
$s .= sprintf( '%.2F %.2F m %.2F %.2F l S ', ( $x + $w ) * $k, ( $this->h - $y ) * $k, ( $x + $w ) * $k, ( $this->h - ( $y + $h ) ) * $k );
}
if ( strpos( $border, 'B' ) !== false ) {
$s .= sprintf( '%.2F %.2F m %.2F %.2F l S ', $x * $k, ( $this->h - ( $y + $h ) ) * $k, ( $x + $w ) * $k, ( $this->h - ( $y + $h ) ) * $k );
}
}
if ( $txt !== '' ) {
if ( !isset( $this->CurrentFont ) ) {
$this->Error( 'No font has been set' );
}
if ( $align == 'R' ) {
$dx = $w - $this->cMargin - $this->GetStringWidth( $txt );
} elseif ( $align == 'C' ) {
$dx = ( $w - $this->GetStringWidth( $txt ) ) / 2;
} else {
$dx = $this->cMargin;
}
if ( $this->ColorFlag ) {
$s .= 'q ' . $this->TextColor . ' ';
}
// If multibyte, Tw has no effect - do word spacing using an adjustment before each space
if ( $this->ws && $this->unifontSubset ) {
foreach ( $this->UTF8StringToArray( $txt ) as $uni ) {
$this->CurrentFont['subset'][$uni] = $uni;
}
$space = $this->_escape( $this->UTF8ToUTF16BE( ' ', false ) );
$s .= sprintf( 'BT 0 Tw %.2F %.2F Td [', ( $this->x + $dx ) * $k, ( $this->h - ( $this->y + .5 * $h + .3 * $this->FontSize ) ) * $k );
$t = explode( ' ', $txt );
$numt = count( $t );
for ( $i = 0; $i < $numt; $i++ ) {
$tx = $t[$i];
$tx = '(' . $this->_escape( $this->UTF8ToUTF16BE( $tx, false ) ) . ')';
$s .= sprintf( '%s ', $tx );
if ( ( $i + 1 ) < $numt ) {
$adj = -( $this->ws * $this->k ) * 1000 / $this->FontSizePt;
$s .= sprintf( '%d(%s) ', $adj, $space );
}
}
$s .= '] TJ';
$s .= ' ET';
} else {
if ( $this->unifontSubset ) {
$txt2 = '(' . $this->_escape( $this->UTF8ToUTF16BE( $txt, false ) ) . ')';
foreach ( $this->UTF8StringToArray( $txt ) as $uni ) {
$this->CurrentFont['subset'][$uni] = $uni;
}
} else {
$txt2 = '(' . $this->_escape( $txt ) . ')';
}
$s .= sprintf( 'BT %.2F %.2F Td %s Tj ET', ( $this->x + $dx ) * $k, ( $this->h - ( $this->y + .5 * $h + .3 * $this->FontSize ) ) * $k, $txt2 );
}
if ( $this->underline ) {
$s .= ' ' . $this->_dounderline( $this->x + $dx, $this->y + .5 * $h + .3 * $this->FontSize, $txt );
}
if ( $this->ColorFlag ) {
$s .= ' Q';
}
if ( $link ) {
$this->Link( $this->x + $dx, $this->y + .5 * $h - .5 * $this->FontSize, $this->GetStringWidth( $txt ), $this->FontSize, $link );
}
}
if ( $s ) {
$this->_out( $s );
}
$this->lasth = $h;
if ( $ln > 0 ) {
// Go to next line
$this->y += $h;
if ( $ln == 1 ) {
$this->x = $this->lMargin;
}
} else {
$this->x += $w;
}
}
function MultiCell( $w, $h, $txt, $border = 0, $align = 'J', $fill = false ) {
// Output text with automatic or explicit line breaks
if ( !isset( $this->CurrentFont ) ) {
$this->Error( 'No font has been set' );
}
$cw = &$this->CurrentFont['cw'];
if ( $w == 0 ) {
$w = $this->w - $this->rMargin - $this->x;
}
$wmax = ( $w - 2 * $this->cMargin );
//$wmax = ($w-2*$this->cMargin)*1000/$this->FontSize;
$s = str_replace( "\r", '', (string) $txt );
if ( $this->unifontSubset ) {
$nb = mb_strlen( $s, 'utf-8' );
while ( $nb > 0 && mb_substr( $s, $nb - 1, 1, 'utf-8' ) == "\n" ) {
$nb--;
}
} else {
$nb = strlen( $s );
if ( $nb > 0 && $s[$nb - 1] == "\n" ) {
$nb--;
}
}
$b = 0;
if ( $border ) {
if ( $border == 1 ) {
$border = 'LTRB';
$b = 'LRT';
$b2 = 'LR';
} else {
$b2 = '';
if ( strpos( $border, 'L' ) !== false ) {
$b2 .= 'L';
}
if ( strpos( $border, 'R' ) !== false ) {
$b2 .= 'R';
}
$b = ( strpos( $border, 'T' ) !== false ) ? $b2 . 'T' : $b2;
}
}
$sep = -1;
$i = 0;
$j = 0;
$l = 0;
$ns = 0;
$nl = 1;
while ( $i < $nb ) {
// Get next character
if ( $this->unifontSubset ) {
$c = mb_substr( $s, $i, 1, 'UTF-8' );
} else {
$c = $s[$i];
}
if ( $c == "\n" ) {
// Explicit line break
if ( $this->ws > 0 ) {
$this->ws = 0;
$this->_out( '0 Tw' );
}
if ( $this->unifontSubset ) {
$this->Cell( $w, $h, mb_substr( $s, $j, $i - $j, 'UTF-8' ), $b, 2, $align, $fill );
} else {
$this->Cell( $w, $h, substr( $s, $j, $i - $j ), $b, 2, $align, $fill );
}
$i++;
$sep = -1;
$j = $i;
$l = 0;
$ns = 0;
$nl++;
if ( $border && $nl == 2 ) {
$b = $b2;
}
continue;
}
if ( $c == ' ' ) {
$sep = $i;
$ls = $l;
$ns++;
}
if ( $this->unifontSubset ) {$l += $this->GetStringWidth( $c );} else { $l += $cw[$c] * $this->FontSize / 1000;}
if ( $l > $wmax ) {
// Automatic line break
if ( $sep == -1 ) {
if ( $i == $j ) {
$i++;
}
if ( $this->ws > 0 ) {
$this->ws = 0;
$this->_out( '0 Tw' );
}
if ( $this->unifontSubset ) {
$this->Cell( $w, $h, mb_substr( $s, $j, $i - $j, 'UTF-8' ), $b, 2, $align, $fill );
} else {
$this->Cell( $w, $h, substr( $s, $j, $i - $j ), $b, 2, $align, $fill );
}
} else {
if ( $align == 'J' ) {
$this->ws = ( $ns > 1 ) ? ( $wmax - $ls ) / ( $ns - 1 ) : 0;
$this->_out( sprintf( '%.3F Tw', $this->ws * $this->k ) );
}
if ( $this->unifontSubset ) {
$this->Cell( $w, $h, mb_substr( $s, $j, $sep - $j, 'UTF-8' ), $b, 2, $align, $fill );
} else {
$this->Cell( $w, $h, substr( $s, $j, $sep - $j ), $b, 2, $align, $fill );
}
$i = $sep + 1;
}
$sep = -1;
$j = $i;
$l = 0;
$ns = 0;
$nl++;
if ( $border && $nl == 2 ) {
$b = $b2;
}
} else {
$i++;
}
}
// Last chunk
if ( $this->ws > 0 ) {
$this->ws = 0;
$this->_out( '0 Tw' );
}
if ( $border && strpos( $border, 'B' ) !== false ) {
$b .= 'B';
}
if ( $this->unifontSubset ) {
$this->Cell( $w, $h, mb_substr( $s, $j, $i - $j, 'UTF-8' ), $b, 2, $align, $fill );
} else {
$this->Cell( $w, $h, substr( $s, $j, $i - $j ), $b, 2, $align, $fill );
}
$this->x = $this->lMargin;
}
function Write( $h, $txt, $link = '' ) {
// Output text in flowing mode
if ( !isset( $this->CurrentFont ) ) {
$this->Error( 'No font has been set' );
}
$cw = &$this->CurrentFont['cw'];
$w = $this->w - $this->rMargin - $this->x;
$wmax = ( $w - 2 * $this->cMargin );
$s = str_replace( "\r", '', (string) $txt );
if ( $this->unifontSubset ) {
$nb = mb_strlen( $s, 'UTF-8' );
if ( $nb == 1 && $s == " " ) {
$this->x += $this->GetStringWidth( $s );
return;
}
} else {
$nb = strlen( $s );
}
$sep = -1;
$i = 0;
$j = 0;
$l = 0;
$nl = 1;
while ( $i < $nb ) {
// Get next character
if ( $this->unifontSubset ) {
$c = mb_substr( $s, $i, 1, 'UTF-8' );
} else {
$c = $s[$i];
}
if ( $c == "\n" ) {
// Explicit line break
if ( $this->unifontSubset ) {
$this->Cell( $w, $h, mb_substr( $s, $j, $i - $j, 'UTF-8' ), 0, 2, '', false, $link );
} else {
$this->Cell( $w, $h, substr( $s, $j, $i - $j ), 0, 2, '', false, $link );
}
$i++;
$sep = -1;
$j = $i;
$l = 0;
if ( $nl == 1 ) {
$this->x = $this->lMargin;
$w = $this->w - $this->rMargin - $this->x;
$wmax = ( $w - 2 * $this->cMargin );
}
$nl++;
continue;
}
if ( $c == ' ' ) {
$sep = $i;
}
if ( $this->unifontSubset ) {$l += $this->GetStringWidth( $c );} else { $l += $cw[$c] * $this->FontSize / 1000;}
if ( $l > $wmax ) {
// Automatic line break
if ( $sep == -1 ) {
if ( $this->x > $this->lMargin ) {
// Move to next line
$this->x = $this->lMargin;
$this->y += $h;
$w = $this->w - $this->rMargin - $this->x;
$wmax = ( $w - 2 * $this->cMargin );
$i++;
$nl++;
continue;
}
if ( $i == $j ) {
$i++;
}
if ( $this->unifontSubset ) {
$this->Cell( $w, $h, mb_substr( $s, $j, $i - $j, 'UTF-8' ), 0, 2, '', false, $link );
} else {
$this->Cell( $w, $h, substr( $s, $j, $i - $j ), 0, 2, '', false, $link );
}
} else {
if ( $this->unifontSubset ) {
$this->Cell( $w, $h, mb_substr( $s, $j, $sep - $j, 'UTF-8' ), 0, 2, '', false, $link );
} else {
$this->Cell( $w, $h, substr( $s, $j, $sep - $j ), 0, 2, '', false, $link );
}
$i = $sep + 1;
}
$sep = -1;
$j = $i;
$l = 0;
if ( $nl == 1 ) {
$this->x = $this->lMargin;
$w = $this->w - $this->rMargin - $this->x;
$wmax = ( $w - 2 * $this->cMargin );
}
$nl++;
} else {
$i++;
}
}
// Last chunk
if ( $i != $j ) {
if ( $this->unifontSubset ) {
$this->Cell( $l, $h, mb_substr( $s, $j, $i - $j, 'UTF-8' ), 0, 0, '', false, $link );
} else {
$this->Cell( $l, $h, substr( $s, $j ), 0, 0, '', false, $link );
}
}
}
function Ln( $h = null ) {
// Line feed; default value is the last cell height
$this->x = $this->lMargin;
if ( $h === null ) {
$this->y += $this->lasth;
} else {
$this->y += $h;
}
}
function Image( $file, $x = null, $y = null, $w = 0, $h = 0, $type = '', $link = '' ) {
// Put an image on the page
if ( $file == '' ) {
$this->Error( 'Image file name is empty' );
}
if ( !isset( $this->images[$file] ) ) {
// First use of this image, get info
if ( $type == '' ) {
$pos = strrpos( $file, '.' );
if ( !$pos ) {
$this->Error( 'Image file has no extension and no type was specified: ' . $file );
}
$type = substr( $file, $pos + 1 );
}
$type = strtolower( $type );
if ( $type == 'jpeg' ) {
$type = 'jpg';
}
$mtd = '_parse' . $type;
if ( !method_exists( $this, $mtd ) ) {
$this->Error( 'Unsupported image type: ' . $type );
}
$info = $this->$mtd( $file );
$info['i'] = count( $this->images ) + 1;
$this->images[$file] = $info;
} else {
$info = $this->images[$file];
}
// Automatic width and height calculation if needed
if ( $w == 0 && $h == 0 ) {
// Put image at 96 dpi
$w = -96;
$h = -96;
}
if ( $w < 0 ) {
$w = -$info['w'] * 72 / $w / $this->k;
}
if ( $h < 0 ) {
$h = -$info['h'] * 72 / $h / $this->k;
}
if ( $w == 0 ) {
$w = $h * $info['w'] / $info['h'];
}
if ( $h == 0 ) {
$h = $w * $info['h'] / $info['w'];
}
// Flowing mode
if ( $y === null ) {
if ( $this->y + $h > $this->PageBreakTrigger && !$this->InHeader && !$this->InFooter && $this->AcceptPageBreak() ) {
// Automatic page break
$x2 = $this->x;
$this->AddPage( $this->CurOrientation, $this->CurPageSize, $this->CurRotation );
$this->x = $x2;
}
$y = $this->y;
$this->y += $h;
}
if ( $x === null ) {
$x = $this->x;
}
$this->_out( sprintf( 'q %.2F 0 0 %.2F %.2F %.2F cm /I%d Do Q', $w * $this->k, $h * $this->k, $x * $this->k, ( $this->h - ( $y + $h ) ) * $this->k, $info['i'] ) );
if ( $link ) {
$this->Link( $x, $y, $w, $h, $link );
}
}
function GetPageWidth() {
// Get current page width
return $this->w;
}
function GetPageHeight() {
// Get current page height
return $this->h;
}
function GetX() {
// Get x position
return $this->x;
}
function SetX( $x ) {
// Set x position
if ( $x >= 0 ) {
$this->x = $x;
} else {
$this->x = $this->w + $x;
}
}
function GetY() {
// Get y position
return $this->y;
}
function SetY( $y, $resetX = true ) {
// Set y position and optionally reset x
if ( $y >= 0 ) {
$this->y = $y;
} else {
$this->y = $this->h + $y;
}
if ( $resetX ) {
$this->x = $this->lMargin;
}
}
function SetXY( $x, $y ) {
// Set x and y positions
$this->SetX( $x );
$this->SetY( $y, false );
}
function Output( $dest = '', $name = '', $isUTF8 = false ) {
// Output PDF to some destination
$this->Close();
if ( strlen( $name ) == 1 && strlen( $dest ) != 1 ) {
// Fix parameter order
$tmp = $dest;
$dest = $name;
$name = $tmp;
}
if ( $dest == '' ) {
$dest = 'I';
}
if ( $name == '' ) {
$name = 'doc.pdf';
}
switch ( strtoupper( $dest ) ) {
case 'I':
// Send to standard output
$this->_checkoutput();
if ( PHP_SAPI != 'cli' ) {
// We send to a browser
header( 'Content-Type: application/pdf' );
header( 'Content-Disposition: inline; ' . $this->_httpencode( 'filename', $name, $isUTF8 ) );
header( 'Cache-Control: private, max-age=0, must-revalidate' );
header( 'Pragma: public' );
}
echo Helper::render( $this->buffer );
break;
case 'D':
// Download file
$this->_checkoutput();
header( 'Content-Type: application/x-download' );
header( 'Content-Disposition: attachment; ' . $this->_httpencode( 'filename', $name, $isUTF8 ) );
header( 'Cache-Control: private, max-age=0, must-revalidate' );
header( 'Pragma: public' );
echo Helper::render( $this->buffer );
break;
case 'F':
// Save to local file
if ( !file_put_contents( $name, $this->buffer ) ) {
$this->Error( 'Unable to create output file: ' . $name );
}
break;
case 'S':
// Return as a string
return $this->buffer;
default:
$this->Error( 'Incorrect output destination: ' . $dest );
}
return '';
}
/*******************************************************************************
* Protected methods *
*******************************************************************************/
protected function _dochecks() {
// Check availability of mbstring
if ( !function_exists( 'mb_strlen' ) ) {
$this->Error( 'mbstring extension is not available' );
}
// Check mbstring overloading
if ( ini_get( 'mbstring.func_overload' ) & 2 ) {
$this->Error( 'mbstring overloading must be disabled' );
}
}
protected function _checkoutput() {
if ( PHP_SAPI != 'cli' ) {
if ( headers_sent( $file, $line ) ) {
$this->Error( "Some data has already been output, can't send PDF file (output started at $file:$line)" );
}
}
if ( ob_get_length() ) {
// The output buffer is not empty
if ( preg_match( '/^(\xEF\xBB\xBF)?\s*$/', ob_get_contents() ) ) {
// It contains only a UTF-8 BOM and/or whitespace, let's clean it
ob_clean();
} else {
$this->Error( "Some data has already been output, can't send PDF file" );
}
}
}
protected function _getpagesize( $size ) {
if ( is_string( $size ) ) {
$size = strtolower( $size );
if ( !isset( $this->StdPageSizes[$size] ) ) {
$this->Error( 'Unknown page size: ' . $size );
}
$a = $this->StdPageSizes[$size];
return [ $a[0] / $this->k, $a[1] / $this->k ];
} else {
if ( $size[0] > $size[1] ) {
return [ $size[1], $size[0] ];
} else {
return $size;
}
}
}
protected function _beginpage( $orientation, $size, $rotation ) {
$this->page++;
$this->pages[$this->page] = '';
$this->state = 2;
$this->x = $this->lMargin;
$this->y = $this->tMargin;
$this->FontFamily = '';
// Check page size and orientation
if ( $orientation == '' ) {
$orientation = $this->DefOrientation;
} else {
$orientation = strtoupper( $orientation[0] );
}
if ( $size == '' ) {
$size = $this->DefPageSize;
} else {
$size = $this->_getpagesize( $size );
}
if ( $orientation != $this->CurOrientation || $size[0] != $this->CurPageSize[0] || $size[1] != $this->CurPageSize[1] ) {
// New size or orientation
if ( $orientation == 'P' ) {
$this->w = $size[0];
$this->h = $size[1];
} else {
$this->w = $size[1];
$this->h = $size[0];
}
$this->wPt = $this->w * $this->k;
$this->hPt = $this->h * $this->k;
$this->PageBreakTrigger = $this->h - $this->bMargin;
$this->CurOrientation = $orientation;
$this->CurPageSize = $size;
}
if ( $orientation != $this->DefOrientation || $size[0] != $this->DefPageSize[0] || $size[1] != $this->DefPageSize[1] ) {
$this->PageInfo[$this->page]['size'] = [ $this->wPt, $this->hPt ];
}
if ( $rotation != 0 ) {
if ( $rotation % 90 != 0 ) {
$this->Error( 'Incorrect rotation value: ' . $rotation );
}
$this->CurRotation = $rotation;
$this->PageInfo[$this->page]['rotation'] = $rotation;
}
}
protected function _endpage() {
$this->state = 1;
}
protected function _loadfont( $font ) {
// Load a font definition file from the font directory
if ( strpos( $font, '/' ) !== false || strpos( $font, "\\" ) !== false ) {
$this->Error( 'Incorrect font definition file name: ' . $font );
}
include $this->fontpath . $font;
if ( !isset( $name ) ) {
$this->Error( 'Could not include font definition file' );
}
if ( isset( $enc ) ) {
$enc = strtolower( $enc );
}
if ( !isset( $subsetted ) ) {
$subsetted = false;
}
return get_defined_vars();
}
protected function _isascii( $s ) {
// Test if string is ASCII
$nb = strlen( $s );
for ( $i = 0; $i < $nb; $i++ ) {
if ( ord( $s[$i] ) > 127 ) {
return false;
}
}
return true;
}
protected function _httpencode( $param, $value, $isUTF8 ) {
// Encode HTTP header field parameter
if ( $this->_isascii( $value ) ) {
return $param . '="' . $value . '"';
}
if ( !$isUTF8 ) {
$value = utf8_encode( $value );
}
if ( strpos( $_SERVER['HTTP_USER_AGENT'], 'MSIE' ) !== false ) {
return $param . '="' . rawurlencode( $value ) . '"';
} else {
return $param . "*=UTF-8''" . rawurlencode( $value );
}
}
protected function _UTF8toUTF16( $s ) {
// Convert UTF-8 to UTF-16BE with BOM
$res = "\xFE\xFF";
$nb = strlen( $s );
$i = 0;
while ( $i < $nb ) {
$c1 = ord( $s[$i++] );
if ( $c1 >= 224 ) {
// 3-byte character
$c2 = ord( $s[$i++] );
$c3 = ord( $s[$i++] );
$res .= chr( ( ( $c1 & 0x0F ) << 4 ) + ( ( $c2 & 0x3C ) >> 2 ) );
$res .= chr( ( ( $c2 & 0x03 ) << 6 ) + ( $c3 & 0x3F ) );
} elseif ( $c1 >= 192 ) {
// 2-byte character
$c2 = ord( $s[$i++] );
$res .= chr( ( $c1 & 0x1C ) >> 2 );
$res .= chr( ( ( $c1 & 0x03 ) << 6 ) + ( $c2 & 0x3F ) );
} else {
// Single-byte character
$res .= "\0" . chr( $c1 );
}
}
return $res;
}
protected function _escape( $s ) {
// Escape special characters
if ( strpos( $s, '(' ) !== false || strpos( $s, ')' ) !== false || strpos( $s, '\\' ) !== false || strpos( $s, "\r" ) !== false ) {
return str_replace( [ '\\', '(', ')', "\r" ], [ '\\\\', '\\(', '\\)', '\\r' ], $s );
} else {
return $s;
}
}
protected function _textstring( $s ) {
// Format a text string
if ( !$this->_isascii( $s ) ) {
$s = $this->_UTF8toUTF16( $s );
}
return '(' . $this->_escape( $s ) . ')';
}
protected function _dounderline( $x, $y, $txt ) {
// Underline text
$up = $this->CurrentFont['up'];
$ut = $this->CurrentFont['ut'];
$w = $this->GetStringWidth( $txt ) + $this->ws * substr_count( $txt, ' ' );
return sprintf( '%.2F %.2F %.2F %.2F re f', $x * $this->k, ( $this->h - ( $y - $up / 1000 * $this->FontSize ) ) * $this->k, $w * $this->k, -$ut / 1000 * $this->FontSizePt );
}
protected function _parsejpg( $file ) {
// Extract info from a JPEG file
$a = getimagesize( $file );
if ( !$a ) {
$this->Error( 'Missing or incorrect image file: ' . $file );
}
if ( $a[2] != 2 ) {
$this->Error( 'Not a JPEG file: ' . $file );
}
if ( !isset( $a['channels'] ) || $a['channels'] == 3 ) {
$colspace = 'DeviceRGB';
} elseif ( $a['channels'] == 4 ) {
$colspace = 'DeviceCMYK';
} else {
$colspace = 'DeviceGray';
}
$bpc = isset( $a['bits'] ) ? $a['bits'] : 8;
$data = file_get_contents( $file );
return [ 'w' => $a[0], 'h' => $a[1], 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'DCTDecode', 'data' => $data ];
}
protected function _parsepng( $file ) {
// Extract info from a PNG file
$f = fopen( $file, 'rb' );
if ( !$f ) {
$this->Error( 'Can\'t open image file: ' . $file );
}
$info = $this->_parsepngstream( $f, $file );
fclose( $f );
return $info;
}
protected function _parsepngstream( $f, $file ) {
// Check signature
if ( $this->_readstream( $f, 8 ) != chr( 137 ) . 'PNG' . chr( 13 ) . chr( 10 ) . chr( 26 ) . chr( 10 ) ) {
$this->Error( 'Not a PNG file: ' . $file );
}
// Read header chunk
$this->_readstream( $f, 4 );
if ( $this->_readstream( $f, 4 ) != 'IHDR' ) {
$this->Error( 'Incorrect PNG file: ' . $file );
}
$w = $this->_readint( $f );
$h = $this->_readint( $f );
$bpc = ord( $this->_readstream( $f, 1 ) );
if ( $bpc > 8 ) {
$this->Error( '16-bit depth not supported: ' . $file );
}
$ct = ord( $this->_readstream( $f, 1 ) );
if ( $ct == 0 || $ct == 4 ) {
$colspace = 'DeviceGray';
} elseif ( $ct == 2 || $ct == 6 ) {
$colspace = 'DeviceRGB';
} elseif ( $ct == 3 ) {
$colspace = 'Indexed';
} else {
$this->Error( 'Unknown color type: ' . $file );
}
if ( ord( $this->_readstream( $f, 1 ) ) != 0 ) {
$this->Error( 'Unknown compression method: ' . $file );
}
if ( ord( $this->_readstream( $f, 1 ) ) != 0 ) {
$this->Error( 'Unknown filter method: ' . $file );
}
if ( ord( $this->_readstream( $f, 1 ) ) != 0 ) {
$this->Error( 'Interlacing not supported: ' . $file );
}
$this->_readstream( $f, 4 );
$dp = '/Predictor 15 /Colors ' . ( $colspace == 'DeviceRGB' ? 3 : 1 ) . ' /BitsPerComponent ' . $bpc . ' /Columns ' . $w;
// Scan chunks looking for palette, transparency and image data
$pal = '';
$trns = '';
$data = '';
do {
$n = $this->_readint( $f );
$type = $this->_readstream( $f, 4 );
if ( $type == 'PLTE' ) {
// Read palette
$pal = $this->_readstream( $f, $n );
$this->_readstream( $f, 4 );
} elseif ( $type == 'tRNS' ) {
// Read transparency info
$t = $this->_readstream( $f, $n );
if ( $ct == 0 ) {
$trns = [ ord( substr( $t, 1, 1 ) ) ];
} elseif ( $ct == 2 ) {
$trns = [ ord( substr( $t, 1, 1 ) ), ord( substr( $t, 3, 1 ) ), ord( substr( $t, 5, 1 ) ) ];
} else {
$pos = strpos( $t, chr( 0 ) );
if ( $pos !== false ) {
$trns = [ $pos ];
}
}
$this->_readstream( $f, 4 );
} elseif ( $type == 'IDAT' ) {
// Read image data block
$data .= $this->_readstream( $f, $n );
$this->_readstream( $f, 4 );
} elseif ( $type == 'IEND' ) {
break;
} else {
$this->_readstream( $f, $n + 4 );
}
} while ( $n );
if ( $colspace == 'Indexed' && empty( $pal ) ) {
$this->Error( 'Missing palette in ' . $file );
}
$info = [ 'w' => $w, 'h' => $h, 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'FlateDecode', 'dp' => $dp, 'pal' => $pal, 'trns' => $trns ];
if ( $ct >= 4 ) {
// Extract alpha channel
if ( !function_exists( 'gzuncompress' ) ) {
$this->Error( 'Zlib not available, can\'t handle alpha channel: ' . $file );
}
$data = gzuncompress( $data );
$color = '';
$alpha = '';
if ( $ct == 4 ) {
// Gray image
$len = 2 * $w;
for ( $i = 0; $i < $h; $i++ ) {
$pos = ( 1 + $len ) * $i;
$color .= $data[$pos];
$alpha .= $data[$pos];
$line = substr( $data, $pos + 1, $len );
$color .= preg_replace( '/(.)./s', '$1', $line );
$alpha .= preg_replace( '/.(.)/s', '$1', $line );
}
} else {
// RGB image
$len = 4 * $w;
for ( $i = 0; $i < $h; $i++ ) {
$pos = ( 1 + $len ) * $i;
$color .= $data[$pos];
$alpha .= $data[$pos];
$line = substr( $data, $pos + 1, $len );
$color .= preg_replace( '/(.{3})./s', '$1', $line );
$alpha .= preg_replace( '/.{3}(.)/s', '$1', $line );
}
}
unset( $data );
$data = gzcompress( $color );
$info['smask'] = gzcompress( $alpha );
$this->WithAlpha = true;
if ( $this->PDFVersion < '1.4' ) {
$this->PDFVersion = '1.4';
}
}
$info['data'] = $data;
return $info;
}
protected function _readstream( $f, $n ) {
// Read n bytes from stream
$res = '';
while ( $n > 0 && !feof( $f ) ) {
$s = fread( $f, $n );
if ( $s === false ) {
$this->Error( 'Error while reading stream' );
}
$n -= strlen( $s );
$res .= $s;
}
if ( $n > 0 ) {
$this->Error( 'Unexpected end of stream' );
}
return $res;
}
protected function _readint( $f ) {
// Read a 4-byte integer from stream
$a = unpack( 'Ni', $this->_readstream( $f, 4 ) );
return $a['i'];
}
protected function _parsegif( $file ) {
// Extract info from a GIF file (via PNG conversion)
if ( !function_exists( 'imagepng' ) ) {
$this->Error( 'GD extension is required for GIF support' );
}
if ( !function_exists( 'imagecreatefromgif' ) ) {
$this->Error( 'GD has no GIF read support' );
}
$im = imagecreatefromgif( $file );
if ( !$im ) {
$this->Error( 'Missing or incorrect image file: ' . $file );
}
imageinterlace( $im, 0 );
ob_start();
imagepng( $im );
$data = ob_get_clean();
imagedestroy( $im );
$f = fopen( 'php://temp', 'rb+' );
if ( !$f ) {
$this->Error( 'Unable to create memory stream' );
}
fwrite( $f, $data );
rewind( $f );
$info = $this->_parsepngstream( $f, $file );
fclose( $f );
return $info;
}
protected function _out( $s ) {
// Add a line to the document
if ( $this->state == 2 ) {
$this->pages[$this->page] .= $s . "\n";
} elseif ( $this->state == 1 ) {
$this->_put( $s );
} elseif ( $this->state == 0 ) {
$this->Error( 'No page has been added yet' );
} elseif ( $this->state == 3 ) {
$this->Error( 'The document is closed' );
}
}
protected function _put( $s ) {
$this->buffer .= $s . "\n";
}
protected function _getoffset() {
return strlen( $this->buffer );
}
protected function _newobj( $n = null ) {
// Begin a new object
if ( $n === null ) {
$n = ++$this->n;
}
$this->offsets[$n] = $this->_getoffset();
$this->_put( $n . ' 0 obj' );
}
protected function _putstream( $data ) {
$this->_put( 'stream' );
$this->_put( $data );
$this->_put( 'endstream' );
}
protected function _putstreamobject( $data ) {
if ( $this->compress ) {
$entries = '/Filter /FlateDecode ';
$data = gzcompress( $data );
} else {
$entries = '';
}
$entries .= '/Length ' . strlen( $data );
$this->_newobj();
$this->_put( '<<' . $entries . '>>' );
$this->_putstream( $data );
$this->_put( 'endobj' );
}
protected function _putpage( $n ) {
$this->_newobj();
$this->_put( '<</Type /Page' );
$this->_put( '/Parent 1 0 R' );
if ( isset( $this->PageInfo[$n]['size'] ) ) {
$this->_put( sprintf( '/MediaBox [0 0 %.2F %.2F]', $this->PageInfo[$n]['size'][0], $this->PageInfo[$n]['size'][1] ) );
}
if ( isset( $this->PageInfo[$n]['rotation'] ) ) {
$this->_put( '/Rotate ' . $this->PageInfo[$n]['rotation'] );
}
$this->_put( '/Resources 2 0 R' );
if ( isset( $this->PageLinks[$n] ) ) {
// Links
$annots = '/Annots [';
foreach ( $this->PageLinks[$n] as $pl ) {
$rect = sprintf( '%.2F %.2F %.2F %.2F', $pl[0], $pl[1], $pl[0] + $pl[2], $pl[1] - $pl[3] );
$annots .= '<</Type /Annot /Subtype /Link /Rect [' . $rect . '] /Border [0 0 0] ';
if ( is_string( $pl[4] ) ) {
$annots .= '/A <</S /URI /URI ' . $this->_textstring( $pl[4] ) . '>>>>';
} else {
$l = $this->links[$pl[4]];
if ( isset( $this->PageInfo[$l[0]]['size'] ) ) {
$h = $this->PageInfo[$l[0]]['size'][1];
} else {
$h = ( $this->DefOrientation == 'P' ) ? $this->DefPageSize[1] * $this->k : $this->DefPageSize[0] * $this->k;
}
$annots .= sprintf( '/Dest [%d 0 R /XYZ 0 %.2F null]>>', $this->PageInfo[$l[0]]['n'], $h - $l[1] * $this->k );
}
}
$this->_put( $annots . ']' );
}
if ( $this->WithAlpha ) {
$this->_put( '/Group <</Type /Group /S /Transparency /CS /DeviceRGB>>' );
}
$this->_put( '/Contents ' . ( $this->n + 1 ) . ' 0 R>>' );
$this->_put( 'endobj' );
// Page content
if ( !empty( $this->AliasNbPages ) ) {
$alias = $this->UTF8ToUTF16BE( $this->AliasNbPages, false );
$r = $this->UTF8ToUTF16BE( $this->page, false );
$this->pages[$n] = str_replace( $alias, $r, $this->pages[$n] );
// Now repeat for no pages in non-subset fonts
$this->pages[$n] = str_replace( $this->AliasNbPages, $this->page, $this->pages[$n] );
}
$this->_putstreamobject( $this->pages[$n] );
}
protected function _putpages() {
$nb = $this->page;
for ( $n = 1; $n <= $nb; $n++ ) {
$this->PageInfo[$n]['n'] = $this->n + 1 + 2 * ( $n - 1 );
}
for ( $n = 1; $n <= $nb; $n++ ) {
$this->_putpage( $n );
}
// Pages root
$this->_newobj( 1 );
$this->_put( '<</Type /Pages' );
$kids = '/Kids [';
for ( $n = 1; $n <= $nb; $n++ ) {
$kids .= $this->PageInfo[$n]['n'] . ' 0 R ';
}
$this->_put( $kids . ']' );
$this->_put( '/Count ' . $nb );
if ( $this->DefOrientation == 'P' ) {
$w = $this->DefPageSize[0];
$h = $this->DefPageSize[1];
} else {
$w = $this->DefPageSize[1];
$h = $this->DefPageSize[0];
}
$this->_put( sprintf( '/MediaBox [0 0 %.2F %.2F]', $w * $this->k, $h * $this->k ) );
$this->_put( '>>' );
$this->_put( 'endobj' );
}
protected function _putfonts() {
foreach ( $this->FontFiles as $file => $info ) {
if ( !isset( $info['type'] ) || $info['type'] != 'TTF' ) {
// Font file embedding
$this->_newobj();
$this->FontFiles[$file]['n'] = $this->n;
$font = file_get_contents( $this->fontpath . $file, true );
if ( !$font ) {
$this->Error( 'Font file not found: ' . $file );
}
$compressed = ( substr( $file, -2 ) == '.z' );
if ( !$compressed && isset( $info['length2'] ) ) {
$font = substr( $font, 6, $info['length1'] ) . substr( $font, 6 + $info['length1'] + 6, $info['length2'] );
}
$this->_put( '<</Length ' . strlen( $font ) );
if ( $compressed ) {
$this->_put( '/Filter /FlateDecode' );
}
$this->_put( '/Length1 ' . $info['length1'] );
if ( isset( $info['length2'] ) ) {
$this->_put( '/Length2 ' . $info['length2'] . ' /Length3 0' );
}
$this->_put( '>>' );
$this->_putstream( $font );
$this->_put( 'endobj' );
}
}
foreach ( $this->fonts as $k => $font ) {
// Encoding
if ( isset( $font['diff'] ) ) {
if ( !isset( $this->encodings[$font['enc']] ) ) {
$this->_newobj();
$this->_put( '<</Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences [' . $font['diff'] . ']>>' );
$this->_put( 'endobj' );
$this->encodings[$font['enc']] = $this->n;
}
}
// ToUnicode CMap
if ( isset( $font['uv'] ) ) {
if ( isset( $font['enc'] ) ) {
$cmapkey = $font['enc'];
} else {
$cmapkey = $font['name'];
}
if ( !isset( $this->cmaps[$cmapkey] ) ) {
$cmap = $this->_tounicodecmap( $font['uv'] );
$this->_putstreamobject( $cmap );
$this->cmaps[$cmapkey] = $this->n;
}
}
// Font object
$type = $font['type'];
$name = $font['name'];
if ( $type == 'Core' ) {
// Core font
$this->fonts[$k]['n'] = $this->n + 1;
$this->_newobj();
$this->_put( '<</Type /Font' );
$this->_put( '/BaseFont /' . $name );
$this->_put( '/Subtype /Type1' );
if ( $name != 'Symbol' && $name != 'ZapfDingbats' ) {
$this->_put( '/Encoding /WinAnsiEncoding' );
}
if ( isset( $font['uv'] ) ) {
$this->_put( '/ToUnicode ' . $this->cmaps[$cmapkey] . ' 0 R' );
}
$this->_put( '>>' );
$this->_put( 'endobj' );
} elseif ( $type == 'Type1' || $type == 'TrueType' ) {
// Additional Type1 or TrueType/OpenType font
if ( isset( $font['subsetted'] ) && $font['subsetted'] ) {
$name = 'AAAAAA+' . $name;
}
$this->fonts[$k]['n'] = $this->n + 1;
$this->_newobj();
$this->_put( '<</Type /Font' );
$this->_put( '/BaseFont /' . $name );
$this->_put( '/Subtype /' . $type );
$this->_put( '/FirstChar 32 /LastChar 255' );
$this->_put( '/Widths ' . ( $this->n + 1 ) . ' 0 R' );
$this->_put( '/FontDescriptor ' . ( $this->n + 2 ) . ' 0 R' );
if ( $font['enc'] ) {
if ( isset( $font['diff'] ) ) {
$this->_put( '/Encoding ' . $this->encodings[$font['enc']] . ' 0 R' );
} else {
$this->_put( '/Encoding /WinAnsiEncoding' );
}
}
if ( isset( $font['uv'] ) ) {
$this->_put( '/ToUnicode ' . $this->cmaps[$cmapkey] . ' 0 R' );
}
$this->_put( '>>' );
$this->_put( 'endobj' );
// Widths
$this->_newobj();
$cw = &$font['cw'];
$s = '[';
for ( $i = 32; $i <= 255; $i++ ) {
$s .= $cw[chr( $i )] . ' ';
}
$this->_put( $s . ']' );
$this->_put( 'endobj' );
// Descriptor
$this->_newobj();
$s = '<</Type /FontDescriptor /FontName /' . $name;
foreach ( $font['desc'] as $k => $v ) {
$s .= ' /' . $k . ' ' . $v;
}
if ( !empty( $font['file'] ) ) {
$s .= ' /FontFile' . ( $type == 'Type1' ? '' : '2' ) . ' ' . $this->FontFiles[$font['file']]['n'] . ' 0 R';
}
$this->_put( $s . '>>' );
$this->_put( 'endobj' );
}
// TrueType embedded SUBSETS or FULL
elseif ( $type == 'TTF' ) {
$this->fonts[$k]['n'] = $this->n + 1;
require_once $this->fontpath . 'unifont/ttfonts.php';
$ttf = new \Etn\Utils\Font\Unifont\TTFontFile();
$fontname = 'MPDFAA' . '+' . $font['name'];
$subset = $font['subset'];
unset( $subset[0] );
$font['ttffile'] = \Wpeventin::utils_dir() . "font/unifont/DejaVuSansCondensed.ttf";
$ttfontstream = $ttf->makeSubset( $font['ttffile'], $subset );
$ttfontsize = strlen( $ttfontstream );
$fontstream = gzcompress( $ttfontstream );
$codeToGlyph = $ttf->codeToGlyph;
unset( $codeToGlyph[0] );
// Type0 Font
// A composite font - a font composed of other fonts, organized hierarchically
$this->_newobj();
$this->_put( '<</Type /Font' );
$this->_put( '/Subtype /Type0' );
$this->_put( '/BaseFont /' . $fontname . '' );
$this->_put( '/Encoding /Identity-H' );
$this->_put( '/DescendantFonts [' . ( $this->n + 1 ) . ' 0 R]' );
$this->_put( '/ToUnicode ' . ( $this->n + 2 ) . ' 0 R' );
$this->_put( '>>' );
$this->_put( 'endobj' );
// CIDFontType2
// A CIDFont whose glyph descriptions are based on TrueType font technology
$this->_newobj();
$this->_put( '<</Type /Font' );
$this->_put( '/Subtype /CIDFontType2' );
$this->_put( '/BaseFont /' . $fontname . '' );
$this->_put( '/CIDSystemInfo ' . ( $this->n + 2 ) . ' 0 R' );
$this->_put( '/FontDescriptor ' . ( $this->n + 3 ) . ' 0 R' );
if ( isset( $font['desc']['MissingWidth'] ) ) {
$this->_out( '/DW ' . $font['desc']['MissingWidth'] . '' );
}
$this->_putTTfontwidths( $font, $ttf->maxUni );
$this->_put( '/CIDToGIDMap ' . ( $this->n + 4 ) . ' 0 R' );
$this->_put( '>>' );
$this->_put( 'endobj' );
// ToUnicode
$this->_newobj();
$toUni = "/CIDInit /ProcSet findresource begin\n";
$toUni .= "12 dict begin\n";
$toUni .= "begincmap\n";
$toUni .= "/CIDSystemInfo\n";
$toUni .= "<</Registry (Adobe)\n";
$toUni .= "/Ordering (UCS)\n";
$toUni .= "/Supplement 0\n";
$toUni .= ">> def\n";
$toUni .= "/CMapName /Adobe-Identity-UCS def\n";
$toUni .= "/CMapType 2 def\n";
$toUni .= "1 begincodespacerange\n";
$toUni .= "<0000> <FFFF>\n";
$toUni .= "endcodespacerange\n";
$toUni .= "1 beginbfrange\n";
$toUni .= "<0000> <FFFF> <0000>\n";
$toUni .= "endbfrange\n";
$toUni .= "endcmap\n";
$toUni .= "CMapName currentdict /CMap defineresource pop\n";
$toUni .= "end\n";
$toUni .= "end";
$this->_put( '<</Length ' . ( strlen( $toUni ) ) . '>>' );
$this->_putstream( $toUni );
$this->_put( 'endobj' );
// CIDSystemInfo dictionary
$this->_newobj();
$this->_put( '<</Registry (Adobe)' );
$this->_put( '/Ordering (UCS)' );
$this->_put( '/Supplement 0' );
$this->_put( '>>' );
$this->_put( 'endobj' );
// Font descriptor
$this->_newobj();
$this->_put( '<</Type /FontDescriptor' );
$this->_put( '/FontName /' . $fontname );
foreach ( $font['desc'] as $kd => $v ) {
if ( $kd == 'Flags' ) {$v = $v | 4;
$v = $v & ~32;}
// SYMBOLIC font flag
$this->_out( ' /' . $kd . ' ' . $v );
}
$this->_put( '/FontFile2 ' . ( $this->n + 2 ) . ' 0 R' );
$this->_put( '>>' );
$this->_put( 'endobj' );
// Embed CIDToGIDMap
// A specification of the mapping from CIDs to glyph indices
$cidtogidmap = '';
$cidtogidmap = str_pad( '', 256 * 256 * 2, "\x00" );
foreach ( $codeToGlyph as $cc => $glyph ) {
$cidtogidmap[$cc * 2] = chr( $glyph >> 8 );
$cidtogidmap[$cc * 2 + 1] = chr( $glyph & 0xFF );
}
$cidtogidmap = gzcompress( $cidtogidmap );
$this->_newobj();
$this->_put( '<</Length ' . strlen( $cidtogidmap ) . '' );
$this->_put( '/Filter /FlateDecode' );
$this->_put( '>>' );
$this->_putstream( $cidtogidmap );
$this->_put( 'endobj' );
//Font file
$this->_newobj();
$this->_put( '<</Length ' . strlen( $fontstream ) );
$this->_put( '/Filter /FlateDecode' );
$this->_put( '/Length1 ' . $ttfontsize );
$this->_put( '>>' );
$this->_putstream( $fontstream );
$this->_put( 'endobj' );
unset( $ttf );
} else {
// Allow for additional types
$this->fonts[$k]['n'] = $this->n + 1;
$mtd = '_put' . strtolower( $type );
if ( !method_exists( $this, $mtd ) ) {
$this->Error( 'Unsupported font type: ' . $type );
}
$this->$mtd( $font );
}
}
}
protected function _putTTfontwidths( &$font, $maxUni ) {
if ( file_exists( $font['unifilename'] . '.cw127.php' ) ) {
include $font['unifilename'] . '.cw127.php';
$startcid = 128;
} else {
$rangeid = 0;
$range = [];
$prevcid = -2;
$prevwidth = -1;
$interval = false;
$startcid = 1;
}
$cwlen = $maxUni + 1;
// for each character
for ( $cid = $startcid; $cid < $cwlen; $cid++ ) {
if ( $cid == 128 && ( !file_exists( $font['unifilename'] . '.cw127.php' ) ) ) {
if ( is_writable( dirname( $this->fontpath . 'unifont/x' ) ) ) {
$fh = fopen( $font['unifilename'] . '.cw127.php', "wb" );
$cw127 = '<?php' . "\n";
$cw127 .= '$rangeid=' . $rangeid . ";\n";
$cw127 .= '$prevcid=' . $prevcid . ";\n";
$cw127 .= '$prevwidth=' . $prevwidth . ";\n";
if ( $interval ) {$cw127 .= '$interval=true' . ";\n";} else { $cw127 .= '$interval=false' . ";\n";}
$cw127 .= '$range=' . var_export( $range, true ) . ";\n";
$cw127 .= "?>";
fwrite( $fh, $cw127, strlen( $cw127 ) );
fclose( $fh );
}
}
if ( ( !isset( $font['cw'][$cid * 2] ) || !isset( $font['cw'][$cid * 2 + 1] ) ) ||
( $font['cw'][$cid * 2] == "\00" && $font['cw'][$cid * 2 + 1] == "\00" ) ) {continue;}
$width = ( ord( $font['cw'][$cid * 2] ) << 8 ) + ord( $font['cw'][$cid * 2 + 1] );
if ( $width == 65535 ) {$width = 0;}
if ( $cid > 255 && ( !isset( $font['subset'][$cid] ) || !$font['subset'][$cid] ) ) {continue;}
if ( !isset( $font['dw'] ) || ( isset( $font['dw'] ) && $width != $font['dw'] ) ) {
if ( $cid == ( $prevcid + 1 ) ) {
if ( $width == $prevwidth ) {
if ( $width == $range[$rangeid][0] ) {
$range[$rangeid][] = $width;
} else {
array_pop( $range[$rangeid] );
// new range
$rangeid = $prevcid;
$range[$rangeid] = [];
$range[$rangeid][] = $prevwidth;
$range[$rangeid][] = $width;
}
$interval = true;
$range[$rangeid]['interval'] = true;
} else {
if ( $interval ) {
// new range
$rangeid = $cid;
$range[$rangeid] = [];
$range[$rangeid][] = $width;
} else { $range[$rangeid][] = $width;}
$interval = false;
}
} else {
$rangeid = $cid;
$range[$rangeid] = [];
$range[$rangeid][] = $width;
$interval = false;
}
$prevcid = $cid;
$prevwidth = $width;
}
}
$prevk = -1;
$nextk = -1;
$prevint = false;
foreach ( $range as $k => $ws ) {
$cws = count( $ws );
if ( ( $k == $nextk ) AND ( !$prevint ) AND ( ( !isset( $ws['interval'] ) ) OR ( $cws < 4 ) ) ) {
if ( isset( $range[$k]['interval'] ) ) {unset( $range[$k]['interval'] );}
$range[$prevk] = array_merge( $range[$prevk], $range[$k] );
unset( $range[$k] );
} else { $prevk = $k;}
$nextk = $k + $cws;
if ( isset( $ws['interval'] ) ) {
if ( $cws > 3 ) {$prevint = true;} else { $prevint = false;}
unset( $range[$k]['interval'] );
--$nextk;
} else { $prevint = false;}
}
$w = '';
foreach ( $range as $k => $ws ) {
if ( count( array_count_values( $ws ) ) == 1 ) {$w .= ' ' . $k . ' ' . ( $k + count( $ws ) - 1 ) . ' ' . $ws[0];} else { $w .= ' ' . $k . ' [ ' . implode( ' ', $ws ) . ' ]' . "\n";}
}
$this->_out( '/W [' . $w . ' ]' );
}
protected function _tounicodecmap( $uv ) {
$ranges = '';
$nbr = 0;
$chars = '';
$nbc = 0;
foreach ( $uv as $c => $v ) {
if ( is_array( $v ) ) {
$ranges .= sprintf( "<%02X> <%02X> <%04X>\n", $c, $c + $v[1] - 1, $v[0] );
$nbr++;
} else {
$chars .= sprintf( "<%02X> <%04X>\n", $c, $v );
$nbc++;
}
}
$s = "/CIDInit /ProcSet findresource begin\n";
$s .= "12 dict begin\n";
$s .= "begincmap\n";
$s .= "/CIDSystemInfo\n";
$s .= "<</Registry (Adobe)\n";
$s .= "/Ordering (UCS)\n";
$s .= "/Supplement 0\n";
$s .= ">> def\n";
$s .= "/CMapName /Adobe-Identity-UCS def\n";
$s .= "/CMapType 2 def\n";
$s .= "1 begincodespacerange\n";
$s .= "<00> <FF>\n";
$s .= "endcodespacerange\n";
if ( $nbr > 0 ) {
$s .= "$nbr beginbfrange\n";
$s .= $ranges;
$s .= "endbfrange\n";
}
if ( $nbc > 0 ) {
$s .= "$nbc beginbfchar\n";
$s .= $chars;
$s .= "endbfchar\n";
}
$s .= "endcmap\n";
$s .= "CMapName currentdict /CMap defineresource pop\n";
$s .= "end\n";
$s .= "end";
return $s;
}
protected function _putimages() {
foreach ( array_keys( $this->images ) as $file ) {
$this->_putimage( $this->images[$file] );
unset( $this->images[$file]['data'] );
unset( $this->images[$file]['smask'] );
}
}
protected function _putimage( &$info ) {
$this->_newobj();
$info['n'] = $this->n;
$this->_put( '<</Type /XObject' );
$this->_put( '/Subtype /Image' );
$this->_put( '/Width ' . $info['w'] );
$this->_put( '/Height ' . $info['h'] );
if ( $info['cs'] == 'Indexed' ) {
$this->_put( '/ColorSpace [/Indexed /DeviceRGB ' . ( strlen( $info['pal'] ) / 3 - 1 ) . ' ' . ( $this->n + 1 ) . ' 0 R]' );
} else {
$this->_put( '/ColorSpace /' . $info['cs'] );
if ( $info['cs'] == 'DeviceCMYK' ) {
$this->_put( '/Decode [1 0 1 0 1 0 1 0]' );
}
}
$this->_put( '/BitsPerComponent ' . $info['bpc'] );
if ( isset( $info['f'] ) ) {
$this->_put( '/Filter /' . $info['f'] );
}
if ( isset( $info['dp'] ) ) {
$this->_put( '/DecodeParms <<' . $info['dp'] . '>>' );
}
if ( isset( $info['trns'] ) && is_array( $info['trns'] ) ) {
$trns = '';
for ( $i = 0; $i < count( $info['trns'] ); $i++ ) {
$trns .= $info['trns'][$i] . ' ' . $info['trns'][$i] . ' ';
}
$this->_put( '/Mask [' . $trns . ']' );
}
if ( isset( $info['smask'] ) ) {
$this->_put( '/SMask ' . ( $this->n + 1 ) . ' 0 R' );
}
$this->_put( '/Length ' . strlen( $info['data'] ) . '>>' );
$this->_putstream( $info['data'] );
$this->_put( 'endobj' );
// Soft mask
if ( isset( $info['smask'] ) ) {
$dp = '/Predictor 15 /Colors 1 /BitsPerComponent 8 /Columns ' . $info['w'];
$smask = [ 'w' => $info['w'], 'h' => $info['h'], 'cs' => 'DeviceGray', 'bpc' => 8, 'f' => $info['f'], 'dp' => $dp, 'data' => $info['smask'] ];
$this->_putimage( $smask );
}
// Palette
if ( $info['cs'] == 'Indexed' ) {
$this->_putstreamobject( $info['pal'] );
}
}
protected function _putxobjectdict() {
foreach ( $this->images as $image ) {
$this->_put( '/I' . $image['i'] . ' ' . $image['n'] . ' 0 R' );
}
}
protected function _putresourcedict() {
$this->_put( '/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]' );
$this->_put( '/Font <<' );
foreach ( $this->fonts as $font ) {
$this->_put( '/F' . $font['i'] . ' ' . $font['n'] . ' 0 R' );
}
$this->_put( '>>' );
$this->_put( '/XObject <<' );
$this->_putxobjectdict();
$this->_put( '>>' );
}
protected function _putresources() {
$this->_putfonts();
$this->_putimages();
// Resource dictionary
$this->_newobj( 2 );
$this->_put( '<<' );
$this->_putresourcedict();
$this->_put( '>>' );
$this->_put( 'endobj' );
}
protected function _putinfo() {
$this->metadata['Producer'] = 'tFPDF ' . tFPDF_VERSION;
$this->metadata['CreationDate'] = 'D:' . @date( 'YmdHis' );
foreach ( $this->metadata as $key => $value ) {
$this->_put( '/' . $key . ' ' . $this->_textstring( $value ) );
}
}
protected function _putcatalog() {
$n = $this->PageInfo[1]['n'];
$this->_put( '/Type /Catalog' );
$this->_put( '/Pages 1 0 R' );
if ( $this->ZoomMode == 'fullpage' ) {
$this->_put( '/OpenAction [' . $n . ' 0 R /Fit]' );
} elseif ( $this->ZoomMode == 'fullwidth' ) {
$this->_put( '/OpenAction [' . $n . ' 0 R /FitH null]' );
} elseif ( $this->ZoomMode == 'real' ) {
$this->_put( '/OpenAction [' . $n . ' 0 R /XYZ null null 1]' );
} elseif ( !is_string( $this->ZoomMode ) ) {
$this->_put( '/OpenAction [' . $n . ' 0 R /XYZ null null ' . sprintf( '%.2F', $this->ZoomMode / 100 ) . ']' );
}
if ( $this->LayoutMode == 'single' ) {
$this->_put( '/PageLayout /SinglePage' );
} elseif ( $this->LayoutMode == 'continuous' ) {
$this->_put( '/PageLayout /OneColumn' );
} elseif ( $this->LayoutMode == 'two' ) {
$this->_put( '/PageLayout /TwoColumnLeft' );
}
}
protected function _putheader() {
$this->_put( '%PDF-' . $this->PDFVersion );
}
protected function _puttrailer() {
$this->_put( '/Size ' . ( $this->n + 1 ) );
$this->_put( '/Root ' . $this->n . ' 0 R' );
$this->_put( '/Info ' . ( $this->n - 1 ) . ' 0 R' );
}
protected function _enddoc() {
$this->_putheader();
$this->_putpages();
$this->_putresources();
// Info
$this->_newobj();
$this->_put( '<<' );
$this->_putinfo();
$this->_put( '>>' );
$this->_put( 'endobj' );
// Catalog
$this->_newobj();
$this->_put( '<<' );
$this->_putcatalog();
$this->_put( '>>' );
$this->_put( 'endobj' );
// Cross-ref
$offset = $this->_getoffset();
$this->_put( 'xref' );
$this->_put( '0 ' . ( $this->n + 1 ) );
$this->_put( '0000000000 65535 f ' );
for ( $i = 1; $i <= $this->n; $i++ ) {
$this->_put( sprintf( '%010d 00000 n ', $this->offsets[$i] ) );
}
// Trailer
$this->_put( 'trailer' );
$this->_put( '<<' );
$this->_puttrailer();
$this->_put( '>>' );
$this->_put( 'startxref' );
$this->_put( $offset );
$this->_put( '%%EOF' );
$this->state = 3;
}
// ********* NEW FUNCTIONS *********
// Converts UTF-8 strings to UTF16-BE.
protected function UTF8ToUTF16BE( $str, $setbom = true ) {
$outstr = "";
if ( $setbom ) {
$outstr .= "\xFE\xFF"; // Byte Order Mark (BOM)
}
$outstr .= mb_convert_encoding( $str, 'UTF-16BE', 'UTF-8' );
return $outstr;
}
// Converts UTF-8 strings to codepoints array
protected function UTF8StringToArray( $str ) {
$out = [];
$len = strlen( $str );
for ( $i = 0; $i < $len; $i++ ) {
$uni = -1;
$h = ord( $str[$i] );
if ( $h <= 0x7F ) {
$uni = $h;
} elseif ( $h >= 0xC2 ) {
if ( ( $h <= 0xDF ) && ( $i < $len - 1 ) ) {
$uni = ( $h & 0x1F ) << 6 | ( ord( $str[++$i] ) & 0x3F );
} elseif ( ( $h <= 0xEF ) && ( $i < $len - 2 ) ) {
$uni = ( $h & 0x0F ) << 12 | ( ord( $str[++$i] ) & 0x3F ) << 6
| ( ord( $str[++$i] ) & 0x3F );
} elseif ( ( $h <= 0xF4 ) && ( $i < $len - 3 ) ) {
$uni = ( $h & 0x0F ) << 18 | ( ord( $str[++$i] ) & 0x3F ) << 12
| ( ord( $str[++$i] ) & 0x3F ) << 6
| ( ord( $str[++$i] ) & 0x3F );
}
}
if ( $uni >= 0 ) {
$out[] = $uni;
}
}
return $out;
}
}
?>