rbpdf 1.18.7 → 1.19.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (119) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +12 -0
  3. data/Gemfile +3 -0
  4. data/README.md +1 -5
  5. data/Rakefile +10 -0
  6. data/lib/rbpdf.rb +715 -222
  7. data/lib/rbpdf/version.rb +1 -1
  8. data/rbpdf.gemspec +3 -3
  9. data/test/err_font1.rb +3 -0
  10. data/test/err_font2.rb +4 -0
  11. data/test/rbpdf_bidi_test.rb +85 -14
  12. data/test/rbpdf_bookmark_test.rb +1 -2
  13. data/test/rbpdf_cell_test.rb +1 -2
  14. data/test/rbpdf_content_test.rb +1 -1
  15. data/test/rbpdf_css_test.rb +1 -1
  16. data/test/rbpdf_dom_test.rb +1 -1
  17. data/test/rbpdf_font_func_test.rb +1 -1
  18. data/test/rbpdf_font_style_test.rb +1 -2
  19. data/test/rbpdf_font_test.rb +111 -1
  20. data/test/rbpdf_format_test.rb +1 -2
  21. data/test/rbpdf_func_test.rb +1 -1
  22. data/test/rbpdf_html_anchor_test.rb +107 -0
  23. data/test/rbpdf_html_func_test.rb +1 -1
  24. data/test/rbpdf_html_test.rb +196 -349
  25. data/test/rbpdf_htmlcell_test.rb +1 -1
  26. data/test/rbpdf_image_rmagick_test.rb +1 -2
  27. data/test/rbpdf_image_test.rb +1 -2
  28. data/test/rbpdf_test.rb +1 -1
  29. data/test/rbpdf_transaction_test.rb +1 -1
  30. data/test/rbpdf_viewerpreferences_test.rb +1 -1
  31. data/test/rbpdf_write_test.rb +1 -2
  32. data/test/test_helper.rb +2 -0
  33. metadata +38 -89
  34. data/lib/fonts/README.z +0 -2
  35. data/lib/fonts/arialunicid0_cw.rb +0 -1738
  36. data/lib/fonts/cid0cs.rb +0 -21
  37. data/lib/fonts/cid0ct.rb +0 -21
  38. data/lib/fonts/cid0jp.rb +0 -21
  39. data/lib/fonts/cid0kr.rb +0 -21
  40. data/lib/fonts/courier.rb +0 -37
  41. data/lib/fonts/dejavu-fonts-ttf-2.33/AUTHORS +0 -53
  42. data/lib/fonts/dejavu-fonts-ttf-2.33/BUGS +0 -3
  43. data/lib/fonts/dejavu-fonts-ttf-2.33/LICENSE +0 -99
  44. data/lib/fonts/dejavu-fonts-ttf-2.33/NEWS +0 -1315
  45. data/lib/fonts/dejavu-fonts-ttf-2.33/README +0 -59
  46. data/lib/fonts/dejavusans.ctg.z +0 -0
  47. data/lib/fonts/dejavusans.rb +0 -338
  48. data/lib/fonts/dejavusans.z +0 -0
  49. data/lib/fonts/dejavusansb.ctg.z +0 -0
  50. data/lib/fonts/dejavusansb.rb +0 -330
  51. data/lib/fonts/dejavusansb.z +0 -0
  52. data/lib/fonts/dejavusansbi.ctg.z +0 -0
  53. data/lib/fonts/dejavusansbi.rb +0 -297
  54. data/lib/fonts/dejavusansbi.z +0 -0
  55. data/lib/fonts/dejavusansi.ctg.z +0 -0
  56. data/lib/fonts/dejavusansi.rb +0 -305
  57. data/lib/fonts/dejavusansi.z +0 -0
  58. data/lib/fonts/freefont-20080912/AUTHORS +0 -191
  59. data/lib/fonts/freefont-20080912/COPYING +0 -341
  60. data/lib/fonts/freefont-20080912/CREDITS +0 -506
  61. data/lib/fonts/freefont-20080912/ChangeLog +0 -3320
  62. data/lib/fonts/freefont-20080912/INSTALL +0 -81
  63. data/lib/fonts/freefont-20080912/README +0 -108
  64. data/lib/fonts/freemono.ctg.z +0 -0
  65. data/lib/fonts/freemono.rb +0 -203
  66. data/lib/fonts/freemono.z +0 -0
  67. data/lib/fonts/freemonob.ctg.z +0 -0
  68. data/lib/fonts/freemonob.rb +0 -120
  69. data/lib/fonts/freemonob.z +0 -0
  70. data/lib/fonts/freemonobi.ctg.z +0 -0
  71. data/lib/fonts/freemonobi.rb +0 -84
  72. data/lib/fonts/freemonobi.z +0 -0
  73. data/lib/fonts/freemonoi.ctg.z +0 -0
  74. data/lib/fonts/freemonoi.rb +0 -136
  75. data/lib/fonts/freemonoi.z +0 -0
  76. data/lib/fonts/freesans.ctg.z +0 -0
  77. data/lib/fonts/freesans.rb +0 -196
  78. data/lib/fonts/freesans.z +0 -0
  79. data/lib/fonts/freesansb.ctg.z +0 -0
  80. data/lib/fonts/freesansb.rb +0 -136
  81. data/lib/fonts/freesansb.z +0 -0
  82. data/lib/fonts/freesansbi.ctg.z +0 -0
  83. data/lib/fonts/freesansbi.rb +0 -108
  84. data/lib/fonts/freesansbi.z +0 -0
  85. data/lib/fonts/freesansi.ctg.z +0 -0
  86. data/lib/fonts/freesansi.rb +0 -136
  87. data/lib/fonts/freesansi.z +0 -0
  88. data/lib/fonts/freeserif.ctg.z +0 -0
  89. data/lib/fonts/freeserif.rb +0 -285
  90. data/lib/fonts/freeserif.z +0 -0
  91. data/lib/fonts/freeserifb.ctg.z +0 -0
  92. data/lib/fonts/freeserifb.rb +0 -164
  93. data/lib/fonts/freeserifb.z +0 -0
  94. data/lib/fonts/freeserifbi.ctg.z +0 -0
  95. data/lib/fonts/freeserifbi.rb +0 -130
  96. data/lib/fonts/freeserifbi.z +0 -0
  97. data/lib/fonts/freeserifi.ctg.z +0 -0
  98. data/lib/fonts/freeserifi.rb +0 -151
  99. data/lib/fonts/freeserifi.z +0 -0
  100. data/lib/fonts/helvetica.rb +0 -34
  101. data/lib/fonts/helveticab.rb +0 -34
  102. data/lib/fonts/helveticabi.rb +0 -34
  103. data/lib/fonts/helveticai.rb +0 -34
  104. data/lib/fonts/hysmyeongjostdmedium.rb +0 -31
  105. data/lib/fonts/kozgopromedium.rb +0 -47
  106. data/lib/fonts/kozminproregular.rb +0 -46
  107. data/lib/fonts/msungstdlight.rb +0 -23
  108. data/lib/fonts/sjis.rb +0 -834
  109. data/lib/fonts/stsongstdlight.rb +0 -23
  110. data/lib/fonts/symbol.rb +0 -33
  111. data/lib/fonts/times.rb +0 -34
  112. data/lib/fonts/timesb.rb +0 -34
  113. data/lib/fonts/timesbi.rb +0 -34
  114. data/lib/fonts/timesi.rb +0 -34
  115. data/lib/fonts/uni2cid_ac15.rb +0 -23613
  116. data/lib/fonts/uni2cid_ag15.rb +0 -30222
  117. data/lib/fonts/uni2cid_aj16.rb +0 -15705
  118. data/lib/fonts/uni2cid_ak12.rb +0 -17530
  119. data/lib/fonts/zapfdingbats.rb +0 -33
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 15432b181857328b6ed5f8c5bbfa2ffce4d69782
4
- data.tar.gz: 3d8ef59f34b8bd101d6cbbd67ff30fb2fbdbfd04
3
+ metadata.gz: d6206e410a85c6ea47f3464985c9a4a8c6673974
4
+ data.tar.gz: 43b4d2ce869cdab5244ed41582fcdcaaf269aa15
5
5
  SHA512:
