private function readString($name) { $font = $this->font; $size = $font->readUInt16(); $this->data["{$name}Size"] = $size; $this->data[$name] = Font::UTF16ToUTF8($font->read($size)); }
protected function _encode() { if (empty($this->data)) { Font::d(" >> Table is empty"); return 0; } return $this->getFont()->pack($this->def, $this->data); }
protected function _parse() { $font = $this->getFont(); $tableOffset = $font->pos(); $data = $font->unpack(self::$header_format); $records = array(); for ($i = 0; $i < $data["count"]; $i++) { $record = new nameRecord(); $record_data = $font->unpack(nameRecord::$format); $record->map($record_data); $records[] = $record; } $names = array(); foreach ($records as $record) { $font->seek($tableOffset + $data["stringOffset"] + $record->offset); $s = $font->read($record->length); $record->string = Font::UTF16ToUTF8($s); $names[$record->nameID] = $record; } $data["records"] = $names; $this->data = $data; }
static function register_font($style, $remote_file) { $fontname = mb_strtolower($style["family"]); $families = Font_Metrics::get_font_families(); $entry = array(); if (isset($families[$fontname])) { $entry = $families[$fontname]; } $local_file = DOMPDF_FONT_DIR . md5($remote_file); $cache_entry = $local_file; $local_file .= ".ttf"; $style_string = Font_Metrics::get_type("{$style['weight']} {$style['style']}"); if (!isset($entry[$style_string])) { $entry[$style_string] = $cache_entry; Font_Metrics::set_font_family($fontname, $entry); // Download the remote file if (!is_file($local_file)) { file_put_contents($local_file, file_get_contents($remote_file)); } $font = Font::load($local_file); if (!$font) { return false; } $font->parse(); $font->saveAdobeFontMetrics("{$cache_entry}.ufm"); // Save the changes Font_Metrics::save_font_families(); } return true; }
/** * Installs a new font family * This function maps a font-family name to a font. It tries to locate the * bold, italic, and bold italic versions of the font as well. Once the * files are located, ttf versions of the font are copied to the fonts * directory. Changes to the font lookup table are saved to the cache. * * @param string $fontname the font-family name * @param string $normal the filename of the normal face font subtype * @param string $bold the filename of the bold face font subtype * @param string $italic the filename of the italic face font subtype * @param string $bold_italic the filename of the bold italic face font subtype * * @throws Exception */ function install_font_family($dompdf, $fontname, $normal, $bold = null, $italic = null, $bold_italic = null) { $fontMetrics = $dompdf->getFontMetrics(); // Check if the base filename is readable if (!is_readable($normal)) { throw new Exception("Unable to read '{$normal}'."); } $dir = dirname($normal); $basename = basename($normal); $last_dot = strrpos($basename, '.'); if ($last_dot !== false) { $file = substr($basename, 0, $last_dot); $ext = strtolower(substr($basename, $last_dot)); } else { $file = $basename; $ext = ''; } if (!in_array($ext, array(".ttf", ".otf"))) { throw new Exception("Unable to process fonts of type '{$ext}'."); } // Try $file_Bold.$ext etc. $path = "{$dir}/{$file}"; $patterns = array("bold" => array("_Bold", "b", "B", "bd", "BD"), "italic" => array("_Italic", "i", "I"), "bold_italic" => array("_Bold_Italic", "bi", "BI", "ib", "IB")); foreach ($patterns as $type => $_patterns) { if (!isset(${$type}) || !is_readable(${$type})) { foreach ($_patterns as $_pattern) { if (is_readable("{$path}{$_pattern}{$ext}")) { ${$type} = "{$path}{$_pattern}{$ext}"; break; } } if (is_null(${$type})) { echo "Unable to find {$type} face file.\n"; } } } $fonts = compact("normal", "bold", "italic", "bold_italic"); $entry = array(); // Copy the files to the font directory. foreach ($fonts as $var => $src) { if (is_null($src)) { $entry[$var] = $dompdf->getOptions()->get('fontDir') . '/' . mb_substr(basename($normal), 0, -4); continue; } // Verify that the fonts exist and are readable if (!is_readable($src)) { throw new Exception("Requested font '{$src}' is not readable"); } $dest = $dompdf->getOptions()->get('fontDir') . '/' . basename($src); if (!is_writeable(dirname($dest))) { throw new Exception("Unable to write to destination '{$dest}'."); } echo "Copying {$src} to {$dest}...\n"; if (!copy($src, $dest)) { throw new Exception("Unable to copy '{$src}' to '{$dest}'"); } $entry_name = mb_substr($dest, 0, -4); echo "Generating Adobe Font Metrics for {$entry_name}...\n"; $font_obj = Font::load($dest); $font_obj->saveAdobeFontMetrics("{$entry_name}.ufm"); $entry[$var] = $entry_name; } // Store the fonts in the lookup table $fontMetrics->setFontFamily($fontname, $entry); // Save the changes $fontMetrics->saveFontFamilies(); }
public function testLoadTTFFontSuccessfully() { $trueTypeFont = Font::load('sample-fonts/IntelClear-Light.ttf'); $this->assertInstanceOf('FontLib\\TrueType\\File', $trueTypeFont); }
function encode($entry_offset) { Font::d("\n==== {$this->tag} ===="); //Font::d("Entry offset = $entry_offset"); $data = $this->font_table; $font = $this->font; $table_offset = $font->pos(); $this->offset = $table_offset; $table_length = $data->encode(); $font->seek($table_offset); $table_data = $font->read($table_length); $font->seek($entry_offset); $font->write($this->tag, 4); $font->writeUInt32(self::computeChecksum($table_data)); $font->writeUInt32($table_offset); $font->writeUInt32($table_length); Font::d("Bytes written = {$table_length}"); $font->seek($table_offset + $table_length); }
use FontLib\Font; use FontLib\Binary_Stream; $fontfile = null; if (isset($_GET["fontfile"])) { $fontfile = basename($_GET["fontfile"]); $fontfile = "../fonts/{$fontfile}"; } if (!file_exists($fontfile)) { return; } $name = isset($_GET["name"]) ? $_GET["name"] : null; if (isset($_POST["subset"])) { $subset = $_POST["subset"]; ob_start(); require_once "../src/FontLib/Autoloader.php"; $font = Font::load($fontfile); $font->parse(); $font->setSubset($subset); $font->reduce(); $new_filename = basename($fontfile); $dot = strpos($new_filename, "."); $new_filename = substr($new_filename, 0, $dot) . "-subset" . substr($new_filename, $dot); header("Content-Type: font/truetype"); header("Content-Disposition: attachment; filename=\"{$new_filename}\""); $tmp = tempnam(sys_get_temp_dir(), "fnt"); $font->open($tmp, Binary_Stream::modeWrite); $font->encode(array("OS/2")); $font->close(); ob_end_clean(); readfile($tmp); unlink($tmp);
$stream->w($type, $data); $stream->seek(0); $new_data = $stream->r($type); if ($new_data !== $data) { echo "NOT OK \t $data \t => $new_data<br />"; } else { echo "OK $type<br />"; } }*/ // font RW $filename = "../fonts/DejaVuSansMono.ttf"; $filename_out = "{$filename}.2.ttf"; Font::$debug = true; $font = Font::load($filename); $font->parse(); $font->setSubset("(.apbiI,mn"); $font->reduce(); $font->open($filename_out, Binary_Stream::modeWrite); $font->encode(array("OS/2")); ?> File size: <?php echo number_format(filesize($filename_out), 0, ".", " "); ?> bytes Memory: <?php echo memory_get_peak_usage(true) / 1024; ?> KB
public function getUTF16() { return Font::UTF8ToUTF16($this->string); }
function encode($tags = array()) { if (!self::$raw) { $tags = array_merge(array("head", "hhea", "cmap", "hmtx", "maxp", "glyf", "loca", "name", "post"), $tags); } else { $tags = array_keys($this->directory); } $num_tables = count($tags); $n = 16; // @todo Font::d("Tables : " . implode(", ", $tags)); /** @var DirectoryEntry[] $entries */ $entries = array(); foreach ($tags as $tag) { if (!isset($this->directory[$tag])) { Font::d(" >> '{$tag}' table doesn't exist"); continue; } $entries[$tag] = $this->directory[$tag]; } $this->header->data["numTables"] = $num_tables; $this->header->encode(); $directory_offset = $this->pos(); $offset = $directory_offset + $num_tables * $n; $this->seek($offset); $i = 0; foreach ($entries as $entry) { $entry->encode($directory_offset + $i * $n); $i++; } }
/** * @param array $style * @param string $remoteFile * @param resource $context * @return bool */ public function registerFont($style, $remoteFile, $context = null) { $fontDir = $this->getOptions()->getFontDir(); $fontname = mb_strtolower($style["family"]); $families = $this->getFontFamilies(); $entry = array(); if (isset($families[$fontname])) { $entry = $families[$fontname]; } $localFile = $fontDir . DIRECTORY_SEPARATOR . md5($remoteFile); $localTempFile = $this->options->get('tempDir') . "/" . md5($remoteFile); $cacheEntry = $localFile; $localFile .= ".ttf"; $styleString = $this->getType("{$style['weight']} {$style['style']}"); if (!isset($entry[$styleString])) { $entry[$styleString] = $cacheEntry; // Download the remote file file_put_contents($localTempFile, file_get_contents($remoteFile, null, $context)); $font = Font::load($localTempFile); if (!$font) { unlink($localTempFile); return false; } $font->parse(); $font->saveAdobeFontMetrics("{$cacheEntry}.ufm"); unlink($localTempFile); if (!file_exists("{$cacheEntry}.ufm")) { return false; } // Save the changes file_put_contents($localFile, file_get_contents($remoteFile, null, $context)); if (!file_exists($localFile)) { unlink("{$cacheEntry}.ufm"); return false; } $this->setFontFamily($fontname, $entry); $this->saveFontFamilies(); } return true; }
/** * if the font is not loaded then load it and make the required object * else just make it the current font * the encoding array can contain 'encoding'=> 'none','WinAnsiEncoding','MacRomanEncoding' or 'MacExpertEncoding' * note that encoding='none' will need to be used for symbolic fonts * and 'differences' => an array of mappings between numbers 0->255 and character names. * */ function selectFont($fontName, $encoding = '', $set = true) { $ext = substr($fontName, -4); if ($ext === '.afm' || $ext === '.ufm') { $fontName = substr($fontName, 0, mb_strlen($fontName) - 4); } if (!isset($this->fonts[$fontName])) { $this->addMessage("selectFont: selecting - {$fontName} - {$encoding}, {$set}"); // load the file $this->openFont($fontName); if (isset($this->fonts[$fontName])) { $this->numObj++; $this->numFonts++; $font =& $this->fonts[$fontName]; //$this->numFonts = md5($fontName); $pos = strrpos($fontName, '/'); // $dir = substr($fontName,0,$pos+1); $name = substr($fontName, $pos + 1); $options = array('name' => $name, 'fontFileName' => $fontName); if (is_array($encoding)) { // then encoding and differences might be set if (isset($encoding['encoding'])) { $options['encoding'] = $encoding['encoding']; } if (isset($encoding['differences'])) { $options['differences'] = $encoding['differences']; } } else { if (mb_strlen($encoding, '8bit')) { // then perhaps only the encoding has been set $options['encoding'] = $encoding; } } $fontObj = $this->numObj; $this->o_font($this->numObj, 'new', $options); $font['fontNum'] = $this->numFonts; // if this is a '.afm' font, and there is a '.pfa' file to go with it ( as there // should be for all non-basic fonts), then load it into an object and put the // references into the font object $basefile = $fontName; $fbtype = ''; if (file_exists("{$basefile}.pfb")) { $fbtype = 'pfb'; } else { if (file_exists("{$basefile}.ttf")) { $fbtype = 'ttf'; } } $fbfile = "{$basefile}.{$fbtype}"; // $pfbfile = substr($fontName,0,strlen($fontName)-4).'.pfb'; // $ttffile = substr($fontName,0,strlen($fontName)-4).'.ttf'; $this->addMessage('selectFont: checking for - ' . $fbfile); // OAR - I don't understand this old check // if (substr($fontName, -4) === '.afm' && strlen($fbtype)) { if ($fbtype) { $adobeFontName = isset($font['PostScriptName']) ? $font['PostScriptName'] : $font['FontName']; // $fontObj = $this->numObj; $this->addMessage("selectFont: adding font file - {$fbfile} - {$adobeFontName}"); // find the array of font widths, and put that into an object. $firstChar = -1; $lastChar = 0; $widths = array(); $cid_widths = array(); foreach ($font['C'] as $num => $d) { if (intval($num) > 0 || $num == '0') { if (!$font['isUnicode']) { // With Unicode, widths array isn't used if ($lastChar > 0 && $num > $lastChar + 1) { for ($i = $lastChar + 1; $i < $num; $i++) { $widths[] = 0; } } } $widths[] = $d; if ($font['isUnicode']) { $cid_widths[$num] = $d; } if ($firstChar == -1) { $firstChar = $num; } $lastChar = $num; } } // also need to adjust the widths for the differences array if (isset($options['differences'])) { foreach ($options['differences'] as $charNum => $charName) { if ($charNum > $lastChar) { if (!$font['isUnicode']) { // With Unicode, widths array isn't used for ($i = $lastChar + 1; $i <= $charNum; $i++) { $widths[] = 0; } } $lastChar = $charNum; } if (isset($font['C'][$charName])) { $widths[$charNum - $firstChar] = $font['C'][$charName]; if ($font['isUnicode']) { $cid_widths[$charName] = $font['C'][$charName]; } } } } if ($font['isUnicode']) { $font['CIDWidths'] = $cid_widths; } $this->addMessage('selectFont: FirstChar = ' . $firstChar); $this->addMessage('selectFont: LastChar = ' . $lastChar); $widthid = -1; if (!$font['isUnicode']) { // With Unicode, widths array isn't used $this->numObj++; $this->o_contents($this->numObj, 'new', 'raw'); $this->objects[$this->numObj]['c'] .= '[' . implode(' ', $widths) . ']'; $widthid = $this->numObj; } $missing_width = 500; $stemV = 70; if (isset($font['MissingWidth'])) { $missing_width = $font['MissingWidth']; } if (isset($font['StdVW'])) { $stemV = $font['StdVW']; } else { if (isset($font['Weight']) && preg_match('!(bold|black)!i', $font['Weight'])) { $stemV = 120; } } // load the pfb file, and put that into an object too. // note that pdf supports only binary format type 1 font files, though there is a // simple utility to convert them from pfa to pfb. // FIXME: should we move font subset creation to CPDF::output? See notes in issue #750. if (!$this->isUnicode || $fbtype !== 'ttf' || empty($this->stringSubsets)) { $data = file_get_contents($fbfile); } else { $this->stringSubsets[$fontName][] = 32; // Force space if not in yet $subset = $this->stringSubsets[$fontName]; sort($subset); // Load font $font_obj = Font::load($fbfile); $font_obj->parse(); // Define subset $font_obj->setSubset($subset); $font_obj->reduce(); // Write new font $tmp_name = $this->tmp . "/" . basename($fbfile) . ".tmp." . uniqid(); $font_obj->open($tmp_name, BinaryStream::modeWrite); $font_obj->encode(array("OS/2")); $font_obj->close(); // Parse the new font to get cid2gid and widths $font_obj = Font::load($tmp_name); // Find Unicode char map table $subtable = null; foreach ($font_obj->getData("cmap", "subtables") as $_subtable) { if ($_subtable["platformID"] == 0 || $_subtable["platformID"] == 3 && $_subtable["platformSpecificID"] == 1) { $subtable = $_subtable; break; } } if ($subtable) { $glyphIndexArray = $subtable["glyphIndexArray"]; $hmtx = $font_obj->getData("hmtx"); unset($glyphIndexArray[0xffff]); $cidtogid = str_pad('', max(array_keys($glyphIndexArray)) * 2 + 1, ""); $font['CIDWidths'] = array(); foreach ($glyphIndexArray as $cid => $gid) { if ($cid >= 0 && $cid < 0xffff && $gid) { $cidtogid[$cid * 2] = chr($gid >> 8); $cidtogid[$cid * 2 + 1] = chr($gid & 0xff); } $width = $font_obj->normalizeFUnit(isset($hmtx[$gid]) ? $hmtx[$gid][0] : $hmtx[0][0]); $font['CIDWidths'][$cid] = $width; } $font['CIDtoGID'] = base64_encode(gzcompress($cidtogid)); $font['CIDtoGID_Compressed'] = true; $data = file_get_contents($tmp_name); } else { $data = file_get_contents($fbfile); } $font_obj->close(); unlink($tmp_name); } // create the font descriptor $this->numObj++; $fontDescriptorId = $this->numObj; $this->numObj++; $pfbid = $this->numObj; // determine flags (more than a little flakey, hopefully will not matter much) $flags = 0; if ($font['ItalicAngle'] != 0) { $flags += pow(2, 6); } if ($font['IsFixedPitch'] === 'true') { $flags += 1; } $flags += pow(2, 5); // assume non-sybolic $list = array('Ascent' => 'Ascender', 'CapHeight' => 'Ascender', 'MissingWidth' => 'MissingWidth', 'Descent' => 'Descender', 'FontBBox' => 'FontBBox', 'ItalicAngle' => 'ItalicAngle'); $fdopt = array('Flags' => $flags, 'FontName' => $adobeFontName, 'StemV' => $stemV); foreach ($list as $k => $v) { if (isset($font[$v])) { $fdopt[$k] = $font[$v]; } } if ($fbtype === 'pfb') { $fdopt['FontFile'] = $pfbid; } else { if ($fbtype === 'ttf') { $fdopt['FontFile2'] = $pfbid; } } $this->o_fontDescriptor($fontDescriptorId, 'new', $fdopt); // embed the font program $this->o_contents($this->numObj, 'new'); $this->objects[$pfbid]['c'] .= $data; // determine the cruicial lengths within this file if ($fbtype === 'pfb') { $l1 = strpos($data, 'eexec') + 6; $l2 = strpos($data, '00000000') - $l1; $l3 = mb_strlen($data, '8bit') - $l2 - $l1; $this->o_contents($this->numObj, 'add', array('Length1' => $l1, 'Length2' => $l2, 'Length3' => $l3)); } else { if ($fbtype == 'ttf') { $l1 = mb_strlen($data, '8bit'); $this->o_contents($this->numObj, 'add', array('Length1' => $l1)); } } // tell the font object about all this new stuff $tmp = array('BaseFont' => $adobeFontName, 'MissingWidth' => $missing_width, 'Widths' => $widthid, 'FirstChar' => $firstChar, 'LastChar' => $lastChar, 'FontDescriptor' => $fontDescriptorId); if ($fbtype === 'ttf') { $tmp['SubType'] = 'TrueType'; } $this->addMessage("adding extra info to font.({$fontObj})"); foreach ($tmp as $fk => $fv) { $this->addMessage("{$fk} : {$fv}"); } $this->o_font($fontObj, 'add', $tmp); } else { $this->addMessage('selectFont: pfb or ttf file not found, ok if this is one of the 14 standard fonts'); } // also set the differences here, note that this means that these will take effect only the //first time that a font is selected, else they are ignored if (isset($options['differences'])) { $font['differences'] = $options['differences']; } } } if ($set && isset($this->fonts[$fontName])) { // so if for some reason the font was not set in the last one then it will not be selected $this->currentBaseFont = $fontName; // the next lines mean that if a new font is selected, then the current text state will be // applied to it as well. $this->currentFont = $this->currentBaseFont; $this->currentFontNum = $this->fonts[$this->currentFont]['fontNum']; //$this->setCurrentFont(); } return $this->currentFontNum; //return $this->numObj; }