You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

chinese.rb 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473
  1. # Copyright (c) 2006 4ssoM LLC <www.4ssoM.com>
  2. # 1.12 contributed by Ed Moss.
  3. #
  4. # The MIT License
  5. #
  6. # Permission is hereby granted, free of charge, to any person obtaining a copy
  7. # of this software and associated documentation files (the "Software"), to deal
  8. # in the Software without restriction, including without limitation the rights
  9. # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. # copies of the Software, and to permit persons to whom the Software is
  11. # furnished to do so, subject to the following conditions:
  12. #
  13. # The above copyright notice and this permission notice shall be included in
  14. # all copies or substantial portions of the Software.
  15. #
  16. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17. # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18. # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19. # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20. # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21. # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22. # THE SOFTWARE.
  23. #
  24. # This is direct port of chinese.php
  25. #
  26. # Chinese PDF support.
  27. #
  28. # Usage is as follows:
  29. #
  30. # require 'fpdf'
  31. # require 'chinese'
  32. # pdf = FPDF.new
  33. # pdf.extend(PDF_Chinese)
  34. #
  35. # This allows it to be combined with other extensions, such as the bookmark
  36. # module.
  37. module PDF_Chinese
  38. Big5_widths={' '=>250,'!'=>250,'"'=>408,'#'=>668,''=>490,'%'=>875,'&'=>698,'\''=>250,
  39. '('=>240,')'=>240,'*'=>417,'+'=>667,','=>250,'-'=>313,'.'=>250,'/'=>520,'0'=>500,'1'=>500,
  40. '2'=>500,'3'=>500,'4'=>500,'5'=>500,'6'=>500,'7'=>500,'8'=>500,'9'=>500,':'=>250,''=>250,
  41. '<'=>667,'='=>667,'>'=>667,'?'=>396,'@'=>921,'A'=>677,'B'=>615,'C'=>719,'D'=>760,'E'=>625,
  42. 'F'=>552,'G'=>771,'H'=>802,'I'=>354,'J'=>354,'K'=>781,'L'=>604,'M'=>927,'N'=>750,'O'=>823,
  43. 'P'=>563,'Q'=>823,'R'=>729,'S'=>542,'T'=>698,'U'=>771,'V'=>729,'W'=>948,'X'=>771,'Y'=>677,
  44. 'Z'=>635,'['=>344,'\\'=>520,']'=>344,'^'=>469,'_'=>500,'`'=>250,'a'=>469,'b'=>521,'c'=>427,
  45. 'd'=>521,'e'=>438,'f'=>271,'g'=>469,'h'=>531,'i'=>250,'j'=>250,'k'=>458,'l'=>240,'m'=>802,
  46. 'n'=>531,'o'=>500,'p'=>521,'q'=>521,'r'=>365,'s'=>333,'t'=>292,'u'=>521,'v'=>458,'w'=>677,
  47. 'x'=>479,'y'=>458,'z'=>427,'{'=>480,'|'=>496,'end'=>480,'~'=>667}
  48. GB_widths={' '=>207,'!'=>270,'"'=>342,'#'=>467,''=>462,'%'=>797,'&'=>710,'\''=>239,
  49. '('=>374,')'=>374,'*'=>423,'+'=>605,','=>238,'-'=>375,'.'=>238,'/'=>334,'0'=>462,'1'=>462,
  50. '2'=>462,'3'=>462,'4'=>462,'5'=>462,'6'=>462,'7'=>462,'8'=>462,'9'=>462,':'=>238,''=>238,
  51. '<'=>605,'='=>605,'>'=>605,'?'=>344,'@'=>748,'A'=>684,'B'=>560,'C'=>695,'D'=>739,'E'=>563,
  52. 'F'=>511,'G'=>729,'H'=>793,'I'=>318,'J'=>312,'K'=>666,'L'=>526,'M'=>896,'N'=>758,'O'=>772,
  53. 'P'=>544,'Q'=>772,'R'=>628,'S'=>465,'T'=>607,'U'=>753,'V'=>711,'W'=>972,'X'=>647,'Y'=>620,
  54. 'Z'=>607,'['=>374,'\\'=>333,']'=>374,'^'=>606,'_'=>500,'`'=>239,'a'=>417,'b'=>503,'c'=>427,
  55. 'd'=>529,'e'=>415,'f'=>264,'g'=>444,'h'=>518,'i'=>241,'j'=>230,'k'=>495,'l'=>228,'m'=>793,
  56. 'n'=>527,'o'=>524,'p'=>524,'q'=>504,'r'=>338,'s'=>336,'t'=>277,'u'=>517,'v'=>450,'w'=>652,
  57. 'x'=>466,'y'=>452,'z'=>407,'{'=>370,'|'=>258,'end'=>370,'~'=>605}
  58. def AddCIDFont(family,style,name,cw,cMap,registry)
  59. #ActionController::Base::logger.debug registry.to_a.join(":").to_s
  60. fontkey=family.downcase+style.upcase
  61. unless @fonts[fontkey].nil?
  62. Error("Font already added: family style")
  63. end
  64. i=@fonts.length+1
  65. name=name.gsub(' ','')
  66. @fonts[fontkey]={'i'=>i,'type'=>'Type0','name'=>name,'up'=>-130,'ut'=>40,'cw'=>cw, 'CMap'=>cMap,'registry'=>registry}
  67. end
  68. def AddCIDFonts(family,name,cw,cMap,registry)
  69. AddCIDFont(family,'',name,cw,cMap,registry)
  70. AddCIDFont(family,'B',name+',Bold',cw,cMap,registry)
  71. AddCIDFont(family,'I',name+',Italic',cw,cMap,registry)
  72. AddCIDFont(family,'BI',name+',BoldItalic',cw,cMap,registry)
  73. end
  74. def AddBig5Font(family='Big5',name='MSungStd-Light-Acro')
  75. #Add Big5 font with proportional Latin
  76. cw=Big5_widths
  77. cMap='ETenms-B5-H'
  78. registry={'ordering'=>'CNS1','supplement'=>0}
  79. #ActionController::Base::logger.debug registry.to_a.join(":").to_s
  80. AddCIDFonts(family,name,cw,cMap,registry)
  81. end
  82. def AddBig5hwFont(family='Big5-hw',name='MSungStd-Light-Acro')
  83. #Add Big5 font with half-witdh Latin
  84. cw = {}
  85. 32.upto(126) do |i|
  86. cw[i.chr]=500
  87. end
  88. cMap='ETen-B5-H'
  89. registry={'ordering'=>'CNS1','supplement'=>0}
  90. AddCIDFonts(family,name,cw,cMap,registry)
  91. end
  92. def AddGBFont(family='GB',name='STSongStd-Light-Acro')
  93. #Add GB font with proportional Latin
  94. cw=GB_widths
  95. cMap='GBKp-EUC-H'
  96. registry={'ordering'=>'GB1','supplement'=>2}
  97. AddCIDFonts(family,name,cw,cMap,registry)
  98. end
  99. def AddGBhwFont(family='GB-hw',name='STSongStd-Light-Acro')
  100. #Add GB font with half-width Latin
  101. 32.upto(126) do |i|
  102. cw[i.chr]=500
  103. end
  104. cMap='GBK-EUC-H'
  105. registry={'ordering'=>'GB1','supplement'=>2}
  106. AddCIDFonts(family,name,cw,cMap,registry)
  107. end
  108. def GetStringWidth(s)
  109. if(@CurrentFont['type']=='Type0')
  110. return GetMBStringWidth(s)
  111. else
  112. return super(s)
  113. end
  114. end
  115. def GetMBStringWidth(s)
  116. #Multi-byte version of GetStringWidth()
  117. l=0
  118. cw=@CurrentFont['cw']
  119. nb=s.length
  120. i=0
  121. while(i<nb)
  122. c=s[i]
  123. if(c<128)
  124. l+=cw[c.chr] if cw[c.chr]
  125. i+=1
  126. else
  127. l+=1000
  128. i+=2
  129. end
  130. end
  131. return l*@FontSize/1000
  132. end
  133. def MultiCell(w,h,txt,border=0,align='L',fill=0)
  134. if(@CurrentFont['type']=='Type0')
  135. MBMultiCell(w,h,txt,border,align,fill)
  136. else
  137. super(w,h,txt,border,align,fill)
  138. end
  139. end
  140. def MBMultiCell(w,h,txt,border=0,align='L',fill=0)
  141. #Multi-byte version of MultiCell()
  142. cw=@CurrentFont['cw']
  143. if(w==0)
  144. w=@w-@rMargin-@x
  145. end
  146. wmax=(w-2*@cMargin)*1000/@FontSize
  147. s=txt.gsub("\r",'')
  148. nb=s.length
  149. if(nb>0 and s[nb-1]=="\n")
  150. nb-=1
  151. end
  152. b=0
  153. if(border)
  154. if(border==1)
  155. border='LTRB'
  156. b='LRT'
  157. b2='LR'
  158. else
  159. b2=''
  160. if(border.to_s.index('L'))
  161. b2+='L'
  162. end
  163. if(border.to_s.index('R'))
  164. b2+='R'
  165. end
  166. b=border.to_s.index('T') ? b2+'T' : b2
  167. end
  168. end
  169. sep=-1
  170. i=0
  171. j=0
  172. l=0
  173. nl=1
  174. while(i<nb)
  175. #Get next character
  176. c=s[i]
  177. #Check if ASCII or MB
  178. ascii=(c<128)
  179. if(c.chr=="\n")
  180. #Explicit line break
  181. Cell(w,h,s[j,i-j],b,2,align,fill)
  182. i+=1
  183. sep=-1
  184. j=i
  185. l=0
  186. nl+=1
  187. if(border and nl==2)
  188. b=b2
  189. end
  190. next
  191. end
  192. if(!ascii)
  193. sep=i
  194. ls=l
  195. elsif(c==' ')
  196. sep=i
  197. ls=l
  198. end
  199. l+=ascii ? (cw[c.chr] || 0) : 1100
  200. if(l>wmax)
  201. #Automatic line break
  202. if(sep==-1 or i==j)
  203. if(i==j)
  204. i+=ascii ? 1 : 3
  205. end
  206. Cell(w,h,s[j,i-j],b,2,align,fill)
  207. else
  208. Cell(w,h,s[j,sep-j],b,2,align,fill)
  209. i=(s[sep]==' ') ? sep+1 : sep
  210. end
  211. sep=-1
  212. j=i
  213. l=0
  214. # nl+=1
  215. if(border and nl==2)
  216. b=b2
  217. end
  218. else
  219. i+=ascii ? 1 : 3
  220. end
  221. end
  222. #Last chunk
  223. if(border and not border.to_s.index('B').nil?)
  224. b+='B'
  225. end
  226. Cell(w,h,s[j,i-j],b,2,align,fill)
  227. @x=@lMargin
  228. end
  229. def Write(h,txt,link='')
  230. if(@CurrentFont['type']=='Type0')
  231. MBWrite(h,txt,link)
  232. else
  233. super(h,txt,link)
  234. end
  235. end
  236. def MBWrite(h,txt,link)
  237. #Multi-byte version of Write()
  238. cw=@CurrentFont['cw']
  239. w=@w-@rMargin-@x
  240. wmax=(w-2*@cMargin)*1000/@FontSize
  241. s=txt.gsub("\r",'')
  242. nb=s.length
  243. sep=-1
  244. i=0
  245. j=0
  246. l=0
  247. nl=1
  248. while(i<nb)
  249. #Get next character
  250. c=s[i]
  251. #Check if ASCII or MB
  252. ascii=(c<128)
  253. if(c.chr=="\n")
  254. #Explicit line break
  255. Cell(w,h,s[j,i-j],0,2,'',0,link)
  256. i+=1
  257. sep=-1
  258. j=i
  259. l=0
  260. if(nl==1)
  261. @x=@lMargin
  262. w=@w-@rMargin-@x
  263. wmax=(w-2*@cMargin)*1000/@FontSize
  264. end
  265. nl+=1
  266. next
  267. end
  268. if(!ascii or c==' ')
  269. sep=i
  270. end
  271. l+=ascii ? cw[c.chr] : 1100
  272. if(l>wmax)
  273. #Automatic line break
  274. if(sep==-1 or i==j)
  275. if(@x>@lMargin)
  276. #Move to next line
  277. @x=@lMargin
  278. @y+=h
  279. w=@w-@rMargin-@x
  280. wmax=(w-2*@cMargin)*1000/@FontSize
  281. i+=1
  282. nl+=1
  283. next
  284. end
  285. if(i==j)
  286. i+=ascii ? 1 : 3
  287. end
  288. Cell(w,h,s[j,i-j],0,2,'',0,link)
  289. else
  290. Cell(w,h,s[j,sep-j],0,2,'',0,link)
  291. i=(s[sep]==' ') ? sep+1 : sep
  292. end
  293. sep=-1
  294. j=i
  295. l=0
  296. if(nl==1)
  297. @x=@lMargin
  298. w=@w-@rMargin-@x
  299. wmax=(w-2*@cMargin)*1000/@FontSize
  300. end
  301. nl+=1
  302. else
  303. i+=ascii ? 1 : 3
  304. end
  305. end
  306. #Last chunk
  307. if(i!=j)
  308. Cell(l/1000*@FontSize,h,s[j,i-j],0,0,'',0,link)
  309. end
  310. end
  311. private
  312. def putfonts()
  313. nf=@n
  314. @diffs.each do |diff|
  315. #Encodings
  316. newobj()
  317. out('<</Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences ['+diff+']>>')
  318. out('endobj')
  319. end
  320. # mqr=get_magic_quotes_runtime()
  321. # set_magic_quotes_runtime(0)
  322. @FontFiles.each_pair do |file, info|
  323. #Font file embedding
  324. newobj()
  325. @FontFiles[file]['n']=@n
  326. if(defined('FPDF_FONTPATH'))
  327. file=FPDF_FONTPATH+file
  328. end
  329. size=filesize(file)
  330. if(!size)
  331. Error('Font file not found')
  332. end
  333. out('<</Length '+size)
  334. if(file[-2]=='.z')
  335. out('/Filter /FlateDecode')
  336. end
  337. out('/Length1 '+info['length1'])
  338. unless info['length2'].nil?
  339. out('/Length2 '+info['length2']+' /Length3 0')
  340. end
  341. out('>>')
  342. f=fopen(file,'rb')
  343. putstream(fread(f,size))
  344. fclose(f)
  345. out('endobj')
  346. end
  347. #
  348. # set_magic_quotes_runtime(mqr)
  349. #
  350. @fonts.each_pair do |k, font|
  351. #Font objects
  352. newobj()
  353. @fonts[k]['n']=@n
  354. out('<</Type /Font')
  355. if(font['type']=='Type0')
  356. putType0(font)
  357. else
  358. name=font['name']
  359. out('/BaseFont /'+name)
  360. if(font['type']=='core')
  361. #Standard font
  362. out('/Subtype /Type1')
  363. if(name!='Symbol' and name!='ZapfDingbats')
  364. out('/Encoding /WinAnsiEncoding')
  365. end
  366. else
  367. #Additional font
  368. out('/Subtype /'+font['type'])
  369. out('/FirstChar 32')
  370. out('/LastChar 255')
  371. out('/Widths '+(@n+1)+' 0 R')
  372. out('/FontDescriptor '+(@n+2)+' 0 R')
  373. if(font['enc'])
  374. if !font['diff'].nil?
  375. out('/Encoding '+(nf+font['diff'])+' 0 R')
  376. else
  377. out('/Encoding /WinAnsiEncoding')
  378. end
  379. end
  380. end
  381. out('>>')
  382. out('endobj')
  383. if(font['type']!='core')
  384. #Widths
  385. newobj()
  386. cw=font['cw']
  387. s='['
  388. 32.upto(255) do |i|
  389. s+=cw[i.chr]+' '
  390. end
  391. out(s+']')
  392. out('endobj')
  393. #Descriptor
  394. newobj()
  395. s='<</Type /FontDescriptor /FontName /'+name
  396. font['desc'].each_pair do |k, v|
  397. s+=' /'+k+' '+v
  398. end
  399. file=font['file']
  400. if(file)
  401. s+=' /FontFile'+(font['type']=='Type1' ? '' : '2')+' '+@FontFiles[file]['n']+' 0 R'
  402. end
  403. out(s+'>>')
  404. out('endobj')
  405. end
  406. end
  407. end
  408. end
  409. def putType0(font)
  410. #Type0
  411. out('/Subtype /Type0')
  412. out('/BaseFont /'+font['name']+'-'+font['CMap'])
  413. out('/Encoding /'+font['CMap'])
  414. out('/DescendantFonts ['+(@n+1).to_s+' 0 R]')
  415. out('>>')
  416. out('endobj')
  417. #CIDFont
  418. newobj()
  419. out('<</Type /Font')
  420. out('/Subtype /CIDFontType0')
  421. out('/BaseFont /'+font['name'])
  422. out('/CIDSystemInfo <</Registry '+textstring('Adobe')+' /Ordering '+textstring(font['registry']['ordering'])+' /Supplement '+font['registry']['supplement'].to_s+'>>')
  423. out('/FontDescriptor '+(@n+1).to_s+' 0 R')
  424. if(font['CMap']=='ETen-B5-H')
  425. w='13648 13742 500'
  426. elsif(font['CMap']=='GBK-EUC-H')
  427. w='814 907 500 7716 [500]'
  428. else
  429. # ActionController::Base::logger.debug font['cw'].keys.sort.join(' ').to_s
  430. # ActionController::Base::logger.debug font['cw'].values.join(' ').to_s
  431. w='1 ['
  432. font['cw'].keys.sort.each {|key|
  433. w+=font['cw'][key].to_s + " "
  434. # ActionController::Base::logger.debug key.to_s
  435. # ActionController::Base::logger.debug font['cw'][key].to_s
  436. }
  437. w +=']'
  438. end
  439. out('/W ['+w+']>>')
  440. out('endobj')
  441. #Font descriptor
  442. newobj()
  443. out('<</Type /FontDescriptor')
  444. out('/FontName /'+font['name'])
  445. out('/Flags 6')
  446. out('/FontBBox [0 -200 1000 900]')
  447. out('/ItalicAngle 0')
  448. out('/Ascent 800')
  449. out('/Descent -200')
  450. out('/CapHeight 800')
  451. out('/StemV 50')
  452. out('>>')
  453. out('endobj')
  454. end
  455. end