PageRenderTime 45ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/plugins/rfpdf/lib/fpdf/chinese.rb

https://bitbucket.org/eimajenthat/redmine
Ruby | 486 lines | 448 code | 2 blank | 36 comment | 0 complexity | 5640f64ecafb3f5751e4d4e219678b48 MD5 | raw file
Possible License(s): GPL-2.0
  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,'}'=>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,'}'=>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(@current_font['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=@current_font['cw']
  119. nb=s.length
  120. i=0
  121. while(i<nb)
  122. c = s[i].is_a?(String) ? s[i].ord : 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*@font_size/1000
  132. end
  133. def MultiCell(w,h,txt,border=0,align='L',fill=0,ln=1)
  134. if(@current_font['type']=='Type0')
  135. MBMultiCell(w,h,txt,border,align,fill,ln)
  136. else
  137. super(w,h,txt,border,align,fill,ln)
  138. end
  139. end
  140. def MBMultiCell(w,h,txt,border=0,align='L',fill=0,ln=1)
  141. # save current position
  142. prevx = @x;
  143. prevy = @y;
  144. #Multi-byte version of MultiCell()
  145. cw=@current_font['cw']
  146. if(w==0)
  147. w=@w-@r_margin-@x
  148. end
  149. wmax=(w-2*@c_margin)*1000/@font_size
  150. s=txt.gsub("\r",'')
  151. nb=s.length
  152. if(nb>0 and s[nb-1]=="\n")
  153. nb-=1
  154. end
  155. b=0
  156. if(border)
  157. if(border==1)
  158. border='LTRB'
  159. b='LRT'
  160. b2='LR'
  161. else
  162. b2=''
  163. b2='L' unless border.to_s.index('L').nil?
  164. b2=b2+'R' unless border.to_s.index('R').nil?
  165. b=(border.to_s.index('T')) ? (b2+'T') : b2
  166. end
  167. end
  168. sep=-1
  169. i=0
  170. j=0
  171. l=0
  172. nl=1
  173. while(i<nb)
  174. #Get next character
  175. c = s[i].is_a?(String) ? s[i].ord : s[i]
  176. #Check if ASCII or MB
  177. ascii=(c<128)
  178. if(c.chr=="\n")
  179. #Explicit line break
  180. Cell(w,h,s[j,i-j],b,2,align,fill)
  181. i+=1
  182. sep=-1
  183. j=i
  184. l=0
  185. nl+=1
  186. if(border and nl==2)
  187. b=b2
  188. end
  189. next
  190. end
  191. if(!ascii)
  192. sep=i
  193. ls=l
  194. elsif(c.chr==' ')
  195. sep=i
  196. ls=l
  197. end
  198. l+=(ascii ? cw[c.chr] : 1000) || 0
  199. if(l>wmax)
  200. #Automatic line break
  201. if(sep==-1 or i==j)
  202. if(i==j)
  203. i+=ascii ? 1 : 2
  204. end
  205. Cell(w,h,s[j,i-j],b,2,align,fill)
  206. else
  207. Cell(w,h,s[j,sep-j],b,2,align,fill)
  208. i=(s[sep].chr==' ') ? sep+1 : sep
  209. end
  210. sep=-1
  211. j=i
  212. l=0
  213. nl+=1
  214. if(border and nl==2)
  215. b=b2
  216. end
  217. else
  218. i+=ascii ? 1 : 2
  219. end
  220. end
  221. #Last chunk
  222. if(border and not border.to_s.index('B').nil?)
  223. b+='B'
  224. end
  225. Cell(w,h,s[j,i-j],b,2,align,fill)
  226. # move cursor to specified position
  227. if (ln == 1)
  228. # go to the beginning of the next line
  229. @x=@l_margin
  230. elsif (ln == 0)
  231. # go to the top-right of the cell
  232. @y = prevy;
  233. @x = prevx + w;
  234. elsif (ln == 2)
  235. # go to the bottom-left of the cell
  236. @x = prevx;
  237. end
  238. end
  239. def Write(h,txt,link='',fill=0)
  240. if(@current_font['type']=='Type0')
  241. MBWrite(h,txt,link,fill)
  242. else
  243. super(h,txt,link,fill)
  244. end
  245. end
  246. def MBWrite(h,txt,link,fill=0)
  247. #Multi-byte version of Write()
  248. cw=@current_font['cw']
  249. w=@w-@r_margin-@x
  250. wmax=(w-2*@c_margin)*1000/@font_size
  251. s=txt.gsub("\r",'')
  252. nb=s.length
  253. sep=-1
  254. i=0
  255. j=0
  256. l=0
  257. nl=1
  258. while(i<nb)
  259. #Get next character
  260. c = s[i].is_a?(String) ? s[i].ord : s[i]
  261. #Check if ASCII or MB
  262. ascii=(c<128)
  263. if(c.chr=="\n")
  264. #Explicit line break
  265. Cell(w,h,s[j,i-j],0,2,'',fill,link)
  266. i+=1
  267. sep=-1
  268. j=i
  269. l=0
  270. if(nl==1)
  271. @x=@l_margin
  272. w=@w-@r_margin-@x
  273. wmax=(w-2*@c_margin)*1000/@font_size
  274. end
  275. nl+=1
  276. next
  277. end
  278. if(!ascii or c.chr==' ')
  279. sep=i
  280. end
  281. l+=(ascii ? cw[c.chr] : 1000) || 0
  282. if(l>wmax)
  283. #Automatic line break
  284. if(sep==-1 or i==j)
  285. if(@x>@l_margin)
  286. #Move to next line
  287. @x=@l_margin
  288. @y+=h
  289. w=@w-@r_margin-@x
  290. wmax=(w-2*@c_margin)*1000/@font_size
  291. i+=1
  292. nl+=1
  293. next
  294. end
  295. if(i==j)
  296. i+=ascii ? 1 : 2
  297. end
  298. Cell(w,h,s[j,i-j],0,2,'',fill,link)
  299. else
  300. Cell(w,h,s[j,sep-j],0,2,'',fill,link)
  301. i=(s[sep].chr==' ') ? sep+1 : sep
  302. end
  303. sep=-1
  304. j=i
  305. l=0
  306. if(nl==1)
  307. @x=@l_margin
  308. w=@w-@r_margin-@x
  309. wmax=(w-2*@c_margin)*1000/@font_size
  310. end
  311. nl+=1
  312. else
  313. i+=ascii ? 1 : 2
  314. end
  315. end
  316. #Last chunk
  317. if(i!=j)
  318. Cell(l*@font_size/1000.0,h,s[j,i-j],0,0,'',fill,link)
  319. end
  320. end
  321. private
  322. def putfonts()
  323. nf=@n
  324. @diffs.each do |diff|
  325. #Encodings
  326. newobj()
  327. out('<</Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences ['+diff+']>>')
  328. out('endobj')
  329. end
  330. # mqr=get_magic_quotes_runtime()
  331. # set_magic_quotes_runtime(0)
  332. @font_files.each_pair do |file, info|
  333. #Font file embedding
  334. newobj()
  335. @font_files[file]['n']=@n
  336. if(defined('FPDF_FONTPATH'))
  337. file=FPDF_FONTPATH+file
  338. end
  339. size=filesize(file)
  340. if(!size)
  341. Error('Font file not found')
  342. end
  343. out('<</Length '+size)
  344. if(file[-2]=='.z')
  345. out('/Filter /FlateDecode')
  346. end
  347. out('/Length1 '+info['length1'])
  348. unless info['length2'].nil?
  349. out('/Length2 '+info['length2']+' /Length3 0')
  350. end
  351. out('>>')
  352. f=fopen(file,'rb')
  353. putstream(fread(f,size))
  354. fclose(f)
  355. out('endobj')
  356. end
  357. #
  358. # set_magic_quotes_runtime(mqr)
  359. #
  360. @fonts.each_pair do |k, font|
  361. #Font objects
  362. newobj()
  363. @fonts[k]['n']=@n
  364. out('<</Type /Font')
  365. if(font['type']=='Type0')
  366. putType0(font)
  367. else
  368. name=font['name']
  369. out('/BaseFont /'+name)
  370. if(font['type']=='core')
  371. #Standard font
  372. out('/Subtype /Type1')
  373. if(name!='Symbol' and name!='ZapfDingbats')
  374. out('/Encoding /WinAnsiEncoding')
  375. end
  376. else
  377. #Additional font
  378. out('/Subtype /'+font['type'])
  379. out('/FirstChar 32')
  380. out('/LastChar 255')
  381. out('/Widths '+(@n+1)+' 0 R')
  382. out('/FontDescriptor '+(@n+2)+' 0 R')
  383. if(font['enc'])
  384. if !font['diff'].nil?
  385. out('/Encoding '+(nf+font['diff'])+' 0 R')
  386. else
  387. out('/Encoding /WinAnsiEncoding')
  388. end
  389. end
  390. end
  391. out('>>')
  392. out('endobj')
  393. if(font['type']!='core')
  394. #Widths
  395. newobj()
  396. cw=font['cw']
  397. s='['
  398. 32.upto(255) do |i|
  399. s+=cw[i.chr]+' '
  400. end
  401. out(s+']')
  402. out('endobj')
  403. #Descriptor
  404. newobj()
  405. s='<</Type /FontDescriptor /FontName /'+name
  406. font['desc'].each_pair do |k, v|
  407. s+=' /'+k+' '+v
  408. end
  409. file=font['file']
  410. if(file)
  411. s+=' /FontFile'+(font['type']=='Type1' ? '' : '2')+' '+@font_files[file]['n']+' 0 R'
  412. end
  413. out(s+'>>')
  414. out('endobj')
  415. end
  416. end
  417. end
  418. end
  419. def putType0(font)
  420. #Type0
  421. out('/Subtype /Type0')
  422. out('/BaseFont /'+font['name']+'-'+font['CMap'])
  423. out('/Encoding /'+font['CMap'])
  424. out('/DescendantFonts ['+(@n+1).to_s+' 0 R]')
  425. out('>>')
  426. out('endobj')
  427. #CIDFont
  428. newobj()
  429. out('<</Type /Font')
  430. out('/Subtype /CIDFontType0')
  431. out('/BaseFont /'+font['name'])
  432. out('/CIDSystemInfo <</Registry '+textstring('Adobe')+' /Ordering '+textstring(font['registry']['ordering'])+' /Supplement '+font['registry']['supplement'].to_s+'>>')
  433. out('/FontDescriptor '+(@n+1).to_s+' 0 R')
  434. if(font['CMap']=='ETen-B5-H')
  435. w='13648 13742 500'
  436. elsif(font['CMap']=='GBK-EUC-H')
  437. w='814 907 500 7716 [500]'
  438. else
  439. # ActionController::Base::logger.debug font['cw'].keys.sort.join(' ').to_s
  440. # ActionController::Base::logger.debug font['cw'].values.join(' ').to_s
  441. w='1 ['
  442. font['cw'].keys.sort.each {|key|
  443. w+=font['cw'][key].to_s + " "
  444. # ActionController::Base::logger.debug key.to_s
  445. # ActionController::Base::logger.debug font['cw'][key].to_s
  446. }
  447. w +=']'
  448. end
  449. out('/W ['+w+']>>')
  450. out('endobj')
  451. #Font descriptor
  452. newobj()
  453. out('<</Type /FontDescriptor')
  454. out('/FontName /'+font['name'])
  455. out('/Flags 6')
  456. out('/FontBBox [0 -200 1000 900]')
  457. out('/ItalicAngle 0')
  458. out('/Ascent 800')
  459. out('/Descent -200')
  460. out('/CapHeight 800')
  461. out('/StemV 50')
  462. out('>>')
  463. out('endobj')
  464. end
  465. end