prawn 1.0.0.rc2 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (169) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +9 -0
  3. data/COPYING +2 -2
  4. data/Gemfile +8 -15
  5. data/LICENSE +1 -1
  6. data/Rakefile +25 -16
  7. data/data/images/16bit.alpha +0 -0
  8. data/data/images/16bit.color +0 -0
  9. data/data/images/dice.alpha +0 -0
  10. data/data/images/dice.color +0 -0
  11. data/data/images/indexed_color.dat +0 -0
  12. data/data/images/indexed_color.png +0 -0
  13. data/data/images/license.md +8 -0
  14. data/data/images/page_white_text.alpha +0 -0
  15. data/data/images/page_white_text.color +0 -0
  16. data/lib/prawn.rb +85 -23
  17. data/lib/prawn/document.rb +134 -116
  18. data/lib/prawn/document/bounding_box.rb +33 -4
  19. data/lib/prawn/document/column_box.rb +18 -6
  20. data/lib/prawn/document/graphics_state.rb +11 -74
  21. data/lib/prawn/document/internals.rb +24 -23
  22. data/lib/prawn/document/span.rb +12 -10
  23. data/lib/prawn/encoding.rb +8 -9
  24. data/lib/prawn/errors.rb +13 -32
  25. data/lib/prawn/font.rb +137 -105
  26. data/lib/prawn/font/afm.rb +76 -32
  27. data/lib/prawn/font/dfont.rb +4 -3
  28. data/lib/prawn/font/ttf.rb +33 -25
  29. data/lib/prawn/font_metric_cache.rb +47 -0
  30. data/lib/prawn/graphics.rb +177 -57
  31. data/lib/prawn/graphics/cap_style.rb +4 -3
  32. data/lib/prawn/graphics/color.rb +5 -4
  33. data/lib/prawn/graphics/dash.rb +53 -31
  34. data/lib/prawn/graphics/join_style.rb +9 -7
  35. data/lib/prawn/graphics/patterns.rb +4 -15
  36. data/lib/prawn/graphics/transformation.rb +10 -9
  37. data/lib/prawn/graphics/transparency.rb +3 -1
  38. data/lib/prawn/{layout/grid.rb → grid.rb} +72 -54
  39. data/lib/prawn/image_handler.rb +42 -0
  40. data/lib/prawn/images.rb +58 -54
  41. data/lib/prawn/images/image.rb +6 -22
  42. data/lib/prawn/images/jpg.rb +20 -14
  43. data/lib/prawn/images/png.rb +58 -121
  44. data/lib/prawn/layout.rb +12 -15
  45. data/lib/prawn/measurement_extensions.rb +10 -6
  46. data/lib/prawn/measurements.rb +27 -21
  47. data/lib/prawn/outline.rb +108 -147
  48. data/lib/prawn/repeater.rb +10 -8
  49. data/lib/prawn/security.rb +59 -40
  50. data/lib/prawn/security/arcfour.rb +52 -0
  51. data/lib/prawn/soft_mask.rb +4 -4
  52. data/lib/prawn/stamp.rb +5 -3
  53. data/lib/prawn/table.rb +83 -60
  54. data/lib/prawn/table/cell.rb +17 -21
  55. data/lib/prawn/table/cell/image.rb +2 -3
  56. data/lib/prawn/table/cell/in_table.rb +8 -2
  57. data/lib/prawn/table/cell/span_dummy.rb +5 -0
  58. data/lib/prawn/table/cell/subtable.rb +3 -2
  59. data/lib/prawn/table/cell/text.rb +14 -12
  60. data/lib/prawn/table/cells.rb +58 -14
  61. data/lib/prawn/table/column_width_calculator.rb +61 -0
  62. data/lib/prawn/text.rb +27 -26
  63. data/lib/prawn/text/box.rb +12 -6
  64. data/lib/prawn/text/formatted.rb +5 -4
  65. data/lib/prawn/text/formatted/arranger.rb +290 -0
  66. data/lib/prawn/text/formatted/box.rb +85 -57
  67. data/lib/prawn/text/formatted/fragment.rb +11 -11
  68. data/lib/prawn/text/formatted/line_wrap.rb +266 -0
  69. data/lib/prawn/text/formatted/parser.rb +11 -4
  70. data/lib/prawn/text/formatted/wrap.rb +156 -0
  71. data/lib/prawn/utilities.rb +5 -3
  72. data/manual/document_and_page_options/document_and_page_options.rb +2 -1
  73. data/manual/document_and_page_options/metadata.rb +3 -3
  74. data/manual/document_and_page_options/page_size.rb +2 -2
  75. data/manual/document_and_page_options/print_scaling.rb +20 -0
  76. data/manual/example_file.rb +2 -7
  77. data/manual/example_helper.rb +62 -81
  78. data/manual/graphics/common_lines.rb +2 -0
  79. data/manual/graphics/helper.rb +11 -4
  80. data/manual/graphics/stroke_dash.rb +19 -14
  81. data/manual/manual/cover.rb +16 -0
  82. data/manual/manual/manual.rb +1 -5
  83. data/manual/text/fallback_fonts.rb +4 -4
  84. data/manual/text/formatted_text.rb +5 -5
  85. data/manual/text/inline.rb +2 -4
  86. data/manual/text/registering_families.rb +12 -12
  87. data/manual/text/single_usage.rb +4 -4
  88. data/manual/text/text.rb +0 -2
  89. data/prawn.gemspec +21 -13
  90. data/spec/acceptance/png.rb +23 -0
  91. data/spec/annotations_spec.rb +16 -32
  92. data/spec/bounding_box_spec.rb +22 -5
  93. data/spec/cell_spec.rb +49 -5
  94. data/spec/column_box_spec.rb +32 -0
  95. data/spec/destinations_spec.rb +5 -5
  96. data/spec/document_spec.rb +112 -118
  97. data/spec/extensions/encoding_helpers.rb +5 -2
  98. data/spec/font_metric_cache_spec.rb +52 -0
  99. data/spec/font_spec.rb +121 -120
  100. data/spec/formatted_text_arranger_spec.rb +24 -24
  101. data/spec/formatted_text_box_spec.rb +31 -32
  102. data/spec/formatted_text_fragment_spec.rb +2 -2
  103. data/spec/graphics_spec.rb +63 -45
  104. data/spec/grid_spec.rb +24 -13
  105. data/spec/image_handler_spec.rb +54 -0
  106. data/spec/images_spec.rb +34 -21
  107. data/spec/inline_formatted_text_parser_spec.rb +69 -20
  108. data/spec/jpg_spec.rb +3 -3
  109. data/spec/line_wrap_spec.rb +25 -14
  110. data/spec/measurement_units_spec.rb +5 -5
  111. data/spec/outline_spec.rb +68 -64
  112. data/spec/png_spec.rb +15 -18
  113. data/spec/reference_spec.rb +2 -82
  114. data/spec/repeater_spec.rb +1 -1
  115. data/spec/security_spec.rb +41 -9
  116. data/spec/soft_mask_spec.rb +0 -40
  117. data/spec/span_spec.rb +6 -11
  118. data/spec/spec_helper.rb +20 -2
  119. data/spec/stamp_spec.rb +19 -20
  120. data/spec/stroke_styles_spec.rb +31 -13
  121. data/spec/table/span_dummy_spec.rb +17 -0
  122. data/spec/table_spec.rb +268 -43
  123. data/spec/text_at_spec.rb +13 -27
  124. data/spec/text_box_spec.rb +35 -30
  125. data/spec/text_spec.rb +56 -40
  126. data/spec/transparency_spec.rb +5 -5
  127. metadata +214 -217
  128. data/README.md +0 -98
  129. data/data/fonts/Action Man.dfont +0 -0
  130. data/data/fonts/Activa.ttf +0 -0
  131. data/data/fonts/Chalkboard.ttf +0 -0
  132. data/data/fonts/DejaVuSans.ttf +0 -0
  133. data/data/fonts/Dustismo_Roman.ttf +0 -0
  134. data/data/fonts/comicsans.ttf +0 -0
  135. data/data/fonts/gkai00mp.ttf +0 -0
  136. data/data/images/16bit.dat +0 -0
  137. data/data/images/barcode_issue.png +0 -0
  138. data/data/images/dice.dat +0 -0
  139. data/data/images/page_white_text.dat +0 -0
  140. data/data/images/rails.dat +0 -0
  141. data/data/images/rails.png +0 -0
  142. data/lib/prawn/compatibility.rb +0 -87
  143. data/lib/prawn/core.rb +0 -87
  144. data/lib/prawn/core/annotations.rb +0 -61
  145. data/lib/prawn/core/byte_string.rb +0 -9
  146. data/lib/prawn/core/destinations.rb +0 -90
  147. data/lib/prawn/core/document_state.rb +0 -79
  148. data/lib/prawn/core/literal_string.rb +0 -16
  149. data/lib/prawn/core/name_tree.rb +0 -177
  150. data/lib/prawn/core/object_store.rb +0 -320
  151. data/lib/prawn/core/page.rb +0 -212
  152. data/lib/prawn/core/pdf_object.rb +0 -125
  153. data/lib/prawn/core/reference.rb +0 -119
  154. data/lib/prawn/core/text.rb +0 -268
  155. data/lib/prawn/core/text/formatted/arranger.rb +0 -294
  156. data/lib/prawn/core/text/formatted/line_wrap.rb +0 -288
  157. data/lib/prawn/core/text/formatted/wrap.rb +0 -153
  158. data/lib/prawn/document/page_geometry.rb +0 -136
  159. data/lib/prawn/document/snapshot.rb +0 -89
  160. data/manual/manual/foreword.rb +0 -13
  161. data/manual/templates/full_template.rb +0 -23
  162. data/manual/templates/page_template.rb +0 -47
  163. data/manual/templates/templates.rb +0 -26
  164. data/manual/text/group.rb +0 -29
  165. data/spec/name_tree_spec.rb +0 -112
  166. data/spec/object_store_spec.rb +0 -170
  167. data/spec/pdf_object_spec.rb +0 -172
  168. data/spec/snapshot_spec.rb +0 -186
  169. data/spec/template_spec.rb +0 -351
