prawn 1.0.0.rc1 → 1.0.0.rc2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (203) hide show
  1. data/Gemfile +18 -0
  2. data/README.md +5 -3
  3. data/Rakefile +8 -14
  4. data/data/pdfs/nested_pages.pdf +13 -13
  5. data/lib/prawn.rb +3 -1
  6. data/lib/prawn/compatibility.rb +46 -10
  7. data/lib/prawn/core.rb +3 -1
  8. data/lib/prawn/core/document_state.rb +2 -1
  9. data/lib/prawn/core/object_store.rb +61 -5
  10. data/lib/prawn/core/page.rb +3 -6
  11. data/lib/prawn/core/pdf_object.rb +21 -4
  12. data/lib/prawn/core/reference.rb +6 -2
  13. data/lib/prawn/core/text.rb +4 -4
  14. data/lib/prawn/core/text/formatted/line_wrap.rb +23 -8
  15. data/lib/prawn/document.rb +21 -15
  16. data/lib/prawn/document/bounding_box.rb +3 -3
  17. data/lib/prawn/document/column_box.rb +22 -4
  18. data/lib/prawn/document/snapshot.rb +1 -1
  19. data/lib/prawn/encoding.rb +1 -1
  20. data/lib/prawn/errors.rb +4 -0
  21. data/lib/prawn/font.rb +1 -1
  22. data/lib/prawn/font/afm.rb +30 -72
  23. data/lib/prawn/font/ttf.rb +6 -33
  24. data/lib/prawn/graphics.rb +148 -23
  25. data/lib/prawn/graphics/color.rb +8 -1
  26. data/lib/prawn/graphics/patterns.rb +137 -0
  27. data/lib/prawn/images.rb +25 -19
  28. data/lib/prawn/images/jpg.rb +4 -4
  29. data/lib/prawn/images/png.rb +18 -12
  30. data/lib/prawn/security.rb +6 -4
  31. data/lib/prawn/soft_mask.rb +94 -0
  32. data/lib/prawn/table.rb +136 -31
  33. data/lib/prawn/table/cell.rb +260 -29
  34. data/lib/prawn/table/cell/span_dummy.rb +88 -0
  35. data/lib/prawn/table/cell/text.rb +36 -14
  36. data/lib/prawn/table/cells.rb +91 -41
  37. data/lib/prawn/text.rb +3 -2
  38. data/lib/prawn/text/formatted/box.rb +14 -5
  39. data/lib/prawn/text/formatted/fragment.rb +33 -22
  40. data/lib/prawn/text/formatted/parser.rb +5 -2
  41. data/lib/prawn/utilities.rb +44 -0
  42. data/manual/basic_concepts/adding_pages.rb +27 -0
  43. data/manual/basic_concepts/basic_concepts.rb +34 -0
  44. data/manual/basic_concepts/creation.rb +39 -0
  45. data/manual/basic_concepts/cursor.rb +33 -0
  46. data/manual/basic_concepts/measurement.rb +25 -0
  47. data/manual/basic_concepts/origin.rb +38 -0
  48. data/manual/basic_concepts/other_cursor_helpers.rb +40 -0
  49. data/manual/bounding_box/bounding_box.rb +39 -0
  50. data/manual/bounding_box/bounds.rb +49 -0
  51. data/manual/bounding_box/canvas.rb +24 -0
  52. data/manual/bounding_box/creation.rb +23 -0
  53. data/manual/bounding_box/indentation.rb +46 -0
  54. data/manual/bounding_box/nesting.rb +45 -0
  55. data/manual/bounding_box/russian_boxes.rb +40 -0
  56. data/manual/bounding_box/stretchy.rb +31 -0
  57. data/manual/document_and_page_options/background.rb +27 -0
  58. data/manual/document_and_page_options/document_and_page_options.rb +31 -0
  59. data/manual/document_and_page_options/metadata.rb +23 -0
  60. data/manual/document_and_page_options/page_margins.rb +38 -0
  61. data/manual/document_and_page_options/page_size.rb +34 -0
  62. data/manual/example_file.rb +116 -0
  63. data/manual/example_helper.rb +430 -0
  64. data/manual/example_package.rb +53 -0
  65. data/manual/example_section.rb +46 -0
  66. data/manual/graphics/circle_and_ellipse.rb +22 -0
  67. data/manual/graphics/color.rb +24 -0
  68. data/manual/graphics/common_lines.rb +28 -0
  69. data/manual/graphics/fill_and_stroke.rb +42 -0
  70. data/manual/graphics/fill_rules.rb +37 -0
  71. data/manual/graphics/gradients.rb +37 -0
  72. data/manual/graphics/graphics.rb +58 -0
  73. data/manual/graphics/helper.rb +17 -0
  74. data/manual/graphics/line_width.rb +35 -0
  75. data/manual/graphics/lines_and_curves.rb +41 -0
  76. data/manual/graphics/polygon.rb +29 -0
  77. data/manual/graphics/rectangle.rb +21 -0
  78. data/manual/graphics/rotate.rb +28 -0
  79. data/manual/graphics/scale.rb +41 -0
  80. data/manual/graphics/soft_masks.rb +46 -0
  81. data/manual/graphics/stroke_cap.rb +31 -0
  82. data/manual/graphics/stroke_dash.rb +43 -0
  83. data/manual/graphics/stroke_join.rb +30 -0
  84. data/manual/graphics/translate.rb +29 -0
  85. data/manual/graphics/transparency.rb +35 -0
  86. data/manual/images/absolute_position.rb +23 -0
  87. data/manual/images/fit.rb +21 -0
  88. data/manual/images/horizontal.rb +25 -0
  89. data/manual/images/images.rb +40 -0
  90. data/manual/images/plain_image.rb +18 -0
  91. data/manual/images/scale.rb +22 -0
  92. data/manual/images/vertical.rb +28 -0
  93. data/manual/images/width_and_height.rb +25 -0
  94. data/manual/layout/boxes.rb +27 -0
  95. data/manual/layout/content.rb +25 -0
  96. data/manual/layout/layout.rb +28 -0
  97. data/manual/layout/simple_grid.rb +23 -0
  98. data/manual/manual/cover.rb +26 -0
  99. data/manual/manual/foreword.rb +13 -0
  100. data/manual/manual/how_to_read_this_manual.rb +41 -0
  101. data/manual/manual/manual.rb +36 -0
  102. data/manual/outline/add_subsection_to.rb +61 -0
  103. data/manual/outline/insert_section_after.rb +47 -0
  104. data/manual/outline/outline.rb +32 -0
  105. data/manual/outline/sections_and_pages.rb +67 -0
  106. data/manual/repeatable_content/page_numbering.rb +54 -0
  107. data/manual/repeatable_content/repeatable_content.rb +31 -0
  108. data/manual/repeatable_content/repeater.rb +55 -0
  109. data/manual/repeatable_content/stamp.rb +41 -0
  110. data/manual/security/encryption.rb +31 -0
  111. data/manual/security/permissions.rb +38 -0
  112. data/manual/security/security.rb +28 -0
  113. data/manual/syntax_highlight.rb +52 -0
  114. data/manual/table/basic_block.rb +53 -0
  115. data/manual/table/before_rendering_page.rb +26 -0
  116. data/manual/table/cell_border_lines.rb +24 -0
  117. data/manual/table/cell_borders_and_bg.rb +31 -0
  118. data/manual/table/cell_dimensions.rb +30 -0
  119. data/manual/table/cell_text.rb +38 -0
  120. data/manual/table/column_widths.rb +30 -0
  121. data/manual/table/content_and_subtables.rb +39 -0
  122. data/manual/table/creation.rb +27 -0
  123. data/manual/table/filtering.rb +36 -0
  124. data/manual/table/flow_and_header.rb +17 -0
  125. data/manual/table/image_cells.rb +33 -0
  126. data/manual/table/position.rb +29 -0
  127. data/manual/table/row_colors.rb +20 -0
  128. data/manual/table/span.rb +30 -0
  129. data/manual/table/style.rb +22 -0
  130. data/manual/table/table.rb +52 -0
  131. data/manual/table/width.rb +27 -0
  132. data/manual/templates/full_template.rb +23 -0
  133. data/manual/templates/page_template.rb +47 -0
  134. data/manual/templates/templates.rb +26 -0
  135. data/manual/text/alignment.rb +44 -0
  136. data/manual/text/color.rb +24 -0
  137. data/manual/text/column_box.rb +32 -0
  138. data/manual/text/fallback_fonts.rb +37 -0
  139. data/manual/text/font.rb +41 -0
  140. data/manual/text/font_size.rb +45 -0
  141. data/manual/text/font_style.rb +23 -0
  142. data/manual/text/formatted_callbacks.rb +60 -0
  143. data/manual/text/formatted_text.rb +50 -0
  144. data/manual/text/free_flowing_text.rb +51 -0
  145. data/manual/text/group.rb +29 -0
  146. data/manual/text/inline.rb +43 -0
  147. data/manual/text/kerning_and_character_spacing.rb +39 -0
  148. data/manual/text/leading.rb +25 -0
  149. data/manual/text/line_wrapping.rb +41 -0
  150. data/manual/text/paragraph_indentation.rb +26 -0
  151. data/manual/text/positioned_text.rb +38 -0
  152. data/manual/text/registering_families.rb +48 -0
  153. data/manual/text/rendering_and_color.rb +37 -0
  154. data/manual/text/right_to_left_text.rb +43 -0
  155. data/manual/text/rotation.rb +43 -0
  156. data/manual/text/single_usage.rb +37 -0
  157. data/manual/text/text.rb +75 -0
  158. data/manual/text/text_box_excess.rb +32 -0
  159. data/manual/text/text_box_extensions.rb +45 -0
  160. data/manual/text/text_box_overflow.rb +44 -0
  161. data/manual/text/utf8.rb +28 -0
  162. data/manual/text/win_ansi_charset.rb +59 -0
  163. data/prawn.gemspec +10 -7
  164. data/spec/bounding_box_spec.rb +107 -17
  165. data/spec/cell_spec.rb +66 -40
  166. data/spec/column_box_spec.rb +33 -0
  167. data/spec/document_spec.rb +45 -24
  168. data/spec/extensions/encoding_helpers.rb +6 -0
  169. data/spec/extensions/mocha.rb +1 -0
  170. data/spec/font_spec.rb +71 -53
  171. data/spec/formatted_text_arranger_spec.rb +19 -19
  172. data/spec/formatted_text_box_spec.rb +16 -16
  173. data/spec/formatted_text_fragment_spec.rb +6 -6
  174. data/spec/graphics_spec.rb +96 -31
  175. data/spec/grid_spec.rb +2 -2
  176. data/spec/images_spec.rb +18 -10
  177. data/spec/jpg_spec.rb +1 -1
  178. data/spec/line_wrap_spec.rb +14 -14
  179. data/spec/measurement_units_spec.rb +2 -2
  180. data/spec/name_tree_spec.rb +6 -6
  181. data/spec/object_store_spec.rb +17 -17
  182. data/spec/outline_spec.rb +35 -17
  183. data/spec/pdf_object_spec.rb +3 -1
  184. data/spec/png_spec.rb +22 -19
  185. data/spec/reference_spec.rb +24 -1
  186. data/spec/repeater_spec.rb +9 -9
  187. data/spec/security_spec.rb +3 -3
  188. data/spec/snapshot_spec.rb +3 -3
  189. data/spec/soft_mask_spec.rb +117 -0
  190. data/spec/span_spec.rb +4 -4
  191. data/spec/spec_helper.rb +12 -6
  192. data/spec/stamp_spec.rb +12 -12
  193. data/spec/stroke_styles_spec.rb +5 -5
  194. data/spec/table_spec.rb +458 -88
  195. data/spec/template_spec.rb +108 -54
  196. data/spec/text_at_spec.rb +17 -17
  197. data/spec/text_box_spec.rb +76 -45
  198. data/spec/text_rendering_mode_spec.rb +5 -5
  199. data/spec/text_spacing_spec.rb +4 -4
  200. data/spec/text_spec.rb +44 -40
  201. metadata +419 -250
  202. data/lib/prawn/graphics/gradient.rb +0 -84
  203. data/lib/prawn/security/arcfour.rb +0 -51
