CFpdf.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  1. <?php
  2. namespace Fpdi;
  3. /**
  4. * 中文支持及文字图片旋转支持
  5. */
  6. class CFpdf extends \Fpdi\Fpdf
  7. {
  8. protected $angle = 0; //旋转角度
  9. protected $Big5_widths = array(
  10. ' ' => 250, '!' => 250, '"' => 408, '#' => 668, '$' => 490, '%' => 875, '&' => 698, '\'' => 250,
  11. '(' => 240, ')' => 240, '*' => 417, '+' => 667, ',' => 250, '-' => 313, '.' => 250, '/' => 520, '0' => 500, '1' => 500,
  12. '2' => 500, '3' => 500, '4' => 500, '5' => 500, '6' => 500, '7' => 500, '8' => 500, '9' => 500, ':' => 250, ';' => 250,
  13. '<' => 667, '=' => 667, '>' => 667, '?' => 396, '@' => 921, 'A' => 677, 'B' => 615, 'C' => 719, 'D' => 760, 'E' => 625,
  14. 'F' => 552, 'G' => 771, 'H' => 802, 'I' => 354, 'J' => 354, 'K' => 781, 'L' => 604, 'M' => 927, 'N' => 750, 'O' => 823,
  15. 'P' => 563, 'Q' => 823, 'R' => 729, 'S' => 542, 'T' => 698, 'U' => 771, 'V' => 729, 'W' => 948, 'X' => 771, 'Y' => 677,
  16. 'Z' => 635, '[' => 344, '\\' => 520, ']' => 344, '^' => 469, '_' => 500, '`' => 250, 'a' => 469, 'b' => 521, 'c' => 427,
  17. 'd' => 521, 'e' => 438, 'f' => 271, 'g' => 469, 'h' => 531, 'i' => 250, 'j' => 250, 'k' => 458, 'l' => 240, 'm' => 802,
  18. 'n' => 531, 'o' => 500, 'p' => 521, 'q' => 521, 'r' => 365, 's' => 333, 't' => 292, 'u' => 521, 'v' => 458, 'w' => 677,
  19. 'x' => 479, 'y' => 458, 'z' => 427, '{' => 480, '|' => 496, '}' => 480, '~' => 667
  20. );
  21. protected $GB_widths = array(
  22. ' ' => 207, '!' => 270, '"' => 342, '#' => 467, '$' => 462, '%' => 797, '&' => 710, '\'' => 239,
  23. '(' => 374, ')' => 374, '*' => 423, '+' => 605, ',' => 238, '-' => 375, '.' => 238, '/' => 334, '0' => 462, '1' => 462,
  24. '2' => 462, '3' => 462, '4' => 462, '5' => 462, '6' => 462, '7' => 462, '8' => 462, '9' => 462, ':' => 238, ';' => 238,
  25. '<' => 605, '=' => 605, '>' => 605, '?' => 344, '@' => 748, 'A' => 684, 'B' => 560, 'C' => 695, 'D' => 739, 'E' => 563,
  26. 'F' => 511, 'G' => 729, 'H' => 793, 'I' => 318, 'J' => 312, 'K' => 666, 'L' => 526, 'M' => 896, 'N' => 758, 'O' => 772,
  27. 'P' => 544, 'Q' => 772, 'R' => 628, 'S' => 465, 'T' => 607, 'U' => 753, 'V' => 711, 'W' => 972, 'X' => 647, 'Y' => 620,
  28. 'Z' => 607, '[' => 374, '\\' => 333, ']' => 374, '^' => 606, '_' => 500, '`' => 239, 'a' => 417, 'b' => 503, 'c' => 427,
  29. 'd' => 529, 'e' => 415, 'f' => 264, 'g' => 444, 'h' => 518, 'i' => 241, 'j' => 230, 'k' => 495, 'l' => 228, 'm' => 793,
  30. 'n' => 527, 'o' => 524, 'p' => 524, 'q' => 504, 'r' => 338, 's' => 336, 't' => 277, 'u' => 517, 'v' => 450, 'w' => 652,
  31. 'x' => 466, 'y' => 452, 'z' => 407, '{' => 370, '|' => 258, '}' => 370, '~' => 605
  32. );
  33. public function AddCIDFont($family, $style, $name, $cw, $CMap, $registry)
  34. {
  35. $fontkey = strtolower($family) . strtoupper($style);
  36. if (isset($this->fonts[$fontkey]))
  37. $this->Error("Font already added: $family $style");
  38. $i = count($this->fonts) + 1;
  39. $name = str_replace(' ', '', $name);
  40. $this->fonts[$fontkey] = array('i' => $i, 'type' => 'Type0', 'name' => $name, 'up' => -130, 'ut' => 40, 'cw' => $cw, 'CMap' => $CMap, 'registry' => $registry);
  41. }
  42. public function AddCIDFonts($family, $name, $cw, $CMap, $registry)
  43. {
  44. $this->AddCIDFont($family, '', $name, $cw, $CMap, $registry);
  45. $this->AddCIDFont($family, 'B', $name . ',Bold', $cw, $CMap, $registry);
  46. $this->AddCIDFont($family, 'I', $name . ',Italic', $cw, $CMap, $registry);
  47. $this->AddCIDFont($family, 'BI', $name . ',BoldItalic', $cw, $CMap, $registry);
  48. }
  49. public function AddBig5Font($family = 'Big5', $name = 'MSungStd-Light-Acro')
  50. {
  51. // Add Big5 font with proportional Latin
  52. $cw = $this->Big5_widths;
  53. $CMap = 'ETenms-B5-H';
  54. $registry = array('ordering' => 'CNS1', 'supplement' => 0);
  55. $this->AddCIDFonts($family, $name, $cw, $CMap, $registry);
  56. }
  57. public function AddBig5hwFont($family = 'Big5-hw', $name = 'MSungStd-Light-Acro')
  58. {
  59. // Add Big5 font with half-witdh Latin
  60. for ($i = 32; $i <= 126; $i++)
  61. $cw[chr($i)] = 500;
  62. $CMap = 'ETen-B5-H';
  63. $registry = array('ordering' => 'CNS1', 'supplement' => 0);
  64. $this->AddCIDFonts($family, $name, $cw, $CMap, $registry);
  65. }
  66. public function AddGBFont($family = 'GB', $name = 'STSongStd-Light-Acro')
  67. {
  68. // Add GB font with proportional Latin
  69. $cw = $this->GB_widths;
  70. $CMap = 'GBKp-EUC-H';
  71. $registry = array('ordering' => 'GB1', 'supplement' => 2);
  72. $this->AddCIDFonts($family, $name, $cw, $CMap, $registry);
  73. }
  74. public function AddGBhwFont($family = 'GB-hw', $name = 'STSongStd-Light-Acro')
  75. {
  76. // Add GB font with half-width Latin
  77. for ($i = 32; $i <= 126; $i++)
  78. $cw[chr($i)] = 500;
  79. $CMap = 'GBK-EUC-H';
  80. $registry = array('ordering' => 'GB1', 'supplement' => 2);
  81. $this->AddCIDFonts($family, $name, $cw, $CMap, $registry);
  82. }
  83. public function GetStringWidth($s)
  84. {
  85. if ($this->CurrentFont['type'] == 'Type0')
  86. return $this->GetMBStringWidth($s);
  87. else
  88. return parent::GetStringWidth($s);
  89. }
  90. public function GetMBStringWidth($s)
  91. {
  92. // Multi-byte version of GetStringWidth()
  93. $l = 0;
  94. $cw = &$this->CurrentFont['cw'];
  95. $nb = strlen($s);
  96. $i = 0;
  97. while ($i < $nb) {
  98. $c = $s[$i];
  99. if (ord($c) < 128) {
  100. $l += $cw[$c];
  101. $i++;
  102. } else {
  103. $l += 1000;
  104. $i += 2;
  105. }
  106. }
  107. return $l * $this->FontSize / 1000;
  108. }
  109. public function MultiCell($w, $h, $txt, $border = 0, $align = 'L', $fill = 0)
  110. {
  111. if ($this->CurrentFont['type'] == 'Type0')
  112. $this->MBMultiCell($w, $h, $txt, $border, $align, $fill);
  113. else
  114. parent::MultiCell($w, $h, $txt, $border, $align, $fill);
  115. }
  116. private function MBMultiCell($w, $h, $txt, $border = 0, $align = 'L', $fill = 0)
  117. {
  118. // Multi-byte version of MultiCell()
  119. $cw = &$this->CurrentFont['cw'];
  120. if ($w == 0)
  121. $w = $this->w - $this->rMargin - $this->x;
  122. $wmax = ($w - 2 * $this->cMargin) * 1000 / $this->FontSize;
  123. $s = str_replace("\r", '', $txt);
  124. $nb = strlen($s);
  125. if ($nb > 0 && $s[$nb - 1] == "\n")
  126. $nb--;
  127. $b = 0;
  128. if ($border) {
  129. if ($border == 1) {
  130. $border = 'LTRB';
  131. $b = 'LRT';
  132. $b2 = 'LR';
  133. } else {
  134. $b2 = '';
  135. if (is_int(strpos($border, 'L')))
  136. $b2 .= 'L';
  137. if (is_int(strpos($border, 'R')))
  138. $b2 .= 'R';
  139. $b = is_int(strpos($border, 'T')) ? $b2 . 'T' : $b2;
  140. }
  141. }
  142. $sep = -1;
  143. $i = 0;
  144. $j = 0;
  145. $l = 0;
  146. $nl = 1;
  147. while ($i < $nb) {
  148. // Get next character
  149. $c = $s[$i];
  150. // Check if ASCII or MB
  151. $ascii = (ord($c) < 128);
  152. if ($c == "\n") {
  153. // Explicit line break
  154. $this->Cell($w, $h, substr($s, $j, $i - $j), $b, 2, $align, $fill);
  155. $i++;
  156. $sep = -1;
  157. $j = $i;
  158. $l = 0;
  159. $nl++;
  160. if ($border && $nl == 2)
  161. $b = $b2;
  162. continue;
  163. }
  164. if (!$ascii) {
  165. $sep = $i;
  166. $ls = $l;
  167. } elseif ($c == ' ') {
  168. $sep = $i;
  169. $ls = $l;
  170. }
  171. $l += $ascii ? $cw[$c] : 1000;
  172. if ($l > $wmax) {
  173. // Automatic line break
  174. if ($sep == -1 || $i == $j) {
  175. if ($i == $j)
  176. $i += $ascii ? 1 : 2;
  177. $this->Cell($w, $h, substr($s, $j, $i - $j), $b, 2, $align, $fill);
  178. } else {
  179. $this->Cell($w, $h, substr($s, $j, $sep - $j), $b, 2, $align, $fill);
  180. $i = ($s[$sep] == ' ') ? $sep + 1 : $sep;
  181. }
  182. $sep = -1;
  183. $j = $i;
  184. $l = 0;
  185. $nl++;
  186. if ($border && $nl == 2)
  187. $b = $b2;
  188. } else
  189. $i += $ascii ? 1 : 2;
  190. }
  191. // Last chunk
  192. if ($border && is_int(strpos($border, 'B')))
  193. $b .= 'B';
  194. $this->Cell($w, $h, substr($s, $j, $i - $j), $b, 2, $align, $fill);
  195. $this->x = $this->lMargin;
  196. }
  197. public function Write($h, $txt, $link = '')
  198. {
  199. if ($this->CurrentFont['type'] == 'Type0')
  200. $this->MBWrite($h, $txt, $link);
  201. else
  202. parent::Write($h, $txt, $link);
  203. }
  204. private function MBWrite($h, $txt, $link)
  205. {
  206. // Multi-byte version of Write()
  207. $cw = &$this->CurrentFont['cw'];
  208. $w = $this->w - $this->rMargin - $this->x;
  209. $wmax = ($w - 2 * $this->cMargin) * 1000 / $this->FontSize;
  210. $s = str_replace("\r", '', $txt);
  211. $nb = strlen($s);
  212. $sep = -1;
  213. $i = 0;
  214. $j = 0;
  215. $l = 0;
  216. $nl = 1;
  217. while ($i < $nb) {
  218. // Get next character
  219. $c = $s[$i];
  220. // Check if ASCII or MB
  221. $ascii = (ord($c) < 128);
  222. if ($c == "\n") {
  223. // Explicit line break
  224. $this->Cell($w, $h, substr($s, $j, $i - $j), 0, 2, '', 0, $link);
  225. $i++;
  226. $sep = -1;
  227. $j = $i;
  228. $l = 0;
  229. if ($nl == 1) {
  230. $this->x = $this->lMargin;
  231. $w = $this->w - $this->rMargin - $this->x;
  232. $wmax = ($w - 2 * $this->cMargin) * 1000 / $this->FontSize;
  233. }
  234. $nl++;
  235. continue;
  236. }
  237. if (!$ascii || $c == ' ')
  238. $sep = $i;
  239. $l += $ascii ? $cw[$c] : 1000;
  240. if ($l > $wmax) {
  241. // Automatic line break
  242. if ($sep == -1 || $i == $j) {
  243. if ($this->x > $this->lMargin) {
  244. // Move to next line
  245. $this->x = $this->lMargin;
  246. $this->y += $h;
  247. $w = $this->w - $this->rMargin - $this->x;
  248. $wmax = ($w - 2 * $this->cMargin) * 1000 / $this->FontSize;
  249. $i++;
  250. $nl++;
  251. continue;
  252. }
  253. if ($i == $j)
  254. $i += $ascii ? 1 : 2;
  255. $this->Cell($w, $h, substr($s, $j, $i - $j), 0, 2, '', 0, $link);
  256. } else {
  257. $this->Cell($w, $h, substr($s, $j, $sep - $j), 0, 2, '', 0, $link);
  258. $i = ($s[$sep] == ' ') ? $sep + 1 : $sep;
  259. }
  260. $sep = -1;
  261. $j = $i;
  262. $l = 0;
  263. if ($nl == 1) {
  264. $this->x = $this->lMargin;
  265. $w = $this->w - $this->rMargin - $this->x;
  266. $wmax = ($w - 2 * $this->cMargin) * 1000 / $this->FontSize;
  267. }
  268. $nl++;
  269. } else
  270. $i += $ascii ? 1 : 2;
  271. }
  272. // Last chunk
  273. if ($i != $j)
  274. $this->Cell($l / 1000 * $this->FontSize, $h, substr($s, $j, $i - $j), 0, 0, '', 0, $link);
  275. }
  276. public function _putType0($font)
  277. {
  278. // Type0
  279. $this->_newobj();
  280. $this->_out('<</Type /Font');
  281. $this->_out('/Subtype /Type0');
  282. $this->_out('/BaseFont /' . $font['name'] . '-' . $font['CMap']);
  283. $this->_out('/Encoding /' . $font['CMap']);
  284. $this->_out('/DescendantFonts [' . ($this->n + 1) . ' 0 R]');
  285. $this->_out('>>');
  286. $this->_out('endobj');
  287. // CIDFont
  288. $this->_newobj();
  289. $this->_out('<</Type /Font');
  290. $this->_out('/Subtype /CIDFontType0');
  291. $this->_out('/BaseFont /' . $font['name']);
  292. $this->_out('/CIDSystemInfo <</Registry ' . $this->_textstring('Adobe') . ' /Ordering ' . $this->_textstring($font['registry']['ordering']) . ' /Supplement ' . $font['registry']['supplement'] . '>>');
  293. $this->_out('/FontDescriptor ' . ($this->n + 1) . ' 0 R');
  294. if ($font['CMap'] == 'ETen-B5-H')
  295. $W = '13648 13742 500';
  296. elseif ($font['CMap'] == 'GBK-EUC-H')
  297. $W = '814 907 500 7716 [500]';
  298. else
  299. $W = '1 [' . implode(' ', $font['cw']) . ']';
  300. $this->_out('/W [' . $W . ']>>');
  301. $this->_out('endobj');
  302. // Font descriptor
  303. $this->_newobj();
  304. $this->_out('<</Type /FontDescriptor');
  305. $this->_out('/FontName /' . $font['name']);
  306. $this->_out('/Flags 6');
  307. $this->_out('/FontBBox [0 -200 1000 900]');
  308. $this->_out('/ItalicAngle 0');
  309. $this->_out('/Ascent 800');
  310. $this->_out('/Descent -200');
  311. $this->_out('/CapHeight 800');
  312. $this->_out('/StemV 50');
  313. $this->_out('>>');
  314. $this->_out('endobj');
  315. }
  316. /**
  317. * 旋转文字,角度为弧度
  318. *
  319. * @param int $angle 角度
  320. * @return void
  321. */
  322. private function Rotate($angle, $x = -1, $y = -1)
  323. {
  324. if ($x == -1)
  325. $x = $this->x;
  326. if ($y == -1)
  327. $y = $this->y;
  328. if ($this->angle != 0)
  329. $this->_out('Q');
  330. $this->angle = $angle;
  331. if ($angle != 0) {
  332. $angle *= M_PI / 180;
  333. $c = cos($angle);
  334. $s = sin($angle);
  335. $cx = $x * $this->k;
  336. $cy = ($this->h - $y) * $this->k;
  337. $this->_out(sprintf('q %.5F %.5F %.5F %.5F %.2F %.2F cm 1 0 0 1 %.2F %.2F cm', $c, $s, -$s, $c, $cx, $cy, -$cx, -$cy));
  338. }
  339. }
  340. public function _endpage()
  341. {
  342. if ($this->angle != 0) {
  343. $this->angle = 0;
  344. $this->_out('Q');
  345. }
  346. parent::_endpage();
  347. }
  348. public function RotatedText($x, $y, $txt, $angle)
  349. {
  350. //Text rotated around its origin
  351. $this->Rotate($angle, $x, $y);
  352. $this->Text($x, $y, $txt);
  353. $this->Rotate(0);
  354. }
  355. public function RotatedImage($file, $x, $y, $w, $h, $angle)
  356. {
  357. //Image rotated around its upper-left corner
  358. $this->Rotate($angle, $x, $y);
  359. $this->Image($file, $x, $y, $w, $h);
  360. $this->Rotate(0);
  361. }
  362. }