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 */ function install_font_family($fontname, $normal, $bold = null, $italic = null, $bold_italic = null) { Font_Metrics::init(); // Check if the base filename is readable if (!is_readable($normal)) { throw new DOMPDF_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 DOMPDF_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_FONT_DIR . mb_substr(basename($normal), 0, -4); continue; } // Verify that the fonts exist and are readable if (!is_readable($src)) { throw new DOMPDF_Exception("Requested font '{$src}' is not readable"); } $dest = DOMPDF_FONT_DIR . basename($src); if (!is_writeable(dirname($dest))) { throw new DOMPDF_Exception("Unable to write to destination '{$dest}'."); } echo "Copying {$src} to {$dest}...\n"; if (!copy($src, $dest)) { throw new DOMPDF_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 Font_Metrics::set_font_family($fontname, $entry); // Save the changes Font_Metrics::save_font_families(); }
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License */ $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 "../classes/Font.php"; $font = Font::load($fontfile); $font->parse(); $font->setSubset($subset); $font->reduce(); $new_filename = basename($fontfile); $new_filename = substr($new_filename, 0, -4) . "-subset." . substr($new_filename, -3); header("Content-Type: font/truetype"); header("Content-Disposition: attachment; filename=\"{$new_filename}\""); $tmp = tempnam(sys_get_temp_dir(), "fnt"); $font->open($tmp, Font_Binary_Stream::modeWrite); $font->encode(array("OS/2")); $font->close(); ob_end_clean(); readfile($tmp); unlink($tmp); return;
$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, Font_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
/** * 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'; } elseif (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']; } elseif (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. if (!$this->isUnicode || $fbtype !== 'ttf' || empty($this->stringSubsets)) { $data = file_get_contents($fbfile); } else { require_once dirname(__FILE__) . "/php-font-lib/classes/font.cls.php"; $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 = "{$fbfile}.tmp." . sprintf("%u", crc32(implode($subset))); $font_obj->open($tmp_name, Font_Binary_Stream::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' => 'CapHeight', '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; }
static function install_fonts($files) { $names = array(); foreach ($files as $file) { $font = Font::load($file); $records = $font->getData("name", "records"); $type = self::get_type($records[2]); $names[mb_strtolower($records[1])][$type] = $file; } return $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}"); $this->openFont($fontName); if (isset($this->fonts[$fontName])) { $this->numObj++; $this->numFonts++; $font =& $this->fonts[$fontName]; $pos = strrpos($fontName, '/'); $name = substr($fontName, $pos + 1); $options = array('name' => $name, 'fontFileName' => $fontName); if (is_array($encoding)) { if (isset($encoding['encoding'])) { $options['encoding'] = $encoding['encoding']; } if (isset($encoding['differences'])) { $options['differences'] = $encoding['differences']; } } else { if (mb_strlen($encoding, '8bit')) { $options['encoding'] = $encoding; } } $fontObj = $this->numObj; $this->o_font($this->numObj, 'new', $options); $font['fontNum'] = $this->numFonts; $basefile = $fontName; $fbtype = ''; if (file_exists("{$basefile}.pfb")) { $fbtype = 'pfb'; } elseif (file_exists("{$basefile}.ttf")) { $fbtype = 'ttf'; } $fbfile = "{$basefile}.{$fbtype}"; $this->addMessage('selectFont: checking for - ' . $fbfile); if ($fbtype) { $adobeFontName = isset($font['PostScriptName']) ? $font['PostScriptName'] : $font['FontName']; $this->addMessage("selectFont: adding font file - {$fbfile} - {$adobeFontName}"); $firstChar = -1; $lastChar = 0; $widths = array(); $cid_widths = array(); foreach ($font['C'] as $num => $d) { if (intval($num) > 0 || $num == '0') { if (!$font['isUnicode']) { 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; } } if (isset($options['differences'])) { foreach ($options['differences'] as $charNum => $charName) { if ($charNum > $lastChar) { if (!$font['isUnicode']) { 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']) { $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']; } elseif (isset($font['Weight']) && preg_match('!(bold|black)!i', $font['Weight'])) { $stemV = 120; } if (!$this->isUnicode || $fbtype !== 'ttf' || empty($this->stringSubsets)) { $data = file_get_contents($fbfile); } else { require_once dirname(__FILE__) . "/php-font-lib/classes/font.cls.php"; $this->stringSubsets[$fontName][] = 32; $subset = $this->stringSubsets[$fontName]; sort($subset); $font_obj = Font::load($fbfile); $font_obj->parse(); $font_obj->setSubset($subset); $font_obj->reduce(); $tmp_name = "{$fbfile}.tmp." . sprintf("%u", crc32(implode($subset))); $font_obj->open($tmp_name, Font_Binary_Stream::modeWrite); $font_obj->encode(array("OS/2")); $font_obj->close(); $font_obj = Font::load($tmp_name); $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); } $this->numObj++; $fontDescriptorId = $this->numObj; $this->numObj++; $pfbid = $this->numObj; $flags = 0; if ($font['ItalicAngle'] != 0) { $flags += pow(2, 6); } if ($font['IsFixedPitch'] === 'true') { $flags += 1; } $flags += pow(2, 5); $list = array('Ascent' => 'Ascender', 'CapHeight' => 'CapHeight', '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); $this->o_contents($this->numObj, 'new'); $this->objects[$pfbid]['c'] .= $data; 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)); } } $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'); } if (isset($options['differences'])) { $font['differences'] = $options['differences']; } } } if ($set && isset($this->fonts[$fontName])) { $this->currentBaseFont = $fontName; $this->currentFont = $this->currentBaseFont; $this->currentFontNum = $this->fonts[$this->currentFont]['fontNum']; } return $this->currentFontNum; }