data/Gemfile ADDED
@@ -0,0 +1,18 @@
1
+ source :rubygems
2
+
3
+ gem "ttfunk", "~>1.0.3"
4
+ gem "pdf-reader", "~> 1.2"
5
+ gem "ruby-rc4"
6
+ gem "afm"
7
+
8
+ group :development do
9
+ gem "coderay", "~> 1.0.7"
10
+ gem "rdoc"
11
+ end
12
+
13
+ group :test do
14
+ gem "pdf-inspector", "~> 1.0.2", :require => "pdf/inspector"
15
+ gem "rspec"
16
+ gem "mocha", :require => false
17
+ gem "rake"
18
+ end
data/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # Prawn: Fast, Nimble PDF Generation For Ruby
2
2
 
3
+ [![Build Status](https://secure.travis-ci.org/prawnpdf/prawn.png)](http://travis-ci.org/prawnpdf/prawn)
4
+
3
5
  Prawn is a pure Ruby PDF generation library that provides a lot of great functionality while trying to remain simple and reasonably performant. Here are some of the important features we provide:
4
6
 
5
7
  * Vector drawing support, including lines, polygons, curves, ellipses, etc.
@@ -33,7 +35,7 @@ You can also install from git if you'd like, the _master_ branch contains the la
33
35
 
34
36
  ## Release Policies
35
37
 
36
- We may introduce backwards incompatible changes each time our minor version number is bumped, but that any tiny version number bump should be bug fixes and internal changes only. Be sure to read the release notes each time we cut a new release and lock your gems accordingly. You can find the project CHANGELOG at: https://github.com/sandal/prawn/wiki/CHANGELOG
38
+ We may introduce backwards incompatible changes each time our minor version number is bumped, but that any tiny version number bump should be bug fixes and internal changes only. Be sure to read the release notes each time we cut a new release and lock your gems accordingly. You can find the project CHANGELOG at: https://github.com/prawnpdf/prawn/wiki/CHANGELOG
37
39
 
38
40
  ## Hello World!
39
41
 
@@ -73,7 +75,7 @@ Please make your posts to the list as specific as possible, including code sampl
73
75
 
74
76
  If you've found a bug, want to submit a patch, or have a feature request, please enter a ticket into our github tracker:
75
77
 
76
- <http://github.com/sandal/prawn/issues>
78
+ <http://github.com/prawnpdf/prawn/issues>
77
79
 
78
80
  We strongly encourage bug reports to come with failing tests or at least a reduced example that demonstrates the problem. Similarly, patches should include tests, API documentation, and an update to the manual where relevant. Feel free to send a pull request early though, if you just want some feedback or a code review before preparing your code to be merged.
79
81
 
@@ -87,7 +89,7 @@ Over the last several years, we've received code contributions from over 50 peop
87
89
 
88
90
  While he was only with us for a short time before moving on to other things, we'd also like to thank Prawn core team emeritus Jamis Buck for his contributions. He was responsible for introducing font subsetting as well as the first implementation of our inline formatting support.
89
91
 
90
- You can find the full list of folks who have at least one patch accepted to Prawn on github at https://github.com/sandal/prawn/contributors
92
+ You can find the full list of folks who have at least one patch accepted to Prawn on github at https://github.com/prawnpdf/prawn/contributors
91
93
 
92
94
  ## License
93
95
 
data/Rakefile CHANGED
@@ -1,21 +1,15 @@
1
- require "rubygems"
2
1
  require "bundler"
3
2
  Bundler.setup
4
3
 
5
4
  require 'rake'
6
- require 'rake/testtask'
7
- require "rake/rdoctask"
8
- require "rake/gempackagetask"
5
+ require 'rspec/core/rake_task'
6
+ require 'rdoc/task'
7
+ require 'rubygems/package_task'
9
8
 
10
- task :default => [:test]
9
+ task :default => [:spec]
11
10
 
12
- desc "Run all tests, test-spec, mocha, and pdf-reader required"
13
- Rake::TestTask.new do |test|
14
- # test.ruby_opts << "-w" # .should == true triggers a lot of warnings
15
- test.libs << "spec"
16
- test.test_files = Dir[ "spec/*_spec.rb" ]
17
- test.verbose = true
18
- end
11
+ desc "Run all rspec files"
12
+ RSpec::Core::RakeTask.new("spec")
19
13
 
20
14
  desc "Show library's code statistics"
21
15
  task :stats do
@@ -26,7 +20,7 @@ task :stats do
26
20
  end
27
21
 
28
22
  desc "genrates documentation"
29
- Rake::RDocTask.new do |rdoc|
23
+ RDoc::Task.new do |rdoc|
30
24
  rdoc.rdoc_files.include( "README",
31
25
  "COPYING",
32
26
  "LICENSE",
@@ -45,7 +39,7 @@ task :manual do
45
39
  end
46
40
 
47
41
  spec = Gem::Specification.load "prawn.gemspec"
48
- Rake::GemPackageTask.new(spec) do |pkg|
42
+ Gem::PackageTask.new(spec) do |pkg|
49
43
  pkg.need_zip = true
50
44
  pkg.need_tar = true
51
45
  end
@@ -49,7 +49,7 @@ endobj
49
49
  >>
50
50
  /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]
51
51
  >>
52
- /Parent 4 0 R
52
+ /Parent 3 0 R
53
53
  >>
54
54
  endobj
55
55
  7 0 obj
@@ -85,7 +85,7 @@ endobj
85
85
  >>
86
86
  /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]
87
87
  >>
88
- /Parent 4 0 R
88
+ /Parent 3 0 R
89
89
  >>
90
90
  endobj
91
91
  10 0 obj
@@ -97,17 +97,17 @@ endobj
97
97
  endobj
98
98
  xref
99
99
  0 11
100
- 0000000000 65535 f
101
- 0000000015 00000 n
102
- 0000000071 00000 n
103
- 0000000120 00000 n
104
- 0000000183 00000 n
105
- 0000000240 00000 n
106
- 0000000494 00000 n
107
- 0000000672 00000 n
108
- 0000000769 00000 n
109
- 0000000994 00000 n
110
- 0000001173 00000 n
100
+ 0000000000 65535 f
101
+ 0000000015 00000 n
102
+ 0000000071 00000 n
103
+ 0000000120 00000 n
104
+ 0000000183 00000 n
105
+ 0000000240 00000 n
106
+ 0000000494 00000 n
107
+ 0000000672 00000 n
108
+ 0000000769 00000 n
109
+ 0000000994 00000 n
110
+ 0000001173 00000 n
111
111
  trailer
112
112
  << /Root 2 0 R
113
113
  /Size 11
@@ -5,9 +5,10 @@
5
5
  # into the lib/prawn/core/* source tree.
6
6
  #
7
7
  module Prawn #:nodoc:
8
- VERSION = "1.0.0.rc1"
8
+ VERSION = "1.0.0.rc2"
9
9
  end
10
10
 
11
+ require "prawn/utilities"
11
12
  require "prawn/core"
12
13
  require "prawn/text"
13
14
  require "prawn/graphics"
@@ -16,6 +17,7 @@ require "prawn/images/image"
16
17
  require "prawn/images/jpg"
17
18
  require "prawn/images/png"
18
19
  require "prawn/stamp"
20
+ require "prawn/soft_mask"
19
21
  require "prawn/security"
20
22
  require "prawn/document"
21
23
  require "prawn/font"
@@ -1,24 +1,60 @@
1
1
  # coding: utf-8
2
2
  #
3
- # Why would we ever use Ruby 1.8.7 when we can backport with something
4
- # as simple as this?
5
- #
3
+ # Compatibility layer to smooth over differences between Ruby implementations
4
+ # The oldest version of Ruby which is supported is MRI 1.8.7
5
+ # Ideally, all version-specific or implementation-specific code should be
6
+ # kept in this file (but that ideal has not been attained yet)
7
+
6
8
  class String #:nodoc:
7
9
  def first_line
8
10
  self.each_line { |line| return line }
9
11
  end
10
- unless "".respond_to?(:lines)
11
- alias_method :lines, :to_a
12
+
13
+ unless "".respond_to?(:codepoints)
14
+ def codepoints(&block)
15
+ if block_given?
16
+ unpack("U*").each(&block)
17
+ else
18
+ unpack("U*")
19
+ end
20
+ end
12
21
  end
13
- unless "".respond_to?(:each_char)
14
- def each_char #:nodoc:
15
- # copied from jcode
22
+
23
+ if "".respond_to?(:encode)
24
+ def normalize_to_utf8
25
+ begin
26
+ encode(Encoding::UTF_8)
27
+ rescue
28
+ raise Prawn::Errors::IncompatibleStringEncoding, "Encoding " +
29
+ "#{text.encoding} can not be transparently converted to UTF-8. " +
30
+ "Please ensure the encoding of the string you are attempting " +
31
+ "to use is set correctly"
32
+ end
33
+ end
34
+ alias :unicode_characters :each_char
35
+ alias :unicode_length :length
36
+
37
+ else
38
+ def normalize_to_utf8
39
+ begin
40
+ # use unpack as a hackish way to verify the string is valid utf-8
41
+ unpack("U*")
42
+ return dup
43
+ rescue
44
+ raise Prawn::Errors::IncompatibleStringEncoding, "The string you " +
45
+ "are attempting to render is not encoded in valid UTF-8."
46
+ end
47
+ end
48
+ def unicode_characters
16
49
  if block_given?
17
- scan(/./m) { |x| yield x }
50
+ unpack("U*").each { |c| yield [c].pack("U") }
18
51
  else
19
- scan(/./m)
52
+ unpack("U*").map { |c| [c].pack("U") }
20
53
  end
21
54
  end
55
+ def unicode_length
56
+ unpack("U*").length
57
+ end
22
58
  end
23
59
  end
24
60
 
@@ -29,7 +29,9 @@ module Prawn
29
29
 
30
30
  # The base source directory for Prawn as installed on the system
31
31
  #
32
- BASEDIR = File.expand_path(File.join(dir, '..', '..'))
32
+ #
33
+ BASEDIR = File.expand_path(File.join(dir, '..','..'))
34
+ DATADIR = File.expand_path(File.join(dir, '..', '..', 'data'))
33
35
 
34
36
  # Whe set to true, Prawn will verify hash options to ensure only valid keys
35
37
  # are used. Off by default.
@@ -6,6 +6,7 @@ module Prawn
6
6
 
7
7
  if options[:template]
8
8
  @store = Prawn::Core::ObjectStore.new(:template => options[:template])
9
+ @store.info.data.merge!(options[:info]) if options[:info]
9
10
  else
10
11
  @store = Prawn::Core::ObjectStore.new(:info => options[:info])
11
12
  end
@@ -41,7 +42,7 @@ module Prawn
41
42
  def normalize_metadata(options)
42
43
  options[:info] ||= {}
43
44
  options[:info][:Creator] ||= "Prawn"
44
- options[:info][:Producer] = "Prawn"
45
+ options[:info][:Producer] ||= "Prawn"
45
46
 
46
47
  info = options[:info]
47
48
  end
@@ -141,16 +141,32 @@ module Prawn
141
141
  # Imports nothing and returns nil if the requested page number doesn't
142
142
  # exist. page_num is 1 indexed, so 1 indicates the first page.
143
143
  #
144
- def import_page(filename, page_num)
144
+ def import_page(input, page_num)
145
145
  @loaded_objects = {}
146
- unless File.file?(filename)
147
- raise ArgumentError, "#{filename} does not exist"
146
+ if template_id = indexed_template(input, page_num)
147
+ return template_id
148
148
  end
149
149
 
150
- hash = PDF::Reader::ObjectHash.new(filename)
150
+ io = if input.respond_to?(:seek) && input.respond_to?(:read)
151
+ input
152
+ elsif File.file?(input.to_s)
153
+ StringIO.new(File.binread(input.to_s))
154
+ else
155
+ raise ArgumentError, "input must be an IO-like object or a filename"
156
+ end
157
+
158
+ # unless File.file?(filename)
159
+ # raise ArgumentError, "#{filename} does not exist"
160
+ # end
161
+
162
+ hash = indexed_hash(input, io)
151
163
  ref = hash.page_references[page_num - 1]
152
164
 
153
- ref.nil? ? nil : load_object_graph(hash, ref).identifier
165
+ if ref.nil?
166
+ nil
167
+ else
168
+ index_template(input, page_num, load_object_graph(hash, ref).identifier)
169
+ end
154
170
 
155
171
  rescue PDF::Reader::MalformedPDFError, PDF::Reader::InvalidObjectError
156
172
  msg = "Error reading template file. If you are sure it's a valid PDF, it may be a bug."
@@ -162,6 +178,46 @@ module Prawn
162
178
 
163
179
  private
164
180
 
181
+ # An index for page templates so that their loaded object graph
182
+ # can be reused without multiple loading
183
+ def template_index
184
+ @template_index ||= {}
185
+ end
186
+
187
+ # An index for the read object hash of a pdf template so that the
188
+ # object hash does not need to be parsed multiple times when using
189
+ # different pages of the pdf as page templates
190
+ def hash_index
191
+ @hash_index ||= {}
192
+ end
193
+
194
+ # returns the indexed object graph identifier for a template page if
195
+ # it exists
196
+ def indexed_template(input, page_number)
197
+ key = indexing_key(input)
198
+ template_index[key] && template_index[key][page_number]
199
+ end
200
+
201
+ # indexes the identifier for a page from a template
202
+ def index_template(input, page_number, id)
203
+ (template_index[indexing_key(input)] ||= {})[page_number] ||= id
204
+ end
205
+
206
+ # reads and indexes a new IO for a template
207
+ # if the IO has been indexed already then the parsed object hash
208
+ # is returned directly
209
+ def indexed_hash(input, io)
210
+ hash_index[indexing_key(input)] ||= PDF::Reader::ObjectHash.new(io)
211
+ end
212
+
213
+ # the index key for the input.
214
+ # uses object_id so that both a string filename or an IO stream can be
215
+ # indexed and reused provided the same object gets used in multiple page
216
+ # template calls.
217
+ def indexing_key(input)
218
+ input.object_id
219
+ end
220
+
165
221
  # returns a nested array of object IDs for all pages in this object store.
166
222
  #
167
223
  def get_page_objects(obj)
@@ -63,7 +63,6 @@ module Prawn
63
63
  document.restore_graphics_state
64
64
  end
65
65
 
66
- @stamp_dictionary.data[:Length] = @stamp_stream.length + 1
67
66
  @stamp_dictionary << @stamp_stream
68
67
 
69
68
  @stamp_stream = nil
@@ -84,7 +83,7 @@ module Prawn
84
83
  unless dictionary.data[:Contents].is_a?(Array)
85
84
  dictionary.data[:Contents] = [content]
86
85
  end
87
- @content = document.ref(:Length => 0)
86
+ @content = document.ref({})
88
87
  dictionary.data[:Contents] << document.state.store[@content]
89
88
  document.open_graphics_state
90
89
  end
@@ -129,11 +128,9 @@ module Prawn
129
128
  if dictionary.data[:Contents].is_a?(Array)
130
129
  dictionary.data[:Contents].each do |stream|
131
130
  stream.compress_stream if document.compression_enabled?
132
- stream.data[:Length] = stream.stream.size
133
131
  end
134
132
  else
135
133
  content.compress_stream if document.compression_enabled?
136
- content.data[:Length] = content.stream.size
137
134
  end
138
135
  end
139
136
 
@@ -160,7 +157,7 @@ module Prawn
160
157
 
161
158
  def init_from_object(options)
162
159
  @dictionary = options[:object_id].to_i
163
- dictionary.data[:Parent] = document.state.store.pages
160
+ dictionary.data[:Parent] = document.state.store.pages if options[:page_template]
164
161
 
165
162
  unless dictionary.data[:Contents].is_a?(Array) # content only on leafs
166
163
  @content = dictionary.data[:Contents].identifier
@@ -175,7 +172,7 @@ module Prawn
175
172
  @size = options[:size] || "LETTER"
176
173
  @layout = options[:layout] || :portrait
177
174
 
178
- @content = document.ref(:Length => 0)
175
+ @content = document.ref({})
179
176
  content << "q" << "\n"
180
177
  @dictionary = document.ref(:Type => :Page,
181
178
  :Parent => document.state.store.pages,
@@ -16,14 +16,21 @@ module Prawn
16
16
  if "".respond_to?(:encode)
17
17
  # Ruby 1.9+
18
18
  def utf8_to_utf16(str)
19
- "\xFE\xFF".force_encoding("UTF-16BE") + str.encode("UTF-16BE")
19
+ utf16 = "\xFE\xFF".force_encoding("UTF-16BE") + str.encode("UTF-16BE")
20
+ end
21
+
22
+ # encodes any string into a hex representation. The result is a string
23
+ # with only 0-9 and a-f characters. That result is valid ASCII so tag
24
+ # it as such to account for behaviour of different ruby VMs
25
+ def string_to_hex(str)
26
+ str.unpack("H*").first.force_encoding("ascii")
20
27
  end
21
28
  else
22
29
  # Ruby 1.8
23
30
  def utf8_to_utf16(str)
24
31
  utf16 = "\xFE\xFF"
25
32
 
26
- str.unpack("U*").each do |cp|
33
+ str.codepoints do |cp|
27
34
  if cp < 0x10000 # Basic Multilingual Plane
28
35
  utf16 << [cp].pack("n")
29
36
  else
@@ -36,6 +43,10 @@ module Prawn
36
43
 
37
44
  utf16
38
45
  end
46
+
47
+ def string_to_hex(str)
48
+ str.unpack("H*").first
49
+ end
39
50
  end
40
51
 
41
52
  # Serializes Ruby objects to their PDF equivalents. Most primitive objects
@@ -57,7 +68,13 @@ module Prawn
57
68
  when NilClass then "null"
58
69
  when TrueClass then "true"
59
70
  when FalseClass then "false"
60
- when Numeric then String(obj)
71
+ when Numeric
72
+ if (str = String(obj)) =~ /e/i
73
+ # scientific notation is not supported in PDF
74
+ sprintf("%.16f", obj).gsub(/\.?0+\z/, "")
75
+ else
76
+ str
77
+ end
61
78
  when Array
62
79
  "[" << obj.map { |e| PdfObject(e, in_content_stream) }.join(' ') << "]"
63
80
  when Prawn::Core::LiteralString
@@ -71,7 +88,7 @@ module Prawn
71
88
  "<" << obj.unpack("H*").first << ">"
72
89
  when String
73
90
  obj = utf8_to_utf16(obj) unless in_content_stream
74
- "<" << obj.unpack("H*").first << ">"
91
+ "<" << string_to_hex(obj) << ">"
75
92
  when Symbol
76
93
  "/" + obj.to_s.unpack("C*").map { |n|
77
94
  if n < 33 || n > 126 || [35,40,41,47,60,62].include?(n)