@@ -6,11 +6,13 @@
6
6
  #
7
7
  # This is free software. Please see the LICENSE and COPYING files for details.
8
8
 
9
- require 'prawn/encoding'
10
- require 'afm'
9
+ require_relative '../../prawn/encoding'
11
10
 
12
11
  module Prawn
13
12
  class Font
13
+
14
+ # @private
15
+
14
16
  class AFM < Font
15
17
  BUILT_INS = %w[ Courier Helvetica Times-Roman Symbol ZapfDingbats
16
18
  Courier-Bold Courier-Oblique Courier-BoldOblique
@@ -49,21 +51,23 @@ module Prawn
49
51
  file_name << ".afm" unless file_name =~ /\.afm$/
50
52
  file_name = file_name[0] == ?/ ? file_name : find_font(file_name)
51
53
 
52
- font_data = @@font_data[file_name] ||= ::AFM::Font.new(file_name)
53
- @glyph_table = build_glyph_table(font_data)
54
- @kern_pairs = font_data.kern_pairs
55
- @kern_pair_table = build_kern_pair_table(@kern_pairs)
56
- @attributes = font_data.metadata
54
+ font_data = @@font_data[file_name] ||= parse_afm(file_name)
55
+ @glyph_widths = font_data[:glyph_widths]
56
+ @glyph_table = font_data[:glyph_table]
57
+ @bounding_boxes = font_data[:bounding_boxes]
58
+ @kern_pairs = font_data[:kern_pairs]
59
+ @kern_pair_table = font_data[:kern_pair_table]
60
+ @attributes = font_data[:attributes]
57
61
 
58
- @ascender = @attributes["Ascender"].to_i
59
- @descender = @attributes["Descender"].to_i
62
+ @ascender = @attributes["ascender"].to_i
63
+ @descender = @attributes["descender"].to_i
60
64
  @line_gap = Float(bbox[3] - bbox[1]) - (@ascender - @descender)
61
65
  end
62
66
 
63
67
  # The font bbox, as an array of integers
64
68
  #
65
69
  def bbox
66
- @bbox ||= @attributes['FontBBox'].split(/\s+/).map { |e| Integer(e) }
70
+ @bbox ||= @attributes['fontbbox'].split(/\s+/).map { |e| Integer(e) }
67
71
  end
68
72
 
69
73
  # NOTE: String *must* be encoded as WinAnsi
@@ -89,7 +93,7 @@ module Prawn
89
93
  # string. Changes the encoding in-place, so the argument itself
90
94
  # is replaced with a string in WinAnsi encoding.
91
95
  #
92
- def normalize_encoding(text)
96
+ def normalize_encoding(text)
93
97
  enc = @@winansi
94
98
  text.unpack("U*").collect { |i| enc[i] }.pack("C*")
95
99
  rescue ArgumentError
@@ -141,7 +145,7 @@ module Prawn
141
145
  end
142
146
 
143
147
  def symbolic?
144
- attributes["CharacterSet"] == "Special"
148
+ attributes["characterset"] == "Special"
145
149
  end
146
150
 
147
151
  def find_font(file)
@@ -152,6 +156,61 @@ module Prawn
152
156
  self.class.metrics_path.join("\n")
153
157
  end
154
158
 
159
+ def parse_afm(file_name)
160
+ data = {:glyph_widths => {}, :bounding_boxes => {}, :kern_pairs => {}, :attributes => {}}
161
+ section = []
162
+
163
+ File.foreach(file_name) do |line|
164
+ case line
165
+ when /^Start(\w+)/
166
+ section.push $1
167
+ next
168
+ when /^End(\w+)/
169
+ section.pop
170
+ next
171
+ end
172
+
173
+ case section
174
+ when ["FontMetrics", "CharMetrics"]
175
+ next unless line =~ /^CH?\s/
176
+
177
+ name = line[/\bN\s+(\.?\w+)\s*;/, 1]
178
+ data[:glyph_widths][name] = line[/\bWX\s+(\d+)\s*;/, 1].to_i
179
+ data[:bounding_boxes][name] = line[/\bB\s+([^;]+);/, 1].to_s.rstrip
180
+ when ["FontMetrics", "KernData", "KernPairs"]
181
+ next unless line =~ /^KPX\s+(\.?\w+)\s+(\.?\w+)\s+(-?\d+)/
182
+ data[:kern_pairs][[$1, $2]] = $3.to_i
183
+ when ["FontMetrics", "KernData", "TrackKern"],
184
+ ["FontMetrics", "Composites"]
185
+ next
186
+ else
187
+ parse_generic_afm_attribute(line, data)
188
+ end
189
+ end
190
+
191
+ # process data parsed from AFM file to build tables which
192
+ # will be used when measuring and kerning text
193
+ data[:glyph_table] = (0..255).map do |i|
194
+ data[:glyph_widths][Encoding::WinAnsi::CHARACTERS[i]].to_i
195
+ end
196
+
197
+ character_hash = Hash[Encoding::WinAnsi::CHARACTERS.zip((0..Encoding::WinAnsi::CHARACTERS.size).to_a)]
198
+ data[:kern_pair_table] = data[:kern_pairs].inject({}) do |h,p|
199
+ h[p[0].map { |n| character_hash[n] }] = p[1]
200
+ h
201
+ end
202
+
203
+ data.each_value { |hash| hash.freeze }
204
+ data.freeze
205
+ end
206
+
207
+ def parse_generic_afm_attribute(line, hash)
208
+ line =~ /(^\w+)\s+(.*)/
209
+ key, value = $1.to_s.downcase, $2
210
+
211
+ hash[:attributes][key] = hash[:attributes][key] ? Array(hash[:attributes][key]) << value : value
212
+ end
213
+
155
214
  # converts a string into an array with spacing offsets
156
215
  # bewteen characters that need to be kerned
157
216
  #
@@ -161,37 +220,22 @@ module Prawn
161
220
  kerned = [[]]
162
221
  last_byte = nil
163
222
 
164
- string.bytes do |byte|
223
+ string.each_byte do |byte|
165
224
  if k = last_byte && @kern_pair_table[[last_byte, byte]]
166
225
  kerned << -k << [byte]
167
226
  else
168
227
  kerned.last << byte
169
- end
228
+ end
170
229
  last_byte = byte
171
230
  end
172
231
 
173
- kerned.map { |e|
232
+ kerned.map { |e|
174
233
  e = (Array === e ? e.pack("C*") : e)
175
- e.respond_to?(:force_encoding) ? e.force_encoding("Windows-1252") : e
234
+ e.respond_to?(:force_encoding) ? e.force_encoding(::Encoding::Windows_1252) : e
176
235
  }
177
236
  end
178
237
 
179
- def build_kern_pair_table(kern_pairs)
180
- character_hash = Hash[Encoding::WinAnsi::CHARACTERS.zip((0..Encoding::WinAnsi::CHARACTERS.size).to_a)]
181
- kern_pairs.inject({}) do |h,p|
182
- h[
183
- [character_hash[p[0]], character_hash[p[1]]]
184
- ] = p[2]
185
- h
186
- end
187
- end
188
-
189
- def build_glyph_table(font_data)
190
- (0..255).map do |char|
191
- metrics = font_data.metrics_for(char)
192
- metrics ? metrics[:wx] : 0
193
- end
194
- end
238
+ private
195
239
 
196
240
  def unscaled_width_of(string)
197
241
  string.bytes.inject(0) do |s,r|
@@ -6,12 +6,13 @@
6
6
  #
7
7
  # This is free software. Please see the LICENSE and COPYING files for details.
8
8
  #
9
- require 'prawn/font/ttf'
9
+ require_relative 'ttf'
10
10
 
11
11
  module Prawn
12
12
  class Font
13
+ # @private
13
14
  class DFont < TTF
14
-
15
+
15
16
  # Returns a list of the names of all named fonts in the given dfont file.
16
17
  # Note that fonts are not required to be named in a dfont file, so the
17
18
  # list may be empty even if the file does contain fonts. Also, note that
@@ -32,7 +33,7 @@ module Prawn
32
33
  end
33
34
  end
34
35
 
35
- private
36
+ private
36
37
 
37
38
  def read_ttf_file
38
39
  TTFunk::File.from_dfont(@name, @options[:font] || 0)
@@ -2,7 +2,7 @@
2
2
 
3
3
  # prawn/font/ttf.rb : Implements AFM font support for Prawn
4
4
  #
5
- # Copyright May 2008, Gregory Brown / James Healy / Jamis Buck
5
+ # Copyright May 2008, Gregory Brown / James Healy / Jamis Buck
6
6
  # All Rights Reserved.
7
7
  #
8
8
  # This is free software. Please see the LICENSE and COPYING files for details.
@@ -12,13 +12,15 @@ require 'ttfunk/subset_collection'
12
12
 
13
13
  module Prawn
14
14
  class Font
15
+
16
+ # @private
15
17
  class TTF < Font
16
18
  attr_reader :ttf, :subsets
17
19
 
18
20
  def unicode?
19
21
  true
20
22
  end
21
-
23
+
22
24
  def initialize(document, name, options={})
23
25
  super
24
26
 
@@ -26,8 +28,8 @@ module Prawn
26
28
  @subsets = TTFunk::SubsetCollection.new(@ttf)
27
29
 
28
30
  @attributes = {}
29
- @bounding_boxes = {}
30
- @char_widths = {}
31
+ @bounding_boxes = {}
32
+ @char_widths = {}
31
33
  @has_kerning_data = @ttf.kerning.exists? && @ttf.kerning.tables.any?
32
34
 
33
35
  @ascender = Integer(@ttf.ascent * scale_factor)
@@ -42,7 +44,7 @@ module Prawn
42
44
  kern(string).inject(0) do |s,r|
43
45
  if r.is_a?(Numeric)
44
46
  s - r
45
- else
47
+ else
46
48
  r.inject(s) { |s2, u| s2 + character_width_by_code(u) }
47
49
  end
48
50
  end * scale
@@ -51,17 +53,17 @@ module Prawn
51
53
  s + character_width_by_code(r)
52
54
  end * scale
53
55
  end
54
- end
55
-
56
+ end
57
+
56
58
  # The font bbox, as an array of integers
57
- #
59
+ #
58
60
  def bbox
59
61
  @bbox ||= @ttf.bbox.map { |i| Integer(i * scale_factor) }
60
62
  end
61
63
 
62
64
  # Returns true if the font has kerning data, false otherwise
63
65
  def has_kerning_data?
64
- @has_kerning_data
66
+ @has_kerning_data
65
67
  end
66
68
 
67
69
  # Perform any changes to the string that need to happen
@@ -77,7 +79,7 @@ module Prawn
77
79
 
78
80
  if options[:kerning]
79
81
  last_subset = nil
80
- kern(text).inject([]) do |result, element|
82
+ kern(text).inject([]) do |result, element|
81
83
  if element.is_a?(Numeric)
82
84
  result.last[1] = [result.last[1]] unless result.last[1].is_a?(Array)
83
85
  result.last[1] << element
@@ -102,7 +104,7 @@ module Prawn
102
104
  @subsets.encode(text.unpack("U*"))
103
105
  end
104
106
  end
105
-
107
+
106
108
  def basename
107
109
  @basename ||= @ttf.name.postscript_name
108
110
  end
@@ -160,7 +162,15 @@ module Prawn
160
162
  end
161
163
 
162
164
  def normalize_encoding(text)
163
- text.normalize_to_utf8
165
+ begin
166
+ text.encode(::Encoding::UTF_8)
167
+ rescue => e
168
+ puts e
169
+ raise Prawn::Errors::IncompatibleStringEncoding, "Encoding " +
170
+ "#{text.encoding} can not be transparently converted to UTF-8. " +
171
+ "Please ensure the encoding of the string you are attempting " +
172
+ "to use is set correctly"
173
+ end
164
174
  end
165
175
 
166
176
  def glyph_present?(char)
@@ -171,7 +181,7 @@ module Prawn
171
181
  # Returns the number of characters in +str+ (a UTF-8-encoded string).
172
182
  #
173
183
  def character_count(str)
174
- str.unicode_length
184
+ str.length
175
185
  end
176
186
 
177
187
  private
@@ -179,7 +189,7 @@ module Prawn
179
189
  def cmap
180
190
  @cmap ||= @ttf.cmap.unicode.first or raise("no unicode cmap for font")
181
191
  end
182
-
192
+
183
193
  # +string+ must be UTF8-encoded.
184
194
  #
185
195
  # Returns an array. If an element is a numeric, it represents the
@@ -188,7 +198,7 @@ module Prawn
188
198
  def kern(string)
189
199
  a = []
190
200
 
191
- string.codepoints do |r|
201
+ string.each_codepoint do |r|
192
202
  if a.empty?
193
203
  a << [r]
194
204
  elsif (kern = kern_pairs_table[[cmap[a.last.last], cmap[r]]])
@@ -215,7 +225,7 @@ module Prawn
215
225
  @hmtx ||= @ttf.horizontal_metrics
216
226
  end
217
227
 
218
- def character_width_by_code(code)
228
+ def character_width_by_code(code)
219
229
  return 0 unless cmap[code]
220
230
 
221
231
  # Some TTF fonts have nonzero widths for \n (UTF-8 / ASCII code: 10).
@@ -223,7 +233,7 @@ module Prawn
223
233
  return 0.0 if code == 10
224
234
 
225
235
  @char_widths[code] ||= Integer(hmtx.widths[cmap[code]] * scale_factor)
226
- end
236
+ end
227
237
 
228
238
  def scale_factor
229
239
  @scale ||= 1000.0 / @ttf.header.units_per_em
@@ -233,7 +243,7 @@ module Prawn
233
243
  temp_name = @ttf.name.postscript_name.gsub("\0","").to_sym
234
244
  ref = @document.ref!(:Type => :Font, :BaseFont => temp_name)
235
245
 
236
- # Embed the font metrics in the document after everything has been
246
+ # Embed the font metrics in the document after everything has been
237
247
  # drawn, just before the document is emitted.
238
248
  @document.before_render { |doc| embed(ref, subset) }
239
249
 
@@ -254,11 +264,9 @@ module Prawn
254
264
 
255
265
  raise "Can't detect a postscript name for #{file}" if basename.nil?
256
266
 
257
- compressed_font = Zlib::Deflate.deflate(font_content)
258
-
259
- fontfile = @document.ref!(:Length1 => font_content.size,
260
- :Filter => :FlateDecode )
261
- fontfile << compressed_font
267
+ fontfile = @document.ref!(:Length1 => font_content.size)
268
+ fontfile.stream << font_content
269
+ fontfile.stream.compress!
262
270
 
263
271
  descriptor = @document.ref!(:Type => :FontDescriptor,
264
272
  :FontName => basename.to_sym,
@@ -289,7 +297,7 @@ module Prawn
289
297
  map = @subsets[subset].to_unicode_map
290
298
 
291
299
  ranges = [[]]
292
- lines = map.keys.sort.inject("") do |s, code|
300
+ map.keys.sort.inject("") do |s, code|
293
301
  ranges << [] if ranges.last.length >= 100
294
302
  unicode = map[code]
295
303
  ranges.last << "<%02x><%04x>" % [code, unicode]
@@ -303,7 +311,7 @@ module Prawn
303
311
 
304
312
  cmap = @document.ref!({})
305
313
  cmap << to_unicode_cmap
306
- cmap.compress_stream
314
+ cmap.stream.compress!
307
315
 
308
316
  reference.data.update(:Subtype => :TrueType,
309
317
  :BaseFont => basename.to_sym,
@@ -0,0 +1,47 @@
1
+ # encoding: utf-8
2
+ #
3
+ # font_metric_cache.rb : The Prawn font class
4
+ #
5
+ # Copyright Dec 2012, Kenneth Kalmer. All Rights Reserved.
6
+ #
7
+ # This is free software. Please see the LICENSE and COPYING files for details.
8
+ #
9
+
10
+ module Prawn
11
+
12
+ # Cache used internally by Prawn::Document instances to calculate the width
13
+ # of various strings for layout purposes.
14
+ #
15
+ # @private
16
+ class FontMetricCache
17
+
18
+ CacheEntry = Struct.new( :font, :options, :string )
19
+
20
+ def initialize( document )
21
+ @document = document
22
+
23
+ @cache = {}
24
+ end
25
+
26
+ def width_of( string, options )
27
+ f = if options[:style]
28
+ # override style with :style => :bold
29
+ @document.find_font(@document.font ? @document.font.name : 'Helvetica',
30
+ :style => options[:style])
31
+ else
32
+ @document.font
33
+ end
34
+
35
+ key = CacheEntry.new( f, options, string )
36
+
37
+ unless length = @cache[ key ]
38
+ length = @cache[ key ] = f.compute_width_of(string, options)
39
+ end
40
+
41
+ length +
42
+ (@document.character_spacing * @document.font.character_count(string))
43
+ end
44
+
45
+ end
46
+
47
+ end
@@ -6,13 +6,14 @@
6
6
  #
7
7
  # This is free software. Please see the LICENSE and COPYING files for details.
8
8
 
9
- require "prawn/graphics/color"
10
- require "prawn/graphics/dash"
11
- require "prawn/graphics/cap_style"
12
- require "prawn/graphics/join_style"
13
- require "prawn/graphics/transparency"
14
- require "prawn/graphics/transformation"
15
- require "prawn/graphics/patterns"
9
+
10
+ require_relative "graphics/color"
11
+ require_relative "graphics/dash"
12
+ require_relative "graphics/cap_style"
13
+ require_relative "graphics/join_style"
14
+ require_relative "graphics/transparency"
15
+ require_relative "graphics/transformation"
16
+ require_relative "graphics/patterns"
16
17
 
17
18
  module Prawn
18
19
 
@@ -32,6 +33,8 @@ module Prawn
32
33
  include Transformation
33
34
  include Patterns
34
35
 
36
+ # @group Stable API
37
+
35
38
  #######################################################################
36
39
  # Low level drawing operations must map the point to absolute coords! #
37
40
  #######################################################################
@@ -82,9 +85,9 @@ module Prawn
82
85
  x,y = map_to_absolute(point)
83
86
  add_content("%.3f %.3f %.3f %.3f re" % [ x, y - height, width, height ])
84
87
  end
85
-
88
+
86
89
  # Draws a rounded rectangle given <tt>point</tt>, <tt>width</tt> and
87
- # <tt>height</tt> and <tt>radius</tt> for the rounded corner. The rectangle
90
+ # <tt>height</tt> and <tt>radius</tt> for the rounded corner. The rectangle
88
91
  # is bounded by its upper-left corner.
89
92
  #
90
93
  # pdf.rounded_rectangle [300,300], 100, 200, 10
@@ -93,7 +96,7 @@ module Prawn
93
96
  x, y = point
94
97
  rounded_polygon(radius, point, [x + width, y], [x + width, y - height], [x, y - height])
95
98
  end
96
-
99
+
97
100
 
98
101
  ###########################################################
99
102
  # Higher level functions: May use relative coords #
@@ -138,7 +141,7 @@ module Prawn
138
141
  # current <tt>y</tt> position, or the position specified by the :at option.
139
142
  #
140
143
  # # draw a line from [25, 75] to [100, 75]
141
- # horizontal_line 25, 100, :at => 75
144
+ # horizontal_line 25, 100, :at => 75
142
145
  #
143
146
  def horizontal_line(x1,x2,options={})
144
147
  if options[:at]
@@ -146,7 +149,7 @@ module Prawn
146
149
  else
147
150
  y1 = y - bounds.absolute_bottom
148
151
  end
149
-
152
+
150
153
  line(x1,y1,x2,y1)
151
154
  end
152
155
 
@@ -181,13 +184,6 @@ module Prawn
181
184
  #
182
185
  KAPPA = 4.0 * ((Math.sqrt(2) - 1.0) / 3.0)
183
186
 
184
- # <b>DEPRECATED:</b> Please use <tt>circle</tt> instead.
185
- def circle_at(point, options)
186
- warn "[DEPRECATION] 'circle_at' is deprecated in favor of 'circle'. " +
187
- "'circle_at' will be removed in release 1.1"
188
- circle(point, options[:radius])
189
- end
190
-
191
187
  # Draws a circle of radius <tt>radius</tt> with the centre-point at <tt>point</tt>
192
188
  # as a complete subpath. The drawing point will be moved to the
193
189
  # centre-point upon completion of the drawing the circle.
@@ -198,13 +194,6 @@ module Prawn
198
194
  ellipse(center, radius, radius)
199
195
  end
200
196
 
201
- # <b>DEPRECATED:</b> Please use <tt>ellipse</tt> instead.
202
- def ellipse_at(point, r1, r2=r1)
203
- warn "[DEPRECATION] 'ellipse_at' is deprecated in favor of 'ellipse'. " +
204
- "'ellipse_at' will be removed in release 1.1"
205
- ellipse(point, r1, r2)
206
- end
207
-
208
197
  # Draws an ellipse of +x+ radius <tt>r1</tt> and +y+ radius <tt>r2</tt>
209
198
  # with the centre-point at <tt>point</tt> as a complete subpath. The
210
199
  # drawing point will be moved to the centre-point upon completion of the
@@ -252,7 +241,7 @@ module Prawn
252
241
  # close the path
253
242
  add_content "h"
254
243
  end
255
-
244
+
256
245
  # Draws a rounded polygon from specified points using the radius to define bezier curves
257
246
  #
258
247
  # # draws a rounded filled in polygon
@@ -268,20 +257,19 @@ module Prawn
268
257
  # close the path
269
258
  add_content "h"
270
259
  end
271
-
272
-
260
+
261
+
273
262
  # Creates a rounded vertex for a line segment used for building a rounded polygon
274
263
  # requires a radius to define bezier curve and three points. The first two points define
275
264
  # the line segment and the third point helps define the curve for the vertex.
276
265
  def rounded_vertex(radius, *points)
277
- x0,y0,x1,y1,x2,y2 = points.flatten
278
266
  radial_point_1 = point_on_line(radius, points[0], points[1])
279
267
  bezier_point_1 = point_on_line((radius - radius*KAPPA), points[0], points[1] )
280
268
  radial_point_2 = point_on_line(radius, points[2], points[1])
281
269
  bezier_point_2 = point_on_line((radius - radius*KAPPA), points[2], points[1])
282
270
  line_to(radial_point_1)
283
271
  curve_to(radial_point_2, :bounds => [bezier_point_1, bezier_point_2])
284
- end
272
+ end
285
273
 
286
274
  # Strokes the current path. If a block is provided, yields to the block
287
275
  # before closing the path. See Graphics::Color for color details.
@@ -298,13 +286,75 @@ module Prawn
298
286
  yield if block_given?
299
287
  add_content "s"
300
288
  end
301
-
289
+
302
290
  # Draws and strokes a rectangle represented by the current bounding box
303
291
  #
304
292
  def stroke_bounds
305
293
  stroke_rectangle bounds.top_left, bounds.width, bounds.height
306
294
  end
307
295
 
296
+ # Draws and strokes X and Y axes rulers beginning at the current bounding
297
+ # box origin (or at a custom location).
298
+ #
299
+ # == Options
300
+ #
301
+ # +:at+::
302
+ # Origin of the X and Y axes (default: [0, 0] = origin of the bounding
303
+ # box)
304
+ #
305
+ # +:width+::
306
+ # Length of the X axis (default: width of the bounding box)
307
+ #
308
+ # +:height+::
309
+ # Length of the Y axis (default: height of the bounding box)
310
+ #
311
+ # +:step_length+::
312
+ # Length of the step between markers (default: 100)
313
+ #
314
+ # +:negative_axes_length+::
315
+ # Length of the negative parts of the axes (default: 20)
316
+ #
317
+ # +:color+:
318
+ # The color of the axes and the text.
319
+ #
320
+ def stroke_axis(options = {})
321
+ options = {
322
+ :at => [0,0],
323
+ :height => bounds.height.to_i - (options[:at] || [0,0])[1],
324
+ :width => bounds.width.to_i - (options[:at] || [0,0])[0],
325
+ :step_length => 100,
326
+ :negative_axes_length => 20,
327
+ :color => "000000",
328
+ }.merge(options)
329
+
330
+ Prawn.verify_options([:at, :width, :height, :step_length,
331
+ :negative_axes_length, :color], options)
332
+
333
+ save_graphics_state do
334
+ fill_color(options[:color])
335
+ stroke_color(options[:color])
336
+
337
+ dash(1, :space => 4)
338
+ stroke_horizontal_line(options[:at][0] - options[:negative_axes_length],
339
+ options[:at][0] + options[:width], :at => options[:at][1])
340
+ stroke_vertical_line(options[:at][1] - options[:negative_axes_length],
341
+ options[:at][1] + options[:height], :at => options[:at][0])
342
+ undash
343
+
344
+ fill_circle(options[:at], 1)
345
+
346
+ (options[:step_length]..options[:width]).step(options[:step_length]) do |point|
347
+ fill_circle([options[:at][0] + point, options[:at][1]], 1)
348
+ draw_text(point, :at => [options[:at][0] + point - 5, options[:at][1] - 10], :size => 7)
349
+ end
350
+
351
+ (options[:step_length]..options[:height]).step(options[:step_length]) do |point|
352
+ fill_circle([options[:at][0], options[:at][1] + point], 1)
353
+ draw_text(point, :at => [options[:at][0] - 17, options[:at][1] + point - 2], :size => 7)
354
+ end
355
+ end
356
+ end
357
+
308
358
  # Closes and fills the current path. See Graphics::Color for color details.
309
359
  #
310
360
  # If the option :fill_rule => :even_odd is specified, Prawn will use the
@@ -339,140 +389,210 @@ module Prawn
339
389
 
340
390
  ##
341
391
  # :method: stroke_rectangle
342
- # Draws and strokes a rectangle given +point+, +width+ and +height+. The rectangle is bounded by its upper-left corner.
392
+ #
393
+ # Draws and strokes a rectangle given +point+, +width+ and +height+. The
394
+ # rectangle is bounded by its upper-left corner.
395
+ #
343
396
  # :call-seq:
344
397
  # stroke_rectangle(point,width,height)
345
398
 
346
399
  ##
347
400
  # :method: fill_rectangle
348
- # Draws and fills ills a rectangle given +point+, +width+ and +height+. The rectangle is bounded by its upper-left corner.
401
+ #
402
+ # Draws and fills ills a rectangle given +point+, +width+ and +height+. The
403
+ # rectangle is bounded by its upper-left corner.
404
+ #
349
405
  # :call-seq:
350
406
  # fill_rectangle(point,width,height)
351
407
 
352
408
  ##
353
409
  # :method: fill_and_stroke_rectangle
354
- # Draws, fills, and strokes a rectangle given +point+, +width+ and +height+. The rectangle is bounded by its upper-left corner.
410
+ #
411
+ # Draws, fills, and strokes a rectangle given +point+, +width+ and +height+.
412
+ # The rectangle is bounded by its upper-left corner.
413
+ #
355
414
  # :call-seq:
356
415
  # fill_and_stroke_rectangle(point,width,height)
357
416
 
358
417
  ##
359
418
  # :method: stroke_rounded_rectangle
360
- # Draws and strokes a rounded rectangle given +point+, +width+ and +height+ and +radius+ for the rounded corner. The rectangle is bounded by its upper-left corner.
419
+ #
420
+ # Draws and strokes a rounded rectangle given +point+, +width+ and +height+
421
+ # and +radius+ for the rounded corner. The rectangle is bounded by its
422
+ # upper-left corner.
423
+ #
361
424
  # :call-seq:
362
425
  # stroke_rounded_rectangle(point,width,height,radius)
363
426
 
364
427
  ##
365
428
  # :method: fill_rounded_rectangle
366
- # Draws and fills a rounded rectangle given +point+, +width+ and +height+ and +radius+ for the rounded corner. The rectangle is bounded by its upper-left corner.
429
+ #
430
+ # Draws and fills a rounded rectangle given +point+, +width+ and +height+
431
+ # and +radius+ for the rounded corner. The rectangle is bounded by its
432
+ # upper-left corner.
433
+ #
367
434
  # :call-seq:
368
435
  # fill_rounded_rectangle(point,width,height,radius)
369
436
 
370
437
  ##
371
438
  # :method: stroke_and_fill_rounded_rectangle
372
- # Draws, fills, and strokes a rounded rectangle given +point+, +width+ and +height+ and +radius+ for the rounded corner. The rectangle is bounded by its upper-left corner.
439
+ #
440
+ # Draws, fills, and strokes a rounded rectangle given +point+, +width+ and
441
+ # +height+ and +radius+ for the rounded corner. The rectangle is bounded by
442
+ # its upper-left corner.
443
+ #
373
444
  # :call-seq:
374
445
  # stroke_and_fill_rounded_rectangle(point,width,height,radius)
375
446
 
376
447
  ##
377
448
  # :method: stroke_line
378
- # Strokes a line from one point to another. Points may be specified as tuples or flattened argument list.
449
+ #
450
+ # Strokes a line from one point to another. Points may be specified as
451
+ # tuples or flattened argument list.
452
+ #
379
453
  # :call-seq:
380
454
  # stroke_line(*points)
381
455
 
382
456
  ##
383
457
  # :method: stroke_horizontal_line
384
- # Strokes a horizontal line from +x1+ to +x2+ at the current y position, or the position specified by the :at option.
458
+ #
459
+ # Strokes a horizontal line from +x1+ to +x2+ at the current y position, or
460
+ # the position specified by the :at option.
461
+ #
385
462
  # :call-seq:
386
463
  # stroke_horizontal_line(x1,x2,options={})
387
464
 
388
465
  ##
389
466
  # :method: stroke_horizontal_rule
390
- # Strokes a horizontal line from the left border to the right border of the bounding box at the current y position.
467
+ #
468
+ # Strokes a horizontal line from the left border to the right border of the
469
+ # bounding box at the current y position.
470
+ #
471
+ # :call-seq:
472
+ # stroke_horizontal_rule
391
473
 
392
474
  ##
393
475
  # :method: stroke_vertical_line
476
+ #
394
477
  # Strokes a vertical line at the x coordinate given by :at from y1 to y2.
478
+ #
395
479
  # :call-seq:
396
480
  # stroke_vertical_line(y1,y2,params)
397
481
 
398
482
  ##
399
483
  # :method: stroke_curve
400
- # Strokes a Bezier curve between two points, bounded by two additional points.
484
+ #
485
+ # Strokes a Bezier curve between two points, bounded by two additional
486
+ # points.
487
+ #
401
488
  # :call-seq:
402
489
  # stroke_curve(origin,dest,options={})
403
490
 
404
491
  ##
405
492
  # :method: stroke_circle
406
- # Draws and strokes a circle of radius +radius+ with the centre-point at +point+.
493
+ #
494
+ # Draws and strokes a circle of radius +radius+ with the centre-point at
495
+ # +point+.
496
+ #
407
497
  # :call-seq:
408
498
  # stroke_circle(center,radius)
409
499
 
410
500
  ##
411
501
  # :method: fill_circle
412
- # Draws and fills a circle of radius +radius+ with the centre-point at +point+.
502
+ #
503
+ # Draws and fills a circle of radius +radius+ with the centre-point at
504
+ # +point+.
505
+ #
413
506
  # :call-seq:
414
507
  # fill_circle(center,radius)
415
508
 
416
509
  ##
417
510
  # :method: fill_and_stroke_circle
418
- # Draws, strokes, and fills a circle of radius +radius+ with the centre-point at +point+.
511
+ #
512
+ # Draws, strokes, and fills a circle of radius +radius+ with the
513
+ # centre-point at +point+.
514
+ #
419
515
  # :call-seq:
420
516
  # fill_and_stroke_circle(center,radius)
421
517
 
422
518
  ##
423
519
  # :method: stroke_ellipse
424
- # Draws and strokes an ellipse of x radius +r1+ and y radius +r2+ with the centre-point at +point+.
520
+ #
521
+ # Draws and strokes an ellipse of x radius +r1+ and y radius +r2+ with the
522
+ # centre-point at +point+.
523
+ #
425
524
  # :call-seq:
426
525
  # stroke_ellipse(point, r1, r2 = r1)
427
526
 
428
527
  ##
429
528
  # :method: fill_ellipse
430
- # Draws and fills an ellipse of x radius +r1+ and y radius +r2+ with the centre-point at +point+.
529
+ #
530
+ # Draws and fills an ellipse of x radius +r1+ and y radius +r2+ with the
531
+ # centre-point at +point+.
532
+ #
431
533
  # :call-seq:
432
534
  # fill_ellipse(point, r1, r2 = r1)
433
535
 
434
536
  ##
435
537
  # :method: fill_and_stroke_ellipse
436
- # Draws, strokes, and fills an ellipse of x radius +r1+ and y radius +r2+ with the centre-point at +point+.
538
+ #
539
+ # Draws, strokes, and fills an ellipse of x radius +r1+ and y radius +r2+
540
+ # with the centre-point at +point+.
541
+ #
437
542
  # :call-seq:
438
543
  # fill_and_stroke_ellipse(point, r1, r2 = r1)
439
544
 
440
545
  ##
441
546
  # :method: stroke_polygon
547
+ #
442
548
  # Draws and strokes a polygon from the specified points.
549
+ #
443
550
  # :call-seq:
444
551
  # stroke_polygon(*points)
445
552
 
446
553
  ##
447
554
  # :method: fill_polygon
555
+ #
448
556
  # Draws and fills a polygon from the specified points.
557
+ #
449
558
  # :call-seq:
450
559
  # fill_polygon(*points)
451
560
 
452
561
  ##
453
562
  # :method: fill_and_stroke_polygon
563
+ #
454
564
  # Draws, strokes, and fills a polygon from the specified points.
565
+ #
455
566
  # :call-seq:
456
567
  # fill_and_stroke_polygon(*points)
457
568
 
458
569
  ##
459
570
  # :method: stroke_rounded_polygon
460
- # Draws and strokes a rounded polygon from specified points, using +radius+ to define Bezier curves.
571
+ #
572
+ # Draws and strokes a rounded polygon from specified points, using +radius+
573
+ # to define Bezier curves.
574
+ #
461
575
  # :call-seq:
462
576
  # stroke_rounded_polygon(radius, *points)
463
577
 
464
578
  ##
465
579
  # :method: fill_rounded_polygon
466
- # Draws and fills a rounded polygon from specified points, using +radius+ to define Bezier curves.
580
+ #
581
+ # Draws and fills a rounded polygon from specified points, using +radius+ to
582
+ # define Bezier curves.
583
+ #
467
584
  # :call-seq:
468
585
  # fill_rounded_polygon(radius, *points)
469
586
 
470
587
  ##
471
588
  # :method: fill_and_stroke_rounded_polygon
472
- # Draws, strokes, and fills a rounded polygon from specified points, using +radius+ to define Bezier curves.
589
+ #
590
+ # Draws, strokes, and fills a rounded polygon from specified points, using
591
+ # +radius+ to define Bezier curves.
592
+ #
473
593
  # :call-seq:
474
594
  # fill_and_stroke_rounded_polygon(radius, *points)
475
-
595
+
476
596
  ops = %w{fill stroke fill_and_stroke}
477
597
  shapes = %w{line_to curve_to rectangle rounded_rectangle line horizontal_line horizontal_rule vertical_line
478
598
  curve circle_at circle ellipse_at ellipse polygon rounded_polygon rounded_vertex}
@@ -482,15 +602,15 @@ module Prawn
482
602
  end
483
603
 
484
604
  private
485
-
605
+
486
606
  def current_line_width
487
607
  graphic_state.line_width
488
608
  end
489
-
609
+
490
610
  def current_line_width=(width)
491
611
  graphic_state.line_width = width
492
612
  end
493
-
613
+
494
614
  def write_line_width
495
615
  add_content("#{current_line_width} w")
496
616
  end
@@ -507,7 +627,7 @@ module Prawn
507
627
  def degree_to_rad(angle)
508
628
  angle * Math::PI / 180
509
629
  end
510
-
630
+
511
631
  # Returns the coordinates for a point on a line that is a given distance away from the second
512
632
  # point defining the line segement
513
633
  def point_on_line(distance_from_end, *points)
@@ -518,6 +638,6 @@ module Prawn
518
638
  yr = y0 + p*(y1 - y0)
519
639
  [xr, yr]
520
640
  end
521
-
641
+
522
642
  end
523
643
  end