PageRenderTime 49ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 1ms

/dompdf/include/font_metrics.cls.php

http://dompdf.googlecode.com/
PHP | 353 lines | 188 code | 52 blank | 113 comment | 22 complexity | fc7517a97e04d53cd784de9d6cbc6de0 MD5 | raw file
Possible License(s): LGPL-2.1, GPL-2.0
  1. <?php
  2. /**
  3. * @package dompdf
  4. * @link http://www.dompdf.com/
  5. * @author Benj Carson <benjcarson@digitaljunkies.ca>
  6. * @author Helmut Tischer <htischer@weihenstephan.org>
  7. * @author Fabien M?Šnager <fabien.menager@gmail.com>
  8. * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
  9. * @version $Id: font_metrics.cls.php 504 2012-11-04 16:10:14Z fabien.menager $
  10. */
  11. require_once DOMPDF_LIB_DIR . "/class.pdf.php";
  12. require_once DOMPDF_LIB_DIR . "/php-font-lib/classes/font.cls.php";
  13. /**
  14. * Name of the font cache file
  15. *
  16. * This file must be writable by the webserver process only to update it
  17. * with save_font_families() after adding the .afm file references of a new font family
  18. * with Font_Metrics::save_font_families().
  19. * This is typically done only from command line with load_font.php on converting
  20. * ttf fonts to ufm with php-font-lib.
  21. *
  22. * Declared here because PHP5 prevents constants from being declared with expressions
  23. */
  24. if (!defined("__DOMPDF_FONT_CACHE_FILE")) {
  25. if (file_exists(DOMPDF_FONT_DIR . "dompdf_font_family_cache")) {
  26. define('__DOMPDF_FONT_CACHE_FILE', DOMPDF_FONT_DIR . "dompdf_font_family_cache");
  27. }
  28. else {
  29. define('__DOMPDF_FONT_CACHE_FILE', DOMPDF_FONT_DIR . "dompdf_font_family_cache.dist.php");
  30. }
  31. }
  32. /**
  33. * The font metrics class
  34. *
  35. * This class provides information about fonts and text. It can resolve
  36. * font names into actual installed font files, as well as determine the
  37. * size of text in a particular font and size.
  38. *
  39. * @static
  40. * @package dompdf
  41. */
  42. class Font_Metrics {
  43. /**
  44. * @see __DOMPDF_FONT_CACHE_FILE
  45. */
  46. const CACHE_FILE = __DOMPDF_FONT_CACHE_FILE;
  47. /**
  48. * Underlying {@link Canvas} object to perform text size calculations
  49. *
  50. * @var Canvas
  51. */
  52. static protected $_pdf = null;
  53. /**
  54. * Array of font family names to font files
  55. *
  56. * Usually cached by the {@link load_font.php} script
  57. *
  58. * @var array
  59. */
  60. static protected $_font_lookup = array();
  61. /**
  62. * Class initialization
  63. *
  64. */
  65. static function init(Canvas $canvas = null) {
  66. if (!self::$_pdf) {
  67. if (!$canvas) {
  68. $canvas = Canvas_Factory::get_instance();
  69. }
  70. self::$_pdf = $canvas;
  71. }
  72. }
  73. /**
  74. * Calculates text size, in points
  75. *
  76. * @param string $text the text to be sized
  77. * @param string $font the desired font
  78. * @param float $size the desired font size
  79. * @param float $word_spacing
  80. * @param float $char_spacing
  81. *
  82. * @internal param float $spacing word spacing, if any
  83. * @return float
  84. */
  85. static function get_text_width($text, $font, $size, $word_spacing = 0.0, $char_spacing = 0.0) {
  86. //return self::$_pdf->get_text_width($text, $font, $size, $word_spacing, $char_spacing);
  87. // @todo Make sure this cache is efficient before enabling it
  88. static $cache = array();
  89. if ( $text === "" ) {
  90. return 0;
  91. }
  92. // Don't cache long strings
  93. $use_cache = !isset($text[50]); // Faster than strlen
  94. $key = "$font/$size/$word_spacing/$char_spacing";
  95. if ( $use_cache && isset($cache[$key][$text]) ) {
  96. return $cache[$key]["$text"];
  97. }
  98. $width = self::$_pdf->get_text_width($text, $font, $size, $word_spacing, $char_spacing);
  99. if ( $use_cache ) {
  100. $cache[$key][$text] = $width;
  101. }
  102. return $width;
  103. }
  104. /**
  105. * Calculates font height
  106. *
  107. * @param string $font
  108. * @param float $size
  109. * @return float
  110. */
  111. static function get_font_height($font, $size) {
  112. return self::$_pdf->get_font_height($font, $size);
  113. }
  114. /**
  115. * Resolves a font family & subtype into an actual font file
  116. * Subtype can be one of 'normal', 'bold', 'italic' or 'bold_italic'. If
  117. * the particular font family has no suitable font file, the default font
  118. * ({@link DOMPDF_DEFAULT_FONT}) is used. The font file returned
  119. * is the absolute pathname to the font file on the system.
  120. *
  121. * @param string $family_raw
  122. * @param string $subtype_raw
  123. *
  124. * @return string
  125. */
  126. static function get_font($family_raw, $subtype_raw = "normal") {
  127. static $cache = array();
  128. if ( isset($cache[$family_raw][$subtype_raw]) ) {
  129. return $cache[$family_raw][$subtype_raw];
  130. }
  131. /* Allow calling for various fonts in search path. Therefore not immediately
  132. * return replacement on non match.
  133. * Only when called with NULL try replacement.
  134. * When this is also missing there is really trouble.
  135. * If only the subtype fails, nevertheless return failure.
  136. * Only on checking the fallback font, check various subtypes on same font.
  137. */
  138. $subtype = strtolower($subtype_raw);
  139. if ( $family_raw ) {
  140. $family = str_replace( array("'", '"'), "", strtolower($family_raw));
  141. if ( isset(self::$_font_lookup[$family][$subtype]) ) {
  142. return $cache[$family_raw][$subtype_raw] = self::$_font_lookup[$family][$subtype];
  143. }
  144. return null;
  145. }
  146. $family = "serif";
  147. if ( isset(self::$_font_lookup[$family][$subtype]) ) {
  148. return $cache[$family_raw][$subtype_raw] = self::$_font_lookup[$family][$subtype];
  149. }
  150. if ( !isset(self::$_font_lookup[$family]) ) {
  151. return null;
  152. }
  153. $family = self::$_font_lookup[$family];
  154. foreach ( $family as $sub => $font ) {
  155. if (strpos($subtype, $sub) !== false) {
  156. return $cache[$family_raw][$subtype_raw] = $font;
  157. }
  158. }
  159. if ($subtype !== "normal") {
  160. foreach ( $family as $sub => $font ) {
  161. if ($sub !== "normal") {
  162. return $cache[$family_raw][$subtype_raw] = $font;
  163. }
  164. }
  165. }
  166. $subtype = "normal";
  167. if ( isset($family[$subtype]) ) {
  168. return $cache[$family_raw][$subtype_raw] = $family[$subtype];
  169. }
  170. return null;
  171. }
  172. static function get_family($family) {
  173. $family = str_replace( array("'", '"'), "", mb_strtolower($family));
  174. if ( isset(self::$_font_lookup[$family]) ) {
  175. return self::$_font_lookup[$family];
  176. }
  177. return null;
  178. }
  179. /**
  180. * Saves the stored font family cache
  181. *
  182. * The name and location of the cache file are determined by {@link
  183. * Font_Metrics::CACHE_FILE}. This file should be writable by the
  184. * webserver process.
  185. *
  186. * @see Font_Metrics::load_font_families()
  187. */
  188. static function save_font_families() {
  189. // replace the path to the DOMPDF font directory with "DOMPDF_FONT_DIR" (allows for more portability)
  190. $cache_data = var_export(self::$_font_lookup, true);
  191. $cache_data = str_replace('\''.DOMPDF_FONT_DIR , 'DOMPDF_FONT_DIR . \'' , $cache_data);
  192. $cache_data = "<"."?php return $cache_data ?".">";
  193. file_put_contents(self::CACHE_FILE, $cache_data);
  194. }
  195. /**
  196. * Loads the stored font family cache
  197. *
  198. * @see save_font_families()
  199. */
  200. static function load_font_families() {
  201. if ( !is_readable(self::CACHE_FILE) ) {
  202. return;
  203. }
  204. self::$_font_lookup = require_once self::CACHE_FILE;
  205. // If the font family cache is still in the old format
  206. if ( self::$_font_lookup === 1 ) {
  207. $cache_data = file_get_contents(self::CACHE_FILE);
  208. file_put_contents(self::CACHE_FILE, "<"."?php return $cache_data ?".">");
  209. self::$_font_lookup = require_once self::CACHE_FILE;
  210. }
  211. }
  212. static function get_type($type) {
  213. if (preg_match("/bold/i", $type)) {
  214. if (preg_match("/italic|oblique/i", $type)) {
  215. $type = "bold_italic";
  216. }
  217. else {
  218. $type = "bold";
  219. }
  220. }
  221. elseif (preg_match("/italic|oblique/i", $type)) {
  222. $type = "italic";
  223. }
  224. else {
  225. $type = "normal";
  226. }
  227. return $type;
  228. }
  229. static function install_fonts($files) {
  230. $names = array();
  231. foreach($files as $file) {
  232. $font = Font::load($file);
  233. $records = $font->getData("name", "records");
  234. $type = self::get_type($records[2]);
  235. $names[mb_strtolower($records[1])][$type] = $file;
  236. }
  237. return $names;
  238. }
  239. static function get_system_fonts() {
  240. $files = glob("/usr/share/fonts/truetype/*.ttf") +
  241. glob("/usr/share/fonts/truetype/*/*.ttf") +
  242. glob("/usr/share/fonts/truetype/*/*/*.ttf") +
  243. glob("C:\\Windows\\fonts\\*.ttf") +
  244. glob("C:\\WinNT\\fonts\\*.ttf") +
  245. glob("/mnt/c_drive/WINDOWS/Fonts/");
  246. return self::install_fonts($files);
  247. }
  248. /**
  249. * Returns the current font lookup table
  250. *
  251. * @return array
  252. */
  253. static function get_font_families() {
  254. return self::$_font_lookup;
  255. }
  256. static function set_font_family($fontname, $entry) {
  257. self::$_font_lookup[mb_strtolower($fontname)] = $entry;
  258. }
  259. static function register_font($style, $remote_file) {
  260. $fontname = mb_strtolower($style["family"]);
  261. $families = Font_Metrics::get_font_families();
  262. $entry = array();
  263. if ( isset($families[$fontname]) ) {
  264. $entry = $families[$fontname];
  265. }
  266. $local_file = DOMPDF_FONT_DIR . md5($remote_file);
  267. $cache_entry = $local_file;
  268. $local_file .= ".ttf";
  269. $style_string = Font_Metrics::get_type("{$style['weight']} {$style['style']}");
  270. if ( !isset($entry[$style_string]) ) {
  271. $entry[$style_string] = $cache_entry;
  272. Font_Metrics::set_font_family($fontname, $entry);
  273. // Download the remote file
  274. if ( !is_file($local_file) ) {
  275. file_put_contents($local_file, file_get_contents($remote_file));
  276. }
  277. $font = Font::load($local_file);
  278. if (!$font) {
  279. return false;
  280. }
  281. $font->parse();
  282. $font->saveAdobeFontMetrics("$cache_entry.ufm");
  283. // Save the changes
  284. Font_Metrics::save_font_families();
  285. }
  286. return true;
  287. }
  288. }
  289. Font_Metrics::load_font_families();