6
- metadata.gz: f3aae8a53859e23684be355730099acfd1770abf1de5f71d0e8adac27cb0862eef72585c9166affa8442c7f4c158e4a379ac32b9bdabfc57837a8006c329e767
7
- data.tar.gz: 6d6e239b6aa135886d4499cedcd8d2df2c20c57aa5f58646112907acabc3d2ec3e427d5af48d84d295410880d2e6b336de7a08d57a4d4e904725775ebf674c48
6
+ metadata.gz: 65e07e6d1a6a72cf760a6bf5492db77b08cd0ffa9dd32de92b65bb8ec5fff85b457c1c20ec28b4bce91a1b0216efa4f49292889995242cebf7bb6be16cbdeda6
7
+ data.tar.gz: c77b80be9193f445da757904c8937f01714aea5be51f82156af113c1e521128cbb413b8533e9a52027b0b16444e59ebec473ec7d73c01d5f6f22da1fe2bad54b
data/CHANGELOG CHANGED
@@ -1,3 +1,15 @@
1
+ 1.19.0 2015-11-20
2
+ - Update base version to TCPDF 5.2.000.
3
+ - IMPORTANT: Support for font subsetting was added by default to reduce the size of documents using large unicode font files.
4
+ If you embed the whole font in the PDF, the person on the other end can make changes to it even if he didn't have your font.
5
+ If you subset the font, file size of the PDF will be smaller but the person who receives your PDF would need to have your same font in order to make changes to your PDF.
6
+ - The signature of the SetFont() and AddFont() methods were changed to include the font subsetting option (subsetting is applied by default).
7
+ - html anchors support. (by Oleg German)
8
+ - Test framework is changed to test-unit.
9
+ This particular error will occur when the test suite is run from outside a Rails environment.
10
+ Support the test suite is run from outside a Rails environment.
11
+ - Fonts file was separated in rbpdf-font.gem.
12
+
1
13
  1.18.7 2015-10-18
2
14
  - Fixed case of missing HTML <pre> tag texts with whitespace. (use htmlentities gem liblary.)
3
15
  - Fixed HTML Image function y position problem with png alpha image.
data/Gemfile CHANGED
@@ -2,3 +2,6 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in rbpdf.gemspec
4
4
  gemspec
5
+
6
+ gem "actionpack"
7
+
data/README.md CHANGED
@@ -1,3 +1,4 @@
1
+ [![Build Status](https://travis-ci.org/naitoh/rbpdf.svg?branch=master)](https://travis-ci.org/naitoh/rbpdf)
1
2
 
2
3
  # RBPDF Template Plugin
3
4
 
@@ -26,11 +27,6 @@ RBPDF is distributed via RubyGems, and can be installed the usual way that you i
26
27
 
27
28
  ==
28
29
 
29
- If you are using HTML, it is recommended you install:
30
- ```
31
- gem install htmlentities
32
- ```
33
-
34
30
  If you are using image file, it is recommended you install:
35
31
  ```
36
32
  gem install rmagick
data/Rakefile CHANGED
@@ -1,2 +1,12 @@
1
1
  require "bundler/gem_tasks"
2
+ require 'rake/testtask'
3
+
4
+ desc 'Run test_unit based test'
5
+ Rake::TestTask.new do |t|
6
+ # To run test for only one file (or file path pattern)
7
+ # $ bundle exec rake test TEST=test/test_specified_path.rb
8
+ t.libs << "test"
9
+ t.test_files = Dir["test/rbpdf_*.rb"]
10
+ t.verbose = true
11
+ end
2
12
 
@@ -2,9 +2,9 @@
2
2
  #============================================================+
3
3
  # File name : rbpdf.rb
4
4
  # Begin : 2002-08-03
5
- # Last Update : 2010-05-27
5
+ # Last Update : 2010-06-02
6
6
  # Author : Nicola Asuni
7
- # Version : 5.1.002
7
+ # Version : 5.2.000
8
8
  # License : GNU LGPL (http://www.gnu.org/copyleft/lesser.html)
9
9
  # ----------------------------------------------------------------------------
10
10
  # This program is free software: you can redistribute it and/or modify
@@ -48,11 +48,8 @@
48
48
 
49
49
  require "rbpdf/version"
50
50
 
51
- begin
52
- require('htmlentities')
53
- rescue LoadError
54
- # This gem is not required - just nice to have.
55
- end
51
+ require 'htmlentities'
52
+ require 'rbpdf-font'
56
53
 
57
54
  begin
58
55
  # RMagick 2.14.0
@@ -72,6 +69,7 @@ end
72
69
  require 'core/rmagick'
73
70
 
74
71
  # Needed to run the test suite outside of a Rails environment.
72
+ require 'rubygems' if RUBY_VERSION < '1.9' # Ruby 1.8.7
75
73
  require 'action_view'
76
74
  require 'tempfile'
77
75
  require 'uri'
@@ -81,21 +79,7 @@ require 'uri'
81
79
  #
82
80
 
83
81
 
84
- PDF_PRODUCER = 'RBPDF 5.1.002'
85
-
86
- module RBPDFFontDescriptor
87
- @@descriptors = { 'freesans' => {} }
88
- @@font_name = 'freesans'
89
-
90
- def self.font(font_name)
91
- @@descriptors[font_name.gsub(".rb", "")]
92
- end
93
-
94
- def self.define(font_name = 'freesans')
95
- @@descriptors[font_name] ||= {}
96
- yield @@descriptors[font_name]
97
- end
98
- end
82
+ PDF_PRODUCER = 'RBPDF 5.2.000'
99
83
 
100
84
  # == This is a Ruby class for generating PDF files on-the-fly without requiring external extensions.
101
85
  # * This class is a Ruby port of the TCPDF class by Nicola Asuni (http://www.tcpdf.org).
@@ -285,6 +269,8 @@ class RBPDF
285
269
  @@k_path_url = Dir.tmpdir
286
270
  end
287
271
 
272
+ @@k_path_fonts = RBPDFFontDescriptor.getfontpath
273
+
288
274
  # set disk caching
289
275
  @diskcache = diskcache ? true : false
290
276
 
@@ -447,12 +433,15 @@ class RBPDF
447
433
  @diffs ||= []
448
434
  @images ||= {}
449
435
  @links ||= []
436
+ @html_anchors ||= {}
437
+ @html_anchor_links ||= {}
450
438
  @gradients ||= []
451
439
  @in_footer ||= false
452
440
  @lasth ||= 0
453
441
  @font_family ||= 'helvetica'
454
442
  @font_style ||= ''
455
443
  @font_size_pt ||= 12
444
+ @font_subsetting ||= true
456
445
  @underline ||= false
457
446
  @overline ||= false
458
447
  @linethrough ||= false
@@ -1540,6 +1529,8 @@ class RBPDF
1540
1529
  # close page
1541
1530
  endPage()
1542
1531
  lastPage()
1532
+ resetLinksAfterCurrentPage()
1533
+
1543
1534
  @state = 2
1544
1535
  SetAutoPageBreak(false)
1545
1536
  @y = @h - (1 / @k)
@@ -2612,11 +2603,12 @@ class RBPDF
2612
2603
  # * BI or IB: bold italic
2613
2604
  # [@param string :fontfile] The font definition file. By default, the name is built from the family and style, in lower case with no space.
2614
2605
  # [@return array] containing the font data, or false in case of error.
2606
+ # [@param boolean :subset] if true embedd only a subset of the font (stores only the information related to the used characters); this option is valid only for TrueTypeUnicode fonts. If you want to enable users to change the document, set this parameter to false. If you subset the font, the person who receives your PDF would need to have your same font in order to make changes to your PDF. The file size of the PDF would also be smaller because you are embedding only part of a font.
2615
2607
  # [@access public]
2616
2608
  # [@since 1.5]
2617
2609
  # [@see] SetFont()
2618
2610
  #
2619
- def AddFont(family, style='', fontfile='')
2611
+ def AddFont(family, style='', fontfile='', subset=nil)
2620
2612
  if empty_string(family)
2621
2613
  if !empty_string(@font_family)
2622
2614
  family = @font_family
@@ -2624,6 +2616,7 @@ class RBPDF
2624
2616
  Error('Empty font family')
2625
2617
  end
2626
2618
  end
2619
+ subset = @font_subsetting if subset.nil?
2627
2620
 
2628
2621
  family = family.downcase
2629
2622
  if ((!@is_unicode) and (family == 'arial'))
@@ -2769,17 +2762,21 @@ class RBPDF
2769
2762
  desc['ItalicAngle'] = -11
2770
2763
  end
2771
2764
  end
2772
- setFontBuffer(fontkey, {'i' => @numfonts, 'type' => font_desc[:type], 'name' => sname, 'desc' => desc, 'cidinfo' => font_desc[:cidinfo], 'up' => font_desc[:up], 'ut' => font_desc[:ut], 'cw' => font_desc[:cw], 'dw' => font_desc[:dw], 'enc' => font_desc[:enc]})
2773
2765
  elsif font_desc[:type] == 'core'
2774
2766
  font_desc[:name] = @core_fonts[fontkey]
2767
+ subset = false
2775
2768
  elsif (font_desc[:type] == 'TrueType') or (font_desc[:type] == 'Type1')
2776
2769
  # ...
2770
+ subset = false
2777
2771
  elsif font_desc[:type] == 'TrueTypeUnicode'
2778
2772
  font_desc[:enc] = 'Identity-H'
2779
2773
  else
2780
- Error('Unknow font type: ' + type + '')
2774
+ Error('Unknow font type: ' + font_desc[:type] + '')
2781
2775
  end
2782
- setFontBuffer(fontkey, {'i' => @numfonts, 'type' => font_desc[:type], 'name' => font_desc[:name], 'desc' => desc, 'up' => font_desc[:up], 'ut' => font_desc[:ut], 'cw' => font_desc[:cw], 'dw' => font_desc[:dw], 'enc' => font_desc[:enc], 'cidinfo' => font_desc[:cidinfo], 'file' => font_desc[:file], 'ctg' => font_desc[:ctg]})
2776
+ # initialize subsetchars to contain default ASCII values (0-255)
2777
+ subsetchars = Array.new(256, true)
2778
+
2779
+ setFontBuffer(fontkey, {'fontkey' => fontkey, 'i' => @numfonts, 'type' => font_desc[:type], 'name' => font_desc[:name], 'desc' => desc, 'up' => font_desc[:up], 'ut' => font_desc[:ut], 'cw' => font_desc[:cw], 'dw' => font_desc[:dw], 'enc' => font_desc[:enc], 'cidinfo' => font_desc[:cidinfo], 'file' => font_desc[:file], 'ctg' => font_desc[:ctg], 'subset' => subset, 'subsetchars' => subsetchars})
2783
2780
 
2784
2781
  if (!font_desc[:diff].nil? and (!font_desc[:diff].empty?))
2785
2782
  #Search existing encodings
@@ -2798,10 +2795,19 @@ class RBPDF
2798
2795
  setFontSubBuffer(fontkey, 'diff', d)
2799
2796
  end
2800
2797
  if !empty_string(font_desc[:file])
2801
- if (font_desc[:type] == 'TrueType') or (font_desc[:type] == 'TrueTypeUnicode')
2802
- @font_files[font_desc[:file]] = {'length1' => font_desc[:originalsize], 'fontdir' => fontdir}
2803
- elsif font_desc[:type] != 'core'
2804
- @font_files[font_desc[:file]] = {'length1' => font_desc[:size1], 'length2' => font_desc[:size2], 'fontdir' => fontdir}
2798
+ if @font_files[font_desc[:file]].nil?
2799
+ if (font_desc[:type] == 'TrueType') or (font_desc[:type] == 'TrueTypeUnicode')
2800
+ @font_files[font_desc[:file]] = {'length1' => font_desc[:originalsize], 'fontdir' => fontdir, 'subset' => subset, 'fontkeys' => [fontkey]}
2801
+ elsif font_desc[:type] != 'core'
2802
+ @font_files[font_desc[:file]] = {'length1' => font_desc[:size1], 'length2' => font_desc[:size2], 'fontdir' => fontdir, 'subset' => subset, 'fontkeys' => [fontkey]}
2803
+ end
2804
+ else
2805
+ # update fontkeys that are sharing this font file
2806
+ @font_files[font_desc[:file]]['subset'] = (@font_files[font_desc[:file]]['subset'] and subset)
2807
+ unless @font_files[font_desc[:file]]['fontkeys'].include? fontkey
2808
+ @font_files[font_desc[:file]]['fontkeys'] ||= []
2809
+ @font_files[font_desc[:file]]['fontkeys'].push fontkey
2810
+ end
2805
2811
  end
2806
2812
  end
2807
2813
  return fontdata
@@ -2845,17 +2851,19 @@ class RBPDF
2845
2851
  # or any combination. The default value is regular. Bold and italic styles do not apply to Symbol and ZapfDingbats basic fonts or other fonts when not defined.
2846
2852
  # [@param float :size] Font size in points. The default value is the current size. If no size has been specified since the beginning of the document, the value taken is 12
2847
2853
  # [@param string :fontfile] The font definition file. By default, the name is built from the family and style, in lower case with no spaces.
2854
+ # [@param boolean :subset] if true embedd only a subset of the font (stores only the information related to the used characters); this option is valid only for TrueTypeUnicode fonts. If you want to enable users to change the document, set this parameter to false. If you subset the font, the person who receives your PDF would need to have your same font in order to make changes to your PDF. The file size of the PDF would also be smaller because you are embedding only part of a font.
2855
+ # [@author Nicola Asuni]
2848
2856
  # [@access public]
2849
2857
  # [@since 1.0]
2850
2858
  # [@see] AddFont(), SetFontSize()
2851
2859
  #
2852
- def SetFont(family, style='', size=0, fontfile='')
2860
+ def SetFont(family, style='', size=0, fontfile='', subset=nil)
2853
2861
  # Select a font; size given in points
2854
2862
  if size == 0
2855
2863
  size = @font_size_pt
2856
2864
  end
2857
2865
  # try to add font (if not already added)
2858
- fontdata = AddFont(family, style, fontfile)
2866
+ fontdata = AddFont(family, style, fontfile, subset)
2859
2867
  @font_family = fontdata['family']
2860
2868
  @font_style = fontdata['style']
2861
2869
  @current_font = getFontBuffer(fontdata['fontkey'])
@@ -3268,6 +3276,34 @@ class RBPDF
3268
3276
  end
3269
3277
  alias_method :break_the_page?, :BreakThePage?
3270
3278
 
3279
+ #
3280
+ # Removes SHY characters from text.
3281
+ # Unicode Data:
3282
+ # * Name : SOFT HYPHEN, commonly abbreviated as SHY
3283
+ # * HTML Entity (decimal): &#173;
3284
+ # * HTML Entity (hex): &#xad;
3285
+ # * HTML Entity (named): &shy;
3286
+ # * How to type in Microsoft Windows: [Alt +00AD] or [Alt 0173]
3287
+ # * UTF-8 (hex): 0xC2 0xAD (c2ad)
3288
+ # * UTF-8 character: chr(194).chr(173)
3289
+ # [@param string :txt] input string
3290
+ # [@return string] without SHY characters.
3291
+ # [@access public]
3292
+ # [@since (4.5.019) 2009-02-28]
3293
+ #
3294
+ def removeSHY(txt='')
3295
+ txt = txt.dup
3296
+ txt.force_encoding('ASCII-8BIT') if txt.respond_to?(:force_encoding)
3297
+ txt.gsub!(/([\xc2]{1}[\xad]{1})/, '')
3298
+ if !@is_unicode
3299
+ txt.gsub!(/([\xad]{1})/, '')
3300
+ return txt
3301
+ end
3302
+ txt.force_encoding('UTF-8') if txt.respond_to?(:force_encoding)
3303
+ return txt
3304
+ end
3305
+ alias_method :remove_shy, :removeSHY
3306
+
3271
3307
  #
3272
3308
  # Prints a cell (rectangular area) with optional borders, background color and character string. The upper-left corner of the cell corresponds to the current position. The text can be aligned or centered. After the call, the current position moves to the right or to the next line. It is possible to put a link on the text.
3273
3309
  # If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting.
@@ -3333,35 +3369,6 @@ class RBPDF
3333
3369
  end
3334
3370
  alias_method :cell, :Cell
3335
3371
 
3336
- #
3337
- # Removes SHY characters from text.
3338
- # [@param string :txt] input string
3339
- # [@return string] without SHY characters.
3340
- # [@access public]
3341
- # [@since (4.5.019) 2009-02-28]
3342
- #
3343
- def removeSHY(txt='')
3344
- txt = txt.dup
3345
- # Unicode Data
3346
- # Name : SOFT HYPHEN, commonly abbreviated as SHY
3347
- # HTML Entity (decimal): &#173;
3348
- # HTML Entity (hex): &#xad;
3349
- # HTML Entity (named): &shy;
3350
- # How to type in Microsoft Windows: [Alt +00AD] or [Alt 0173]
3351
- # UTF-8 (hex): 0xC2 0xAD (c2ad)
3352
- # UTF-8 character: chr(194).chr(173)
3353
-
3354
- txt.force_encoding('ASCII-8BIT') if txt.respond_to?(:force_encoding)
3355
- txt.gsub!(/([\xc2]{1}[\xad]{1})/, '')
3356
- if !@is_unicode
3357
- txt.gsub!(/([\xad]{1})/, '')
3358
- return txt
3359
- end
3360
- txt.force_encoding('UTF-8') if txt.respond_to?(:force_encoding)
3361
- return txt
3362
- end
3363
- alias_method :remove_shy, :removeSHY
3364
-
3365
3372
  #
3366
3373
  # Returns the PDF string code to print a cell (rectangular area) with optional borders, background color and character string. The upper-left corner of the cell corresponds to the current position. The text can be aligned or centered. After the call, the current position moves to the right or to the next line. It is possible to put a link on the text.
3367
3374
  # If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting.
@@ -5449,7 +5456,9 @@ class RBPDF
5449
5456
  def Output(name='', dest='')
5450
5457
  #Output PDF to some destination
5451
5458
  #Finish document if necessary
5459
+
5452
5460
  lastPage()
5461
+
5453
5462
  if (@state < 3)
5454
5463
  Close();
5455
5464
  end
@@ -6446,8 +6455,521 @@ protected
6446
6455
  return @apxo_obj_id
6447
6456
  end
6448
6457
 
6458
+ #
6459
+ # Get ULONG from string (Big Endian 32-bit unsigned integer).
6460
+ # [@parameter string :str] string from where to extract value
6461
+ # [@parameter int :offset] point from where to read the data
6462
+ # [@return int] 32 bit value
6463
+ # [@author Nicola Asuni]
6464
+ # [@access protected]
6465
+ # [@since 5.2.000 (2010-06-02)]
6466
+ #
6467
+ def getULONG(str, offset)
6468
+ v = str[offset, 4].unpack('N')
6469
+ return v[0].to_i
6470
+ end
6471
+
6472
+ #
6473
+ # Get USHORT from string (Big Endian 16-bit unsigned integer).
6474
+ # [@parameter string :str] string from where to extract value
6475
+ # [@parameter int :offset] point from where to read the data
6476
+ # [@return int] 16 bit value
6477
+ # [@author Nicola Asuni]
6478
+ # [@access protected]
6479
+ # [@since 5.2.000 (2010-06-02)]
6480
+ #
6481
+ def getUSHORT(str, offset)
6482
+ v = str[offset, 2].unpack('n')
6483
+ return v[0].to_i
6484
+ end
6485
+
6486
+ #
6487
+ # Get SHORT from string (Big Endian 16-bit signed integer).
6488
+ # [@parameter string :str] string from where to extract value
6489
+ # [@parameter int :offset] point from where to read the data
6490
+ # [@return int] 16 bit value
6491
+ # [@author Nicola Asuni]
6492
+ # [@access protected]
6493
+ # [@since 5.2.000 (2010-06-02)]
6494
+ #
6495
+ def getSHORT(str, offset)
6496
+ v = str[offset, 2].unpack('s')
6497
+ return v[0].to_i
6498
+ end
6499
+
6500
+ #
6501
+ # Get BYTE from string (8-bit unsigned integer).
6502
+ # [@parameter string :str] string from where to extract value
6503
+ # [@parameter int :offset] point from where to read the data
6504
+ # [@return int] 8 bit value
6505
+ # [@author Nicola Asuni]
6506
+ # [@access protected]
6507
+ # [@since 5.2.000 (2010-06-02)]
6508
+ #
6509
+ def getBYTE(str, offset)
6510
+ v = str[offset, 1].unpack('C')
6511
+ return v[0].to_i
6512
+ end
6513
+
6514
+ #
6515
+ # Returns a subset of the TrueType font data without the unused glyphs.
6516
+ # [@parameter string :font] TrueType font data
6517
+ # [@parameter array :subsetchars] array of used characters (the glyphs to keep)
6518
+ # [@return string] a subset of TrueType font data without the unused glyphs
6519
+ # [@author Nicola Asuni]
6520
+ # [@access protected]
6521
+ # [@since 5.2.000 (2010-06-02)]
6522
+ #
6523
+ def getTrueTypeFontSubset(font, subsetchars)
6524
+ #ksort(subsetchars)
6525
+ offset = 0 # offset position of the font data
6526
+ if getULONG(font, offset) != 0x10000
6527
+ # sfnt version must be 0x00010000 for TrueType version 1.0.
6528
+ return font
6529
+ end
6530
+ offset += 4
6531
+
6532
+ # get number of tables
6533
+ numTables = getUSHORT(font, offset); offset += 2
6534
+ # skip searchRange, entrySelector and rangeShift
6535
+ offset += 6
6536
+ # tables array(Hash)
6537
+ table = {}
6538
+ # for each table
6539
+ numTables.times {
6540
+ # get table info
6541
+ tag = font[offset, 4]
6542
+ offset += 4
6543
+
6544
+ table[tag] = {}
6545
+ table[tag]['checkSum'] = getULONG(font, offset); offset += 4
6546
+ table[tag]['offset'] = getULONG(font, offset); offset += 4
6547
+ table[tag]['length'] = getULONG(font, offset); offset += 4
6548
+ }
6549
+ # check magicNumber
6550
+ offset = table['head']['offset'] + 12
6551
+ if getULONG(font, offset) != 0x5F0F3CF5
6552
+ # magicNumber must be 0x5F0F3CF5
6553
+ return font
6554
+ end
6555
+ offset += 4
6556
+
6557
+ # get offset mode (indexToLocFormat : 0 = short, 1 = long)
6558
+ offset = table['head']['offset'] + 50
6559
+ short_offset = (getSHORT(font, offset) == 0); offset += 2
6560
+ # get the offsets to the locations of the glyphs in the font, relative to the beginning of the glyphData table
6561
+ indexToLoc = []
6562
+ offset = table['loca']['offset']
6563
+ if short_offset
6564
+ # short version
6565
+ n = table['loca']['length'] / 2 # numGlyphs + 1
6566
+ n.times {|i|
6567
+ indexToLoc[i] = getUSHORT(font, offset) * 2; offset += 2
6568
+ }
6569
+ else
6570
+ # long version
6571
+ n = table['loca']['length'] / 4 # numGlyphs + 1
6572
+ n.times {|i|
6573
+ indexToLoc[i] = getULONG(font, offset); offset += 4
6574
+ }
6575
+ end
6576
+ # get glyphs indexes of chars from cmap table
6577
+ subsetglyphs = [] # glyph IDs on key
6578
+ subsetglyphs[0] = true # character codes that do not correspond to any glyph in the font should be mapped to glyph index 0
6579
+ offset = table['cmap']['offset'] + 2
6580
+ numEncodingTables = getUSHORT(font, offset); offset += 2
6581
+ encodingTables = []
6582
+ numEncodingTables.times {|i|
6583
+ encodingTables[i] ||= {}
6584
+ encodingTables[i]['platformID'] = getUSHORT(font, offset); offset += 2
6585
+ encodingTables[i]['encodingID'] = getUSHORT(font, offset); offset += 2
6586
+ encodingTables[i]['offset'] = getULONG(font, offset); offset += 4
6587
+ }
6588
+ encodingTables.each {|enctable|
6589
+ if (enctable['platformID'] == 3) and (enctable['encodingID'] == 0)
6590
+ modesymbol = true
6591
+ else
6592
+ modesymbol = false
6593
+ end
6594
+ offset = table['cmap']['offset'] + enctable['offset']
6595
+ format = getUSHORT(font, offset); offset += 2
6596
+ case format
6597
+ when 0 # Format 0: Byte encoding table
6598
+ offset += 4 # skip length and version/language
6599
+ 256.times {|k|
6600
+ if subsetchars[k]
6601
+ g = getBYTE(font, offset); offset += 1
6602
+ subsetglyphs[g] = k
6603
+ else
6604
+ offset += 1
6605
+ end
6606
+ }
6607
+ when 2 # Format 2: High-byte mapping through table
6608
+ offset += 4 # skip length and version
6609
+ # to be implemented ...
6610
+ when 4 # Format 4: Segment mapping to delta values
6611
+ length = getUSHORT(font, offset); offset += 2
6612
+ offset += 2 # skip version/language
6613
+ segCount = getUSHORT(font, offset) / 2; offset += 2
6614
+ offset += 6 # skip searchRange, entrySelector, rangeShift
6615
+ endCount = [] # array of end character codes for each segment
6616
+ segCount.times {|k|
6617
+ endCount[k] = getUSHORT(font, offset); offset += 2
6618
+ }
6619
+ offset += 2 # skip reservedPad
6620
+ startCount = [] # array of start character codes for each segment
6621
+ segCount.times {|k|
6622
+ startCount[k] = getUSHORT(font, offset); offset += 2
6623
+ }
6624
+ idDelta = [] # delta for all character codes in segment
6625
+ segCount.times {|k|
6626
+ idDelta[k] = getUSHORT(font, offset); offset += 2
6627
+ }
6628
+ idRangeOffset = [] # Offsets into glyphIdArray or 0
6629
+ segCount.times {|k|
6630
+ idRangeOffset[k] = getUSHORT(font, offset); offset += 2
6631
+ }
6632
+ gidlen = (length / 2) - 8 - (4 * segCount)
6633
+ glyphIdArray = [] # glyph index array
6634
+ gidlen.times {|k|
6635
+ glyphIdArray[k] = getUSHORT(font, offset); offset += 2
6636
+ }
6637
+ segCount.times {|k|
6638
+ startCount[k].upto(endCount[k]) {|c|
6639
+ if subsetchars[c]
6640
+ if idRangeOffset[k] == 0
6641
+ g = c
6642
+ else
6643
+ gid = (idRangeOffset[k] / 2) + (c - startCount[k]) - (segCount - k)
6644
+ g = glyphIdArray[gid]
6645
+ end
6646
+ g += (idDelta[k] - 65536)
6647
+ if g < 0
6648
+ g = 0
6649
+ end
6650
+ subsetglyphs[g] = c
6651
+ end
6652
+ }
6653
+ }
6654
+ when 6 # Format 6: Trimmed table mapping
6655
+ offset += 4 # skip length and version/language
6656
+ firstCode = getUSHORT(font, offset); offset += 2
6657
+ entryCount = getUSHORT(font, offset); offset += 2
6658
+ entryCount.times {|k|
6659
+ c = k + firstCode
6660
+ if subsetchars[c]
6661
+ g = getUSHORT(font, offset); offset += 2
6662
+ subsetglyphs[g] = c
6663
+ else
6664
+ offset += 2
6665
+ end
6666
+ }
6667
+ when 8 # Format 8: Mixed 16-bit and 32-bit coverage
6668
+ offset += 10 # skip length and version
6669
+ # to be implemented ...
6670
+ when 10 # Format 10: Trimmed array
6671
+ offset += 10 # skip length and version/language
6672
+ startCharCode = getULONG(font, offset); offset += 4
6673
+ numChars = getULONG(font, offset); offset += 4
6674
+ numChars.times {|k|
6675
+ c = k + startCharCode
6676
+ if subsetchars[c]
6677
+ g = getUSHORT(font, offset); offset += 2
6678
+ subsetglyphs[g] = c
6679
+ else
6680
+ offset += 2
6681
+ end
6682
+ }
6683
+ when 12 # Format 12: Segmented coverage
6684
+ offset += 10 # skip length and version/language
6685
+ nGroups = getULONG(font, offset); offset += 4
6686
+ nGroups.times {|k|
6687
+ startCharCode = getULONG(font, offset); offset += 4
6688
+ endCharCode = getULONG(font, offset); offset += 4
6689
+ startGlyphCode = getULONG(font, offset); offset += 4
6690
+ startCharCode.upto(endCharCode) {|c|
6691
+ if subsetchars[c]
6692
+ subsetglyphs[startGlyphCode] = c
6693
+ end
6694
+ startGlyphCode += 1
6695
+ }
6696
+ }
6697
+ end
6698
+ }
6699
+ # sort glyphs by key
6700
+ #ksort(subsetglyphs)
6701
+ # add composite glyps to subsetglyphs and remove missing glyphs
6702
+ subsetglyphs.each_with_index {|val, key|
6703
+ next if val.nil?
6704
+ if indexToLoc[key]
6705
+ offset = table['glyf']['offset'] + indexToLoc[key]
6706
+ numberOfContours = getSHORT(font, offset); offset += 2
6707
+ if numberOfContours < 0 # composite glyph
6708
+ offset += 8 # skip xMin, yMin, xMax, yMax
6709
+ loop {
6710
+ flags = getUSHORT(font, offset); offset += 2
6711
+ glyphIndex = getUSHORT(font, offset); offset += 2
6712
+ if subsetglyphs[glyphIndex].nil? and indexToLoc[glyphIndex]
6713
+ # add missing glyphs
6714
+ subsetglyphs[glyphIndex] = true
6715
+ end
6716
+ # skip some bytes by case
6717
+ if (flags & 1) != 0
6718
+ offset += 4
6719
+ else
6720
+ offset += 2
6721
+ end
6722
+ if (flags & 8) != 0
6723
+ offset += 2
6724
+ elsif (flags & 64) != 0
6725
+ offset += 4
6726
+ elsif (flags & 128) != 0
6727
+ offset += 8
6728
+ end
6729
+
6730
+ break if (flags & 32) == 0
6731
+ }
6732
+ end
6733
+ else
6734
+ subsetglyphs.delete_at(key)
6735
+ end
6736
+ }
6737
+ # build new glyf table with only used glyphs
6738
+ glyf = ''
6739
+ glyfSize = 0
6740
+ # create new empty indexToLoc table
6741
+ newIndexToLoc = Array.new(indexToLoc.length, 0)
6742
+ goffset = 0
6743
+ subsetglyphs.each_with_index {|char, glyphID|
6744
+ next if char.nil?
6745
+ if indexToLoc[glyphID] and indexToLoc[glyphID + 1]
6746
+ start = indexToLoc[glyphID]
6747
+ length = indexToLoc[glyphID + 1] - start
6748
+ glyf << font[table['glyf']['offset'] + start, length]
6749
+ newIndexToLoc[glyphID] = goffset
6750
+ goffset += length
6751
+ end
6752
+ }
6753
+ # build new loca table
6754
+ loca = ''
6755
+ if short_offset
6756
+ newIndexToLoc.each {|offset|
6757
+ loca << [offset / 2].pack('n')
6758
+ }
6759
+ else
6760
+ newIndexToLoc.each {|offset|
6761
+ loca << [offset].pack('N')
6762
+ }
6763
+ end
6764
+ # array of table names to preserve (loca and glyf tables will be added later)
6765
+ # table_names = ['cmap', 'head', 'hhea', 'hmtx', 'maxp', 'name', 'OS/2', 'post', 'cvt ', 'fpgm', 'prep']
6766
+ table_names = ['head', 'hhea', 'hmtx', 'maxp', 'OS/2', 'cvt ', 'fpgm', 'prep']
6767
+ # get the tables to preserve
6768
+ offset = 12
6769
+ table.each {|tag, val|
6770
+ if table_names.include?(tag)
6771
+ table[tag]['data'] = font[table[tag]['offset'], table[tag]['length']]
6772
+ if tag == 'head'
6773
+ # set the checkSumAdjustment to 0
6774
+ table[tag]['data'] = table[tag]['data'][0, 8] + "\x0\x0\x0\x0" + table[tag]['data'][12..-1]
6775
+ end
6776
+ pad = 4 - (table[tag]['length'] % 4)
6777
+ if pad != 4
6778
+ # the length of a table must be a multiple of four bytes
6779
+ table[tag]['length'] += pad
6780
+ table[tag]['data'] << "\x0" * pad
6781
+ end
6782
+ table[tag]['offset'] = offset
6783
+ offset += table[tag]['length']
6784
+ # table[tag]['checkSum'] = getTTFtableChecksum(table[tag]['data'], table[tag]['length'])
6785
+ else
6786
+ table.delete(tag)
6787
+ end
6788
+ }
6789
+ # add loca
6790
+
6791
+ table['loca'] = {}
6792
+ table['loca']['data'] = loca
6793
+ table['loca']['length'] = loca.length
6794
+ pad = 4 - (table['loca']['length'] % 4)
6795
+ if pad != 4
6796
+ # the length of a table must be a multiple of four bytes
6797
+ table['loca']['length'] += pad
6798
+ table['loca']['data'] << "\x0" * pad
6799
+ end
6800
+ table['loca']['offset'] = offset
6801
+ table['loca']['checkSum'] = getTTFtableChecksum(table['loca']['data'], table['loca']['length'])
6802
+ offset += table['loca']['length']
6803
+ # add glyf
6804
+ table['glyf'] = {}
6805
+ table['glyf']['data'] = glyf
6806
+ table['glyf']['length'] = glyf.length
6807
+ pad = 4 - (table['glyf']['length'] % 4)
6808
+ if pad != 4
6809
+ # the length of a table must be a multiple of four bytes
6810
+ table['glyf']['length'] += pad
6811
+ table['glyf']['data'] << "\x0" * pad
6812
+ end
6813
+ table['glyf']['offset'] = offset
6814
+ table['glyf']['checkSum'] = getTTFtableChecksum(table['glyf']['data'], table['glyf']['length'])
6815
+ # rebuild font
6816
+ font = ''
6817
+ font << [0x10000].pack('N') # sfnt version
6818
+ numTables = table.length
6819
+ font << [numTables].pack('n') # numTables
6820
+ entrySelector = (Math.log(numTables) / Math.log(2.0)).floor
6821
+
6822
+ searchRange = 2 **entrySelector * 16
6823
+ rangeShift = numTables * 16 - searchRange
6824
+ font << [searchRange].pack('n') # searchRange
6825
+ font << [entrySelector].pack('n') # entrySelector
6826
+ font << [rangeShift].pack('n') # rangeShift
6827
+ offset = numTables * 16
6828
+ table.each{|tag, data|
6829
+ font << tag # tag
6830
+ font << [data['checkSum']].pack('N') # checkSum
6831
+ font << [data['offset'] + offset].pack('N') # offset
6832
+ font << [data['length']].pack('N') # length
6833
+ }
6834
+ table.each{|tag, data|
6835
+ font << data['data']
6836
+ }
6837
+ # set checkSumAdjustment on head table
6838
+ checkSumAdjustment = 0xB1B0AFBA - getTTFtableChecksum(font, font.length)
6839
+ font = font[0, table['head']['offset'] + 8] + [checkSumAdjustment].pack('N') + font[(table['head']['offset'] + 12)..-1]
6840
+ return font
6841
+ end
6842
+
6843
+ #
6844
+ # Returs the checksum of a TTF table.
6845
+ # [@parameter string :table] table to check
6846
+ # [@parameter int :length] lenght of table in bytes
6847
+ # [@return int] checksum
6848
+ # [@author Nicola Asuni]
6849
+ # [@access protected]
6850
+ # [@since 5.2.000 (2010-06-02)]
6851
+ #
6852
+ def getTTFtableChecksum(table, length)
6853
+ sum = 0
6854
+ tlen = length / 4
6855
+ offset = 0
6856
+ tlen.times {
6857
+ v = table[offset, 4].unpack('N')
6858
+ sum += v[0]
6859
+ offset += 4
6860
+ }
6861
+ sum %= 0x100000000
6862
+ sum = [sum].pack('N').unpack('N')
6863
+ return sum[0]
6864
+ end
6865
+
6866
+ #
6867
+ # Outputs font widths
6868
+ # [@parameter array :font] font data
6869
+ # [@parameter int :cidoffset] offset for CID values
6870
+ # [@return] PDF command string for font widths
6871
+ # [@author] Nicola Asuni
6872
+ # [@access protected]
6873
+ # [@since 4.4.000 (2008-12-07)]
6874
+ #
6875
+ def putfontwidths(font, cidoffset=0)
6876
+ font_cw = font['cw'].sort
6877
+ rangeid = 0
6878
+ range = []
6879
+ prevcid = -2
6880
+ prevwidth = -1
6881
+ interval = false
6882
+ range_interval = []
6883
+ # for each character
6884
+ font_cw.each {|cid, width|
6885
+ cid -= cidoffset
6886
+ if font['subset'] and (cid > 255) and font['subsetchars'][cid].nil?
6887
+ # ignore the unused characters (font subsetting)
6888
+ next
6889
+ end
6890
+ if width != font['dw']
6891
+ if cid == prevcid + 1
6892
+ # consecutive CID
6893
+ if width == prevwidth
6894
+ if width == range[rangeid][0]
6895
+ range[rangeid].push width
6896
+ else
6897
+ range[rangeid].pop
6898
+ # new range
6899
+ rangeid = prevcid
6900
+ range[rangeid] = []
6901
+ range[rangeid].push prevwidth
6902
+ range[rangeid].push width
6903
+ end
6904
+ interval = true
6905
+ range_interval[rangeid] = true
6906
+ else
6907
+ if interval
6908
+ # new range
6909
+ rangeid = cid
6910
+ range[rangeid] = []
6911
+ range[rangeid].push width
6912
+ else
6913
+ range[rangeid].push width
6914
+ end
6915
+ interval = false
6916
+ end
6917
+ else
6918
+ # new range
6919
+ rangeid = cid
6920
+ range[rangeid] = []
6921
+ range[rangeid].push width
6922
+ interval = false
6923
+ end
6924
+ prevcid = cid
6925
+ prevwidth = width
6926
+ end
6927
+ }
6928
+ # optimize ranges
6929
+ prevk = -1
6930
+ nextk = -1
6931
+ prevint = false
6932
+ range.each_with_index {|ws, k|
6933
+ cws = ws ? ws.length : 0
6934
+ if (k == nextk) and !prevint and (range_interval[k].nil? or (cws < 4))
6935
+ if !range_interval[k].nil?
6936
+ range_interval[k] = nil
6937
+ end
6938
+ range[prevk] = range[prevk].concat(range[k]) if range[k]
6939
+ range[k] = nil
6940
+ else
6941
+ prevk = k
6942
+ end
6943
+ nextk = k + cws
6944
+ if !range_interval[k].nil?
6945
+ if cws > 3
6946
+ prevint = true
6947
+ else
6948
+ prevint = false
6949
+ end
6950
+ range_interval[k] = nil
6951
+ nextk -= 1
6952
+ else
6953
+ prevint = false
6954
+ end
6955
+ }
6956
+ # output data
6957
+ w = ''
6958
+ range.each_with_index {|ws, k|
6959
+ if ws and ws.uniq.length == 1
6960
+ # interval mode is more compact
6961
+ w << ' ' + k.to_s + ' ' + (k + ws.length - 1).to_s + ' ' + ws[0].to_s
6962
+ elsif ws
6963
+ # range mode
6964
+ w << ' ' + k.to_s + ' [ ' + ws.join(' ') + ' ]'
6965
+ end
6966
+ }
6967
+ return ('/W [' + w + ' ]')
6968
+ end
6969
+
6449
6970
  #
6450
6971
  # Output fonts.
6972
+ # [@author Nicola Asuni]
6451
6973
  # [@access protected]
6452
6974
  #
6453
6975
  def putfonts()
@@ -6455,7 +6977,7 @@ protected
6455
6977
  @diffs.each do |diff|
6456
6978
  #Encodings
6457
6979
  newobj();
6458
- out('<</Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences [' + diff + ']>> endobj')
6980
+ out('<< /Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences [' + diff + '] >> endobj')
6459
6981
  end
6460
6982
  @font_files.each do |file, info|
6461
6983
  # search and get font file to embedd
@@ -6487,6 +7009,23 @@ protected
6487
7009
  # Strip second binary header
6488
7010
  font = font[0..info['length1']] + font[info['length1'] + 6]
6489
7011
  end
7012
+ elsif info['subset'] and (!compressed or (compressed and Object.const_defined?(:Zlib)))
7013
+
7014
+ if compressed
7015
+ # uncompress font
7016
+ font = Zlib::Inflate.inflate(font)
7017
+ end
7018
+ # merge subset characters
7019
+ subsetchars = [] # used chars
7020
+ info['fontkeys'].each {|fontkey|
7021
+ fontinfo = getFontBuffer(fontkey)
7022
+ subsetchars += fontinfo['subsetchars']
7023
+ }
7024
+ font = getTrueTypeFontSubset(font, subsetchars)
7025
+ if compressed
7026
+ # recompress font
7027
+ font = Zlib::Deflate.deflate(font)
7028
+ end
6490
7029
  end
6491
7030
  newobj()
6492
7031
  @font_files[file]['n'] = @n
@@ -6511,7 +7050,7 @@ protected
6511
7050
  type = font['type'];
6512
7051
  name = font['name'];
6513
7052
  if (type=='core')
6514
- #Standard font
7053
+ # standard core font
6515
7054
  obj_id = newobj()
6516
7055
  out = '<</Type /Font'
6517
7056
  out << ' /Subtype /Type1'
@@ -6526,10 +7065,8 @@ protected
6526
7065
  end
6527
7066
  out << ' >> endobj'
6528
7067
  out(out)
6529
- elsif type == 'Type0'
6530
- putType0(font)
6531
7068
  elsif (type=='Type1' || type=='TrueType')
6532
- #Additional Type1 or TrueType font
7069
+ # additional Type1 or TrueType font
6533
7070
  obj_id = newobj()
6534
7071
  out = '<</Type /Font'
6535
7072
  out << ' /Subtype /' + type
@@ -6569,7 +7106,7 @@ protected
6569
7106
  end
6570
7107
  out(s + '>> endobj')
6571
7108
  else
6572
- #Allow for additional types
7109
+ # additional types
6573
7110
  mtd='put' + type.downcase;
6574
7111
  unless self.respond_to?(mtd, true)
6575
7112
  Error('Unsupported font type: ' + type)
@@ -6581,145 +7118,6 @@ protected
6581
7118
  end
6582
7119
  end
6583
7120
 
6584
- #
6585
- # Outputs font widths
6586
- # [@parameter array :font] font data
6587
- # [@parameter int :cidoffset] offset for CID values
6588
- # [@return] PDF command string for font widths
6589
- # [@author] Nicola Asuni
6590
- # [@access protected]
6591
- # [@since 4.4.000 (2008-12-07)]
6592
- #
6593
- def putfontwidths(font, cidoffset=0)
6594
- font_cw = font['cw'].sort
6595
- rangeid = 0
6596
- range = []
6597
- prevcid = -2
6598
- prevwidth = -1
6599
- interval = false
6600
- range_interval = []
6601
- # for each character
6602
- font_cw.each {|cid, width|
6603
- cid -= cidoffset
6604
- if width != font['dw']
6605
- if cid == prevcid + 1
6606
- # consecutive CID
6607
- if width == prevwidth
6608
- if width == range[rangeid][0]
6609
- range[rangeid].push width
6610
- else
6611
- range[rangeid].pop
6612
- # new range
6613
- rangeid = prevcid
6614
- range[rangeid] = []
6615
- range[rangeid].push prevwidth
6616
- range[rangeid].push width
6617
- end
6618
- interval = true
6619
- range_interval[rangeid] = true
6620
- else
6621
- if interval
6622
- # new range
6623
- rangeid = cid
6624
- range[rangeid] = []
6625
- range[rangeid].push width
6626
- else
6627
- range[rangeid].push width
6628
- end
6629
- interval = false
6630
- end
6631
- else
6632
- # new range
6633
- rangeid = cid
6634
- range[rangeid] = []
6635
- range[rangeid].push width
6636
- interval = false
6637
- end
6638
- prevcid = cid
6639
- prevwidth = width
6640
- end
6641
- }
6642
- # optimize ranges
6643
- prevk = -1
6644
- nextk = -1
6645
- prevint = false
6646
- range.each_with_index {|ws, k|
6647
- cws = ws ? ws.length : 0
6648
- if (k == nextk) and !prevint and (range_interval[k].nil? or (cws < 4))
6649
- if !range_interval[k].nil?
6650
- range_interval[k] = nil
6651
- end
6652
- range[prevk] = range[prevk].concat(range[k]) if range[k]
6653
- range[k] = nil
6654
- else
6655
- prevk = k
6656
- end
6657
- nextk = k + cws
6658
- if !range_interval[k].nil?
6659
- if cws > 3
6660
- prevint = true
6661
- else
6662
- prevint = false
6663
- end
6664
- range_interval[k] = nil
6665
- nextk -= 1
6666
- else
6667
- prevint = false
6668
- end
6669
- }
6670
- # output data
6671
- w = ''
6672
- range.each_with_index {|ws, k|
6673
- if ws and ws.uniq.length == 1
6674
- # interval mode is more compact
6675
- w << ' ' + k.to_s + ' ' + (k + ws.length - 1).to_s + ' ' + ws[0].to_s
6676
- elsif ws
6677
- # range mode
6678
- w << ' ' + k.to_s + ' [ ' + ws.join(' ') + ' ]'
6679
- end
6680
- }
6681
- return ('/W [' + w + ' ]')
6682
- end
6683
-
6684
- def putType0(font)
6685
- # Type0
6686
- newobj()
6687
- out('<</Type /Font')
6688
- out('/Subtype /Type0')
6689
- out('/BaseFont /'+font['name']+'-'+font['cMap'])
6690
- out('/Encoding /'+font['cMap'])
6691
- out('/DescendantFonts ['+(@n+1).to_s+' 0 R]')
6692
- out('>>')
6693
- out('endobj')
6694
- # CIDFont
6695
- newobj()
6696
- out('<</Type /Font')
6697
- out('/Subtype /CIDFontType0')
6698
- out('/BaseFont /'+font['name'])
6699
- out('/CIDSystemInfo <</Registry (Adobe) /Ordering ('+font['registry']['ordering']+') /Supplement '+font['registry']['supplement'].to_s+'>>')
6700
- out('/FontDescriptor '+(@n+1).to_s+' 0 R')
6701
- w='/W [1 ['
6702
- font['cw'].keys.sort.each {|key|
6703
- w+=font['cw'][key].to_s + " "
6704
- }
6705
- out(w+'] 231 325 500 631 [500] 326 389 500]')
6706
- out('>>')
6707
- out('endobj')
6708
- # Font descriptor
6709
- newobj()
6710
- out('<</Type /FontDescriptor')
6711
- out('/FontName /'+font['name'])
6712
- out('/Flags 6')
6713
- out('/FontBBox [0 -200 1000 900]')
6714
- out('/ItalicAngle 0')
6715
- out('/Ascent 800')
6716
- out('/Descent -200')
6717
- out('/CapHeight 800')
6718
- out('/StemV 60')
6719
- out('>>')
6720
- out('endobj')
6721
- end
6722
-
6723
7121
  #
6724
7122
  # Adds unicode fonts.
6725
7123
  # Based on PDF Reference 1.3 (section 5)
@@ -6730,12 +7128,20 @@ protected
6730
7128
  # [@since 1.52.0.TC005 (2005-01-05)]
6731
7129
  #
6732
7130
  def puttruetypeunicode(font)
7131
+ fontname = ''
7132
+ if font['subset']
7133
+ # change name for font subsetting
7134
+ subtag = sprintf('%06u', font['i'])
7135
+ subtag = subtag.gsub('0123456789', 'ABCDEFGHIJ')
7136
+ fontname << subtag + '+'
7137
+ end
7138
+ fontname << font['name']
6733
7139
  # Type0 Font
6734
7140
  # A composite font composed of other fonts, organized hierarchically
6735
7141
  obj_id = newobj()
6736
7142
  out = '<</Type /Font'
6737
7143
  out << ' /Subtype /Type0'
6738
- out << ' /BaseFont /' + font['name'] + ''
7144
+ out << ' /BaseFont /' + fontname + ''
6739
7145
  out << ' /Name /F' + font['i'].to_s
6740
7146
  out << ' /Encoding /' + font['enc']
6741
7147
  out << ' /ToUnicode /Identity-H'
@@ -6749,7 +7155,7 @@ protected
6749
7155
  newobj();
6750
7156
  out = '<</Type /Font'
6751
7157
  out << ' /Subtype /CIDFontType2'
6752
- out << ' /BaseFont /' + font['name']
7158
+ out << ' /BaseFont /' + fontname
6753
7159
 
6754
7160
  # A dictionary containing entries that define the character collection of the CIDFont.
6755
7161
 
@@ -6761,14 +7167,15 @@ protected
6761
7167
  out << ' /FontDescriptor ' + (@n + 1).to_s + ' 0 R'
6762
7168
  out << ' /DW ' + font['dw'].to_s + '' # default width
6763
7169
  out << "\n" + putfontwidths(font, 0)
6764
- out << ' /CIDToGIDMap ' + (@n + 2).to_s + ' 0 R >> endobj'
7170
+ out << ' /CIDToGIDMap ' + (@n + 2).to_s + ' 0 R'
7171
+ out << ' >> endobj'
6765
7172
  out(out)
6766
7173
 
6767
7174
  # Font descriptor
6768
7175
  # A font descriptor describing the CIDFont default metrics other than its glyph widths
6769
7176
  newobj();
6770
7177
  out = '<</Type /FontDescriptor'
6771
- out << ' /FontName /' + font['name']
7178
+ out << ' /FontName /' + fontname
6772
7179
  font['desc'].each do |key, value|
6773
7180
  if value.is_a? Float
6774
7181
  value = sprintf('%.3f', value)
@@ -6949,6 +7356,17 @@ protected
6949
7356
  end
6950
7357
  end
6951
7358
 
7359
+ #
7360
+ # Maps HTML anchor links to anchors.
7361
+ # [@access protected]
7362
+ #
7363
+ def mapLinksToHtmlAnchors
7364
+ @html_anchors.each do |anchor, positon|
7365
+ links = @html_anchor_links.find_all { |url, link_anchor| link_anchor == anchor}
7366
+ links.each {|url, link_anchor| @links[url] = positon }
7367
+ end
7368
+ end
7369
+
6952
7370
  #
6953
7371
  # Output Spot Colors Resources.
6954
7372
  # [@access protected[
@@ -7037,11 +7455,13 @@ protected
7037
7455
  # [@access protected]
7038
7456
  #
7039
7457
  def putresources()
7458
+ mapLinksToHtmlAnchors()
7040
7459
  putextgstates()
7041
7460
  putocg()
7042
7461
  putfonts();
7043
7462
  putimages();
7044
7463
  putspotcolors()
7464
+
7045
7465
  #Resource dictionary
7046
7466
  @offsets[2]=@bufferlen
7047
7467
  putresourcedict();
@@ -7461,7 +7881,7 @@ protected
7461
7881
  end
7462
7882
 
7463
7883
  #
7464
- # Read a 4-byte integer from file
7884
+ # Read a 4-byte (32 bit) integer from file
7465
7885
  # [@param string :f] file name.
7466
7886
  # [@return] 4-byte integer
7467
7887
  # [@access protected]
@@ -7665,6 +8085,7 @@ protected
7665
8085
  return strarr
7666
8086
  end
7667
8087
 
8088
+ unichar = -1 # last unicode char
7668
8089
  unicode = [] # array containing unicode values
7669
8090
  bytes = [] # array containing single character byte sequences
7670
8091
  numbytes = 1; # number of octetc needed to represent the UTF-8 character
@@ -7674,7 +8095,7 @@ protected
7674
8095
  str.each_byte do |char|
7675
8096
  if (bytes.length == 0) # get starting octect
7676
8097
  if (char <= 0x7F)
7677
- unicode << char # use the character "as is" because is ASCII
8098
+ unichar = char # use the character "as is" because is ASCII
7678
8099
  numbytes = 1
7679
8100
  elsif ((char >> 0x05) == 0x06) # 2 bytes character (0x06 = 110 BIN)
7680
8101
  bytes << ((char - 0xC0) << 0x06)
@@ -7687,7 +8108,7 @@ protected
7687
8108
  numbytes = 4
7688
8109
  else
7689
8110
  # use replacement character for other invalid sequences
7690
- unicode << 0xFFFD
8111
+ unichar = 0xFFFD
7691
8112
  bytes = []
7692
8113
  numbytes = 1
7693
8114
  end
@@ -7704,9 +8125,9 @@ protected
7704
8125
  # U+D800 and U+DFFF, which are reserved for use with the UTF-16
7705
8126
  # encoding form (as surrogate pairs) and do not directly represent
7706
8127
  # characters
7707
- unicode << 0xFFFD; # use replacement character
8128
+ unichar = 0xFFFD # use replacement character
7708
8129
  else
7709
- unicode << char # add char to array
8130
+ unichar = char # add char to array
7710
8131
  end
7711
8132
  # reset data for next char
7712
8133
  bytes = []
@@ -7714,11 +8135,20 @@ protected
7714
8135
  end
7715
8136
  else
7716
8137
  # use replacement character for other invalid sequences
7717
- unicode << 0xFFFD;
8138
+ unichar = 0xFFFD
7718
8139
  bytes = []
7719
8140
  numbytes = 1;
7720
8141
  end
8142
+ if unichar >= 0
8143
+ # insert unicode value into array
8144
+ unicode.push unichar
8145
+ # store this char for font subsetting
8146
+ @current_font['subsetchars'][unichar] = true
8147
+ unichar = -1
8148
+ end
7721
8149
  end
8150
+ # update font subsetchars
8151
+ setFontSubBuffer(@current_font['fontkey'], 'subsetchars', @current_font['subsetchars'])
7722
8152
  # insert new value on cache
7723
8153
  @cache_utf8_string_to_array[str] = unicode.dup
7724
8154
  return unicode;
@@ -7864,6 +8294,26 @@ protected
7864
8294
  # ====================================================
7865
8295
  public
7866
8296
 
8297
+ #
8298
+ # Set Font Subsetting.
8299
+ # [@param boolean :subset] subset of the font default setting.
8300
+ # [@access public]
8301
+ #
8302
+ def setFontSubsetting(subset)
8303
+ @font_subsetting = (subset == true ? true : false)
8304
+ end
8305
+ alias_method :set_font_subsetting, :setFontSubsetting
8306
+
8307
+ #
8308
+ # Get Font Subsetting.
8309
+ # [@return boolean]
8310
+ # [@access public]
8311
+ #
8312
+ def getFontSubsetting()
8313
+ return @font_subsetting
8314
+ end
8315
+ alias_method :get_font_subsetting, :getFontSubsetting
8316
+
7867
8317
  #
7868
8318
  # Set header font.
7869
8319
  # [@param array :font] font
@@ -7970,8 +8420,10 @@ public
7970
8420
  def addHtmlLink(url, name, fill=0, firstline=false, color='', style=-1, firstblock=false)
7971
8421
  if !empty_string(url) and (url[0, 1] == '#')
7972
8422
  # convert url to internal link
7973
- page = url.sub(/^#/, "").to_i
8423
+ anchor = url.sub(/^#/, "")
8424
+ page = anchor.to_i
7974
8425
  url = AddLink()
8426
+ @html_anchor_links[url] = anchor
7975
8427
  SetLink(url, 0, page)
7976
8428
  end
7977
8429
  # store current settings
@@ -7995,6 +8447,27 @@ public
7995
8447
  end
7996
8448
  alias_method :add_html_link, :addHtmlLink
7997
8449
 
8450
+ #
8451
+ # Adds HTML anchor and remembers it's position
8452
+ # [@param string :anchor] html anchor id
8453
+ # [@access public]
8454
+ #
8455
+ def addHtmlAnchor(anchor)
8456
+ @html_anchors[anchor] = [@page, @y]
8457
+ end
8458
+ alias_method :add_html_anchor, :addHtmlAnchor
8459
+
8460
+ #
8461
+ # Outputs HTML anchor position
8462
+ # [@param string :anchor] html anchor id
8463
+ # [@return array] [Page, Y] of anchor
8464
+ # [@access public]
8465
+ #
8466
+ def getHtmlAnchorPosition(anchor)
8467
+ @html_anchors[anchor]
8468
+ end
8469
+ alias_method :get_html_anchor_position, :getHtmlAnchorPosition
8470
+
7998
8471
  #
7999
8472
  # Returns an associative array (keys: R,G,B) from an html color name or a six-digit or three-digit hexadecimal color representation (i.e. #3FE5AA or #7FF).
8000
8473
  # [@param string :color] html color
@@ -9476,7 +9949,7 @@ public
9476
9949
  if arabic
9477
9950
  endedletter = [1569,1570,1571,1572,1573,1575,1577,1583,1584,1585,1586,1608,1688]
9478
9951
  alfletter = [1570,1571,1573,1575]
9479
- chardata2 = chardata
9952
+ chardata2 = chardata.dup
9480
9953
  laaletter = false
9481
9954
  charAL = []
9482
9955
  x = 0
@@ -9492,7 +9965,7 @@ public
9492
9965
  numAL = x
9493
9966
  reg_AL_NSM = /^(AL|NSM)$/
9494
9967
  numchars.times do |i|
9495
- thischar = chardata[i]
9968
+ thischar = chardata[i].dup
9496
9969
  if i > 0
9497
9970
  prevchar = chardata[i-1]
9498
9971
  else
@@ -9653,8 +10126,12 @@ public
9653
10126
  ordarray = []
9654
10127
  numchars.times do |i|
9655
10128
  ordarray.push chardata[i][:char]
10129
+ # store char values for subsetting
10130
+ @current_font['subsetchars'][chardata[i][:char]] = true
9656
10131
  end
9657
10132
 
10133
+ # update font subsetchars
10134
+ setFontSubBuffer(@current_font['fontkey'], 'subsetchars', @current_font['subsetchars'])
9658
10135
  return ordarray
9659
10136
  end
9660
10137
  protected :utf8Bidi
@@ -10774,7 +11251,8 @@ protected
10774
11251
  # create an array of elements
10775
11252
  dom = []
10776
11253
  dom[key] = {}
10777
- # set first void element
11254
+ # set inheritable properties fot the first void element
11255
+ # possible inheritable properties are: azimuth, border-collapse, border-spacing, caption-side, color, cursor, direction, empty-cells, font, font-family, font-stretch, font-size, font-size-adjust, font-style, font-variant, font-weight, letter-spacing, line-height, list-style, list-style-image, list-style-position, list-style-type, orphans, page, page-break-inside, quotes, speak, speak-header, text-align, text-indent, text-transform, volume, white-space, widows, word-spacing
10778
11256
  dom[key]['tag'] = false
10779
11257
  dom[key]['block'] = false
10780
11258
  dom[key]['value'] = ''
@@ -10787,7 +11265,7 @@ protected
10787
11265
  dom[key]['clip'] = (@textrendermode > 3)
10788
11266
  dom[key]['line-height'] = @cell_height_ratio
10789
11267
  dom[key]['bgcolor'] = ActiveSupport::OrderedHash.new
10790
- dom[key]['fgcolor'] = @fgcolor.dup
11268
+ dom[key]['fgcolor'] = @fgcolor.dup # color
10791
11269
  dom[key]['strokecolor'] = @strokecolor.dup
10792
11270
 
10793
11271
  dom[key]['align'] = ''
@@ -11328,7 +11806,7 @@ public
11328
11806
  # Escape '<' character for not tag case.
11329
11807
  html = html.gsub(%r{(<+)([^/a-zA-Z])}){CGI.escapeHTML($1) + $2}.gsub(%r{</([^a-zA-Z])}){'&lt;/' + $1}
11330
11808
 
11331
- html = "%s" % sanitize(html, :tags=> %w(a b blockquote body br dd del div dl dt em font h1 h2 h3 h4 h5 h6 hr i img li ol p pre small span strong sub sup table td th thead tr tt u ins ul), :attributes => %w(cellspacing cellpadding bgcolor color value width height src size colspan rowspan style align border face href dir class id nobr stroke strokecolor fill nested tablehead))
11809
+ html = "%s" % sanitize(html, :tags=> %w(a b blockquote body br dd del div dl dt em font h1 h2 h3 h4 h5 h6 hr i img li ol p pre small span strong sub sup table td th thead tr tt u ins ul), :attributes => %w(cellspacing cellpadding bgcolor color value width height src size colspan rowspan style align border face href name dir class id nobr stroke strokecolor fill nested tablehead))
11332
11810
  end
11333
11811
  protected :sanitize_html
11334
11812
 
@@ -12055,6 +12533,7 @@ public
12055
12533
  opentagpos = nil
12056
12534
  end
12057
12535
  if dom[key]['tag']
12536
+ addHtmlAnchor(@html_anchor) if @html_anchor
12058
12537
  if dom[key]['opening']
12059
12538
  # get text indentation (if any)
12060
12539
  if dom[key]['text-indent'] and dom[key]['block']
@@ -12625,6 +13104,8 @@ public
12625
13104
  when 'a'
12626
13105
  if tag['attribute'].key?('href')
12627
13106
  @href['url'] = get_sever_url(tag['attribute']['href'])
13107
+ elsif tag['attribute'].key?('id') || tag['attribute'].key?('name')
13108
+ @html_anchor = tag['attribute']['id'] || tag['attribute']['name']
12628
13109
  end
12629
13110
  when 'img'
12630
13111
  if !tag['attribute']['src'].nil?
@@ -13136,6 +13617,7 @@ public
13136
13617
  end
13137
13618
  when 'a'
13138
13619
  @href = {}
13620
+ @html_anchor = nil
13139
13621
  when 'sup'
13140
13622
  SetXY(GetX(), GetY() + (0.7 * parent['fontsize'] / @k))
13141
13623
  when 'sub'
@@ -13836,6 +14318,17 @@ protected
13836
14318
  end
13837
14319
  end
13838
14320
 
14321
+
14322
+ #
14323
+ # Reset all links that points to pages after current
14324
+ # [@access protected]
14325
+ #
14326
+ def resetLinksAfterCurrentPage()
14327
+ @links.each do |link|
14328
+ link[0] = @page if link.present? && link[0] > @page
14329
+ end
14330
+ end
14331
+
13839
14332
  #
13840
14333
  # Get font buffer content.
13841
14334
  # [@param string :font] font key