prawn 0.13.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.yardopts +9 -0
- data/Gemfile +7 -14
- data/Rakefile +5 -10
- data/data/images/16bit.alpha +0 -0
- data/data/images/16bit.color +0 -0
- data/data/images/dice.alpha +0 -0
- data/data/images/dice.color +0 -0
- data/data/images/page_white_text.alpha +0 -0
- data/data/images/page_white_text.color +0 -0
- data/lib/pdf/core/document_state.rb +3 -2
- data/lib/pdf/core/graphics_state.rb +29 -8
- data/lib/pdf/core/object_store.rb +6 -15
- data/lib/pdf/core/pdf_object.rb +8 -33
- data/lib/prawn/document/bounding_box.rb +11 -0
- data/lib/prawn/document/column_box.rb +12 -4
- data/lib/prawn/document.rb +30 -42
- data/lib/prawn/encoding.rb +1 -2
- data/lib/prawn/font/afm.rb +71 -30
- data/lib/prawn/font/dfont.rb +1 -1
- data/lib/prawn/font/ttf.rb +10 -2
- data/lib/prawn/font.rb +7 -8
- data/lib/prawn/graphics.rb +8 -7
- data/lib/prawn/image_handler.rb +4 -0
- data/lib/prawn/images/jpg.rb +9 -10
- data/lib/prawn/images/png.rb +46 -118
- data/lib/prawn/images.rb +2 -7
- data/lib/prawn/layout.rb +2 -2
- data/lib/prawn/measurement_extensions.rb +1 -1
- data/lib/prawn/table/cells.rb +6 -2
- data/lib/prawn/table/column_width_calculator.rb +55 -0
- data/lib/prawn/table.rb +9 -22
- data/lib/prawn/templates.rb +75 -0
- data/lib/prawn/text/formatted/arranger.rb +1 -5
- data/lib/prawn/text/formatted/box.rb +1 -1
- data/lib/prawn/text/formatted/fragment.rb +5 -11
- data/lib/prawn/text/formatted/line_wrap.rb +4 -25
- data/lib/prawn/text/formatted/wrap.rb +1 -4
- data/lib/prawn.rb +1 -2
- data/manual/document_and_page_options/document_and_page_options.rb +2 -1
- data/manual/document_and_page_options/metadata.rb +3 -3
- data/manual/document_and_page_options/print_scaling.rb +20 -0
- data/manual/example_file.rb +2 -7
- data/manual/manual/cover.rb +3 -2
- data/manual/manual/manual.rb +1 -2
- data/prawn.gemspec +10 -6
- data/spec/bounding_box_spec.rb +12 -0
- data/spec/column_box_spec.rb +32 -0
- data/spec/document_spec.rb +5 -7
- data/spec/extensions/encoding_helpers.rb +2 -3
- data/spec/filters_spec.rb +1 -1
- data/spec/font_spec.rb +3 -2
- data/spec/formatted_text_box_spec.rb +14 -25
- data/spec/graphics_spec.rb +18 -0
- data/spec/image_handler_spec.rb +12 -0
- data/spec/images_spec.rb +2 -6
- data/spec/line_wrap_spec.rb +2 -2
- data/spec/object_store_spec.rb +6 -0
- data/spec/outline_spec.rb +10 -10
- data/spec/png_spec.rb +9 -12
- data/spec/table_spec.rb +55 -2
- data/spec/{template_spec.rb → template_spec_obsolete.rb} +2 -1
- data/spec/text_at_spec.rb +11 -26
- data/spec/text_box_spec.rb +6 -2
- data/spec/text_spec.rb +39 -27
- metadata +58 -38
- data/README.md +0 -109
- data/data/images/16bit.dat +0 -0
- data/data/images/dice.dat +0 -0
- data/data/images/page_white_text.dat +0 -0
- data/lib/prawn/compatibility.rb +0 -91
- data/manual/templates/full_template.rb +0 -25
- data/manual/templates/page_template.rb +0 -48
- data/manual/templates/templates.rb +0 -27
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d83c112e79d7d99ade30524585cf7d7cb7d4de52
|
4
|
+
data.tar.gz: a0143b1b1ebf8db3776daafe04dbceeedb62db6b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6f9ac40edd92c31df1f2a63e73684f983069edcb8f1dd15ffb26f9bcc85256ac4adc0f476aa6ef5b4fa2248318515c56a39dc68e5f2352711e30dd4863aa4989
|
7
|
+
data.tar.gz: 7ecbae3e281cae29a8a7234fc32c0148eaae4d27dabc35cecba01073d28319c5480c50dcc1cce2f1bdb5b1c99b4a11e80168d62b8c7c94faa8a1a1f7995f0043
|
data/.yardopts
ADDED
data/Gemfile
CHANGED
@@ -1,18 +1,11 @@
|
|
1
1
|
source "https://rubygems.org"
|
2
2
|
|
3
|
-
|
4
|
-
gem "pdf-reader", "~> 1.2"
|
5
|
-
gem "ruby-rc4"
|
6
|
-
gem "afm"
|
3
|
+
gemspec
|
7
4
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
gem "pdf-inspector", "~> 1.1.0", :require => "pdf/inspector"
|
15
|
-
gem "rspec"
|
16
|
-
gem "mocha", :require => false
|
17
|
-
gem "rake"
|
5
|
+
if ENV["CI"]
|
6
|
+
platforms :rbx do
|
7
|
+
gem "rubysl-singleton", "~> 2.0"
|
8
|
+
gem "rubysl-digest", "~> 2.0"
|
9
|
+
gem "rubysl-enumerator", "~> 2.0"
|
10
|
+
end
|
18
11
|
end
|
data/Rakefile
CHANGED
@@ -3,7 +3,7 @@ Bundler.setup
|
|
3
3
|
|
4
4
|
require 'rake'
|
5
5
|
require 'rspec/core/rake_task'
|
6
|
-
require '
|
6
|
+
require 'yard'
|
7
7
|
require 'rubygems/package_task'
|
8
8
|
|
9
9
|
task :default => [:spec]
|
@@ -21,16 +21,11 @@ task :stats do
|
|
21
21
|
["Specs", "spec"] ).to_s
|
22
22
|
end
|
23
23
|
|
24
|
-
|
25
|
-
|
26
|
-
rdoc.rdoc_files.include( "README.md",
|
27
|
-
"COPYING",
|
28
|
-
"LICENSE",
|
29
|
-
"lib/" )
|
30
|
-
rdoc.main = "README.md"
|
31
|
-
rdoc.rdoc_dir = "doc/html"
|
32
|
-
rdoc.title = "Prawn Documentation"
|
24
|
+
YARD::Rake::YardocTask.new do |t|
|
25
|
+
t.options = ['--output-dir', 'doc/html']
|
33
26
|
end
|
27
|
+
task :docs => :yard
|
28
|
+
|
34
29
|
|
35
30
|
desc "Generate the 'Prawn by Example' manual"
|
36
31
|
task :manual do
|
data/data/images/16bit.alpha
CHANGED
Binary file
|
Binary file
|
data/data/images/dice.alpha
CHANGED
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -5,10 +5,11 @@ module PDF
|
|
5
5
|
normalize_metadata(options)
|
6
6
|
|
7
7
|
if options[:template]
|
8
|
-
@store = PDF::Core::ObjectStore.new(:template => options[:template]
|
8
|
+
@store = PDF::Core::ObjectStore.new(:template => options[:template],
|
9
|
+
:print_scaling => options[:print_scaling])
|
9
10
|
@store.info.data.merge!(options[:info]) if options[:info]
|
10
11
|
else
|
11
|
-
@store = PDF::Core::ObjectStore.new(:info => options[:info])
|
12
|
+
@store = PDF::Core::ObjectStore.new(:info => options[:info], :print_scaling => options[:print_scaling])
|
12
13
|
end
|
13
14
|
|
14
15
|
@version = 1.3
|
@@ -43,17 +43,23 @@ module PDF
|
|
43
43
|
|
44
44
|
end
|
45
45
|
|
46
|
+
# NOTE: This class may be a good candidate for a copy-on-write hash.
|
46
47
|
class GraphicState
|
47
|
-
attr_accessor :color_space, :dash, :cap_style, :join_style, :line_width,
|
48
|
+
attr_accessor :color_space, :dash, :cap_style, :join_style, :line_width,
|
49
|
+
:fill_color, :stroke_color
|
48
50
|
|
49
51
|
def initialize(previous_state = nil)
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
52
|
+
if previous_state
|
53
|
+
initialize_copy(previous_state)
|
54
|
+
else
|
55
|
+
@color_space = {}
|
56
|
+
@fill_color = "000000"
|
57
|
+
@stroke_color = "000000"
|
58
|
+
@dash = { :dash => nil, :space => nil, :phase => 0 }
|
59
|
+
@cap_style = :butt
|
60
|
+
@join_style = :miter
|
61
|
+
@line_width = 1
|
62
|
+
end
|
57
63
|
end
|
58
64
|
|
59
65
|
def dash_setting
|
@@ -63,6 +69,21 @@ module PDF
|
|
63
69
|
"[#{@dash[:dash]} #{@dash[:space]}] #{@dash[:phase]} d"
|
64
70
|
end
|
65
71
|
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
def initialize_copy(other)
|
76
|
+
# mutable state
|
77
|
+
@color_space = other.color_space.dup
|
78
|
+
@fill_color = other.fill_color.dup
|
79
|
+
@stroke_color = other.stroke_color.dup
|
80
|
+
@dash = other.dash.dup
|
81
|
+
|
82
|
+
# immutable state that doesn't need to be duped
|
83
|
+
@cap_style = other.cap_style
|
84
|
+
@join_style = other.join_style
|
85
|
+
@line_width = other.line_width
|
86
|
+
end
|
66
87
|
end
|
67
88
|
end
|
68
89
|
end
|
@@ -26,6 +26,9 @@ module PDF
|
|
26
26
|
|
27
27
|
@info ||= ref(opts[:info] || {}).identifier
|
28
28
|
@root ||= ref(:Type => :Catalog).identifier
|
29
|
+
if opts[:print_scaling] == :none
|
30
|
+
root.data[:ViewerPreferences] = {:PrintScaling => :None}
|
31
|
+
end
|
29
32
|
if pages.nil?
|
30
33
|
root.data[:Pages] = ref(:Type => :Pages, :Count => 0, :Kids => [])
|
31
34
|
end
|
@@ -299,21 +302,9 @@ module PDF
|
|
299
302
|
end
|
300
303
|
end
|
301
304
|
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
str.unpack("U*")
|
306
|
-
true
|
307
|
-
rescue
|
308
|
-
false
|
309
|
-
end
|
310
|
-
end
|
311
|
-
end
|
312
|
-
ruby_19 do
|
313
|
-
def is_utf8?(str)
|
314
|
-
str.force_encoding("utf-8")
|
315
|
-
str.valid_encoding?
|
316
|
-
end
|
305
|
+
def is_utf8?(str)
|
306
|
+
str.force_encoding(::Encoding::UTF_8)
|
307
|
+
str.valid_encoding?
|
317
308
|
end
|
318
309
|
end
|
319
310
|
end
|
data/lib/pdf/core/pdf_object.rb
CHANGED
@@ -12,40 +12,15 @@ module PDF
|
|
12
12
|
module Core
|
13
13
|
module_function
|
14
14
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
"\xFE\xFF".force_encoding("UTF-16BE") + str.encode("UTF-16BE")
|
19
|
-
end
|
20
|
-
|
21
|
-
# encodes any string into a hex representation. The result is a string
|
22
|
-
# with only 0-9 and a-f characters. That result is valid ASCII so tag
|
23
|
-
# it as such to account for behaviour of different ruby VMs
|
24
|
-
def string_to_hex(str)
|
25
|
-
str.unpack("H*").first.force_encoding("ascii")
|
26
|
-
end
|
27
|
-
else
|
28
|
-
# Ruby 1.8
|
29
|
-
def utf8_to_utf16(str)
|
30
|
-
utf16 = "\xFE\xFF"
|
31
|
-
|
32
|
-
str.codepoints do |cp|
|
33
|
-
if cp < 0x10000 # Basic Multilingual Plane
|
34
|
-
utf16 << [cp].pack("n")
|
35
|
-
else
|
36
|
-
# pull out high/low 10 bits
|
37
|
-
hi, lo = (cp - 0x10000).divmod(2**10)
|
38
|
-
# encode a surrogate pair
|
39
|
-
utf16 << [0xD800 + hi, 0xDC00 + lo].pack("n*")
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
utf16
|
44
|
-
end
|
15
|
+
def utf8_to_utf16(str)
|
16
|
+
"\xFE\xFF".force_encoding(::Encoding::UTF_16BE) + str.encode(::Encoding::UTF_16BE)
|
17
|
+
end
|
45
18
|
|
46
|
-
|
47
|
-
|
48
|
-
|
19
|
+
# encodes any string into a hex representation. The result is a string
|
20
|
+
# with only 0-9 and a-f characters. That result is valid ASCII so tag
|
21
|
+
# it as such to account for behaviour of different ruby VMs
|
22
|
+
def string_to_hex(str)
|
23
|
+
str.unpack("H*").first.force_encoding(::Encoding::US_ASCII)
|
49
24
|
end
|
50
25
|
|
51
26
|
# Serializes Ruby objects to their PDF equivalents. Most primitive objects
|
@@ -188,10 +188,21 @@ module Prawn
|
|
188
188
|
|
189
189
|
parent_box = @bounding_box
|
190
190
|
|
191
|
+
original_ypos = y
|
192
|
+
|
191
193
|
init_block.call(parent_box)
|
192
194
|
|
193
195
|
self.y = @bounding_box.absolute_top
|
194
196
|
user_block.call
|
197
|
+
|
198
|
+
# If the user actions did not modify the y position
|
199
|
+
# restore the original y position before the bounding
|
200
|
+
# box was created.
|
201
|
+
|
202
|
+
if y == @bounding_box.absolute_top
|
203
|
+
self.y = original_ypos
|
204
|
+
end
|
205
|
+
|
195
206
|
unless options[:hold_position] || @bounding_box.stretchy?
|
196
207
|
self.y = @bounding_box.absolute_bottom
|
197
208
|
end
|
@@ -4,7 +4,8 @@
|
|
4
4
|
#
|
5
5
|
# Author Paul Ostazeski.
|
6
6
|
|
7
|
-
|
7
|
+
require_relative "bounding_box"
|
8
|
+
|
8
9
|
module Prawn
|
9
10
|
class Document
|
10
11
|
|
@@ -14,10 +15,13 @@ module Prawn
|
|
14
15
|
# filled.
|
15
16
|
#
|
16
17
|
# column_box accepts the same parameters as bounding_box, as well as the
|
17
|
-
# number of :columns and a :spacer (in points) between columns.
|
18
|
-
#
|
19
|
-
#
|
18
|
+
# number of :columns and a :spacer (in points) between columns. If resetting
|
19
|
+
# the top margin is desired on a new page (e.g. to allow for initial page
|
20
|
+
# wide column titles) the option :reflow_margins => true can be set.
|
20
21
|
#
|
22
|
+
# Defaults are :columns = 3, :spacer = font_size, and
|
23
|
+
# :reflow_margins => false
|
24
|
+
#
|
21
25
|
# Under PDF::Writer, "spacer" was known as "gutter"
|
22
26
|
#
|
23
27
|
def column_box(*args, &block)
|
@@ -51,6 +55,7 @@ module Prawn
|
|
51
55
|
@columns = options[:columns] || 3
|
52
56
|
@spacer = options[:spacer] || @document.font_size
|
53
57
|
@current_column = 0
|
58
|
+
@reflow_margins = options[:reflow_margins]
|
54
59
|
end
|
55
60
|
|
56
61
|
# The column width, not the width of the whole box,
|
@@ -103,6 +108,9 @@ module Prawn
|
|
103
108
|
@current_column = (@current_column + 1) % @columns
|
104
109
|
@document.y = @y
|
105
110
|
if 0 == @current_column
|
111
|
+
if @reflow_margins
|
112
|
+
@y = @parent.absolute_top
|
113
|
+
end
|
106
114
|
@document.start_new_page
|
107
115
|
end
|
108
116
|
end
|
data/lib/prawn/document.rb
CHANGED
@@ -7,12 +7,12 @@
|
|
7
7
|
# This is free software. Please see the LICENSE and COPYING files for details.
|
8
8
|
|
9
9
|
require "stringio"
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
10
|
+
require_relative "document/bounding_box"
|
11
|
+
require_relative "document/column_box"
|
12
|
+
require_relative "document/internals"
|
13
|
+
require_relative "document/span"
|
14
|
+
require_relative "document/snapshot"
|
15
|
+
require_relative "document/graphics_state"
|
16
16
|
|
17
17
|
module Prawn
|
18
18
|
|
@@ -62,6 +62,14 @@ module Prawn
|
|
62
62
|
include Prawn::Stamp
|
63
63
|
include Prawn::SoftMask
|
64
64
|
|
65
|
+
# NOTE: We probably need to rethink the options validation system, but this
|
66
|
+
# constant temporarily allows for extensions to modify the list.
|
67
|
+
|
68
|
+
VALID_OPTIONS = [:page_size, :page_layout, :margin, :left_margin,
|
69
|
+
:right_margin, :top_margin, :bottom_margin, :skip_page_creation,
|
70
|
+
:compress, :skip_encoding, :background, :info,
|
71
|
+
:optimize_objects, :text_formatter, :print_scaling]
|
72
|
+
|
65
73
|
# Any module added to this array will be included into instances of
|
66
74
|
# Prawn::Document at the per-object level. These will also be inherited by
|
67
75
|
# any subclasses.
|
@@ -138,7 +146,6 @@ module Prawn
|
|
138
146
|
# <tt>:background</tt>:: An image path to be used as background on all pages [nil]
|
139
147
|
# <tt>:background_scale</tt>:: Backgound image scale [1] [nil]
|
140
148
|
# <tt>:info</tt>:: Generic hash allowing for custom metadata properties [nil]
|
141
|
-
# <tt>:template</tt>:: The path to an existing PDF file to use as a template [nil]
|
142
149
|
# <tt>:text_formatter</tt>: The text formatter to use for <tt>:inline_format</tt>ted text [Prawn::Text::Formatted::Parser]
|
143
150
|
#
|
144
151
|
# Setting e.g. the :margin to 100 points and the :left_margin to 50 will result in margins
|
@@ -173,10 +180,7 @@ module Prawn
|
|
173
180
|
def initialize(options={},&block)
|
174
181
|
options = options.dup
|
175
182
|
|
176
|
-
Prawn.verify_options
|
177
|
-
:right_margin, :top_margin, :bottom_margin, :skip_page_creation,
|
178
|
-
:compress, :skip_encoding, :background, :info,
|
179
|
-
:optimize_objects, :template, :text_formatter], options
|
183
|
+
Prawn.verify_options VALID_OPTIONS, options
|
180
184
|
|
181
185
|
# need to fix, as the refactoring breaks this
|
182
186
|
# raise NotImplementedError if options[:skip_page_creation]
|
@@ -186,6 +190,8 @@ module Prawn
|
|
186
190
|
@internal_state.populate_pages_from_store(self)
|
187
191
|
min_version(state.store.min_version) if state.store.min_version
|
188
192
|
|
193
|
+
min_version(1.6) if options[:print_scaling] == :none
|
194
|
+
|
189
195
|
@background = options[:background]
|
190
196
|
@background_scale = options[:background_scale] || 1
|
191
197
|
@font_size = 12
|
@@ -200,16 +206,7 @@ module Prawn
|
|
200
206
|
options[:size] = options.delete(:page_size)
|
201
207
|
options[:layout] = options.delete(:page_layout)
|
202
208
|
|
203
|
-
|
204
|
-
fresh_content_streams(options)
|
205
|
-
go_to_page(1)
|
206
|
-
else
|
207
|
-
if options[:skip_page_creation] || options[:template]
|
208
|
-
start_new_page(options.merge(:orphan => true))
|
209
|
-
else
|
210
|
-
start_new_page(options)
|
211
|
-
end
|
212
|
-
end
|
209
|
+
initialize_first_page(options)
|
213
210
|
|
214
211
|
@bounding_box = @margin_box
|
215
212
|
|
@@ -232,6 +229,14 @@ module Prawn
|
|
232
229
|
state.page
|
233
230
|
end
|
234
231
|
|
232
|
+
def initialize_first_page(options)
|
233
|
+
if options[:skip_page_creation]
|
234
|
+
start_new_page(options.merge(:orphan => true))
|
235
|
+
else
|
236
|
+
start_new_page(options)
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
235
240
|
# Creates and advances to a new page in the document.
|
236
241
|
#
|
237
242
|
# Page size, margins, and layout can also be set when generating a
|
@@ -242,14 +247,6 @@ module Prawn
|
|
242
247
|
# pdf.start_new_page(:left_margin => 50, :right_margin => 50)
|
243
248
|
# pdf.start_new_page(:margin => 100)
|
244
249
|
#
|
245
|
-
# A template for a page can be specified by pointing to the path of and existing pdf.
|
246
|
-
# One can also specify which page of the template which defaults otherwise to 1.
|
247
|
-
#
|
248
|
-
# pdf.start_new_page(:template => multipage_template.pdf, :template_page => 2)
|
249
|
-
#
|
250
|
-
# Note: templates get indexed by either the object_id of the filename or stream
|
251
|
-
# entered so that if you reuse the same template multiple times be sure to use the
|
252
|
-
# same instance for more efficient use of resources and smaller rendered pdfs.
|
253
250
|
def start_new_page(options = {})
|
254
251
|
if last_page = state.page
|
255
252
|
last_page_size = last_page.size
|
@@ -266,7 +263,6 @@ module Prawn
|
|
266
263
|
new_graphic_state.color_space = {} if new_graphic_state
|
267
264
|
page_options.merge!(:graphic_state => new_graphic_state)
|
268
265
|
end
|
269
|
-
merge_template_options(page_options, options) if options[:template]
|
270
266
|
|
271
267
|
state.page = PDF::Core::Page.new(self, page_options)
|
272
268
|
|
@@ -279,9 +275,7 @@ module Prawn
|
|
279
275
|
@bounding_box = @margin_box
|
280
276
|
end
|
281
277
|
|
282
|
-
|
283
|
-
use_graphic_settings(options[:template])
|
284
|
-
forget_text_rendering_mode! if options[:template]
|
278
|
+
use_graphic_settings
|
285
279
|
|
286
280
|
unless options[:orphan]
|
287
281
|
state.insert_page(state.page, @page_number)
|
@@ -370,7 +364,7 @@ module Prawn
|
|
370
364
|
render_trailer(output)
|
371
365
|
if output.instance_of?(StringIO)
|
372
366
|
str = output.string
|
373
|
-
str.force_encoding(
|
367
|
+
str.force_encoding(::Encoding::ASCII_8BIT)
|
374
368
|
return str
|
375
369
|
else
|
376
370
|
return nil
|
@@ -382,8 +376,7 @@ module Prawn
|
|
382
376
|
# pdf.render_file "foo.pdf"
|
383
377
|
#
|
384
378
|
def render_file(filename)
|
385
|
-
|
386
|
-
File.open(filename,mode) { |f| render(f) }
|
379
|
+
File.open(filename, "wb") { |f| render(f) }
|
387
380
|
end
|
388
381
|
|
389
382
|
# The bounds method returns the current bounding box you are currently in,
|
@@ -652,13 +645,8 @@ module Prawn
|
|
652
645
|
|
653
646
|
private
|
654
647
|
|
655
|
-
def merge_template_options(page_options, options)
|
656
|
-
object_id = state.store.import_page(options[:template], options[:template_page] || 1)
|
657
|
-
page_options.merge!(:object_id => object_id, :page_template => true)
|
658
|
-
end
|
659
|
-
|
660
648
|
# setting override_settings to true ensures that a new graphic state does not end up using
|
661
|
-
# previous settings
|
649
|
+
# previous settings.
|
662
650
|
def use_graphic_settings(override_settings = false)
|
663
651
|
set_fill_color if current_fill_color != "000000" || override_settings
|
664
652
|
set_stroke_color if current_stroke_color != "000000" || override_settings
|
data/lib/prawn/encoding.rb
CHANGED
@@ -108,8 +108,7 @@ module Prawn
|
|
108
108
|
private
|
109
109
|
|
110
110
|
def load_mapping
|
111
|
-
|
112
|
-
File.open(@mapping_file, mode) do |f|
|
111
|
+
File.open(@mapping_file, "r:BINARY") do |f|
|
113
112
|
f.each do |l|
|
114
113
|
_, single_byte, unicode = *l.match(/([0-9A-Za-z]+);([0-9A-F]{4})/)
|
115
114
|
self.class.mapping["0x#{unicode}".hex] = "0x#{single_byte}".hex if single_byte
|
data/lib/prawn/font/afm.rb
CHANGED
@@ -6,8 +6,7 @@
|
|
6
6
|
#
|
7
7
|
# This is free software. Please see the LICENSE and COPYING files for details.
|
8
8
|
|
9
|
-
|
10
|
-
require 'afm'
|
9
|
+
require_relative '../../prawn/encoding'
|
11
10
|
|
12
11
|
module Prawn
|
13
12
|
class Font
|
@@ -49,21 +48,23 @@ module Prawn
|
|
49
48
|
file_name << ".afm" unless file_name =~ /\.afm$/
|
50
49
|
file_name = file_name[0] == ?/ ? file_name : find_font(file_name)
|
51
50
|
|
52
|
-
font_data = @@font_data[file_name] ||=
|
53
|
-
@
|
54
|
-
@
|
55
|
-
@
|
56
|
-
@
|
51
|
+
font_data = @@font_data[file_name] ||= parse_afm(file_name)
|
52
|
+
@glyph_widths = font_data[:glyph_widths]
|
53
|
+
@glyph_table = font_data[:glyph_table]
|
54
|
+
@bounding_boxes = font_data[:bounding_boxes]
|
55
|
+
@kern_pairs = font_data[:kern_pairs]
|
56
|
+
@kern_pair_table = font_data[:kern_pair_table]
|
57
|
+
@attributes = font_data[:attributes]
|
57
58
|
|
58
|
-
@ascender = @attributes["
|
59
|
-
@descender = @attributes["
|
59
|
+
@ascender = @attributes["ascender"].to_i
|
60
|
+
@descender = @attributes["descender"].to_i
|
60
61
|
@line_gap = Float(bbox[3] - bbox[1]) - (@ascender - @descender)
|
61
62
|
end
|
62
63
|
|
63
64
|
# The font bbox, as an array of integers
|
64
65
|
#
|
65
66
|
def bbox
|
66
|
-
@bbox ||= @attributes['
|
67
|
+
@bbox ||= @attributes['fontbbox'].split(/\s+/).map { |e| Integer(e) }
|
67
68
|
end
|
68
69
|
|
69
70
|
# NOTE: String *must* be encoded as WinAnsi
|
@@ -141,7 +142,7 @@ module Prawn
|
|
141
142
|
end
|
142
143
|
|
143
144
|
def symbolic?
|
144
|
-
attributes["
|
145
|
+
attributes["characterset"] == "Special"
|
145
146
|
end
|
146
147
|
|
147
148
|
def find_font(file)
|
@@ -152,6 +153,61 @@ module Prawn
|
|
152
153
|
self.class.metrics_path.join("\n")
|
153
154
|
end
|
154
155
|
|
156
|
+
def parse_afm(file_name)
|
157
|
+
data = {:glyph_widths => {}, :bounding_boxes => {}, :kern_pairs => {}, :attributes => {}}
|
158
|
+
section = []
|
159
|
+
|
160
|
+
File.foreach(file_name) do |line|
|
161
|
+
case line
|
162
|
+
when /^Start(\w+)/
|
163
|
+
section.push $1
|
164
|
+
next
|
165
|
+
when /^End(\w+)/
|
166
|
+
section.pop
|
167
|
+
next
|
168
|
+
end
|
169
|
+
|
170
|
+
case section
|
171
|
+
when ["FontMetrics", "CharMetrics"]
|
172
|
+
next unless line =~ /^CH?\s/
|
173
|
+
|
174
|
+
name = line[/\bN\s+(\.?\w+)\s*;/, 1]
|
175
|
+
data[:glyph_widths][name] = line[/\bWX\s+(\d+)\s*;/, 1].to_i
|
176
|
+
data[:bounding_boxes][name] = line[/\bB\s+([^;]+);/, 1].to_s.rstrip
|
177
|
+
when ["FontMetrics", "KernData", "KernPairs"]
|
178
|
+
next unless line =~ /^KPX\s+(\.?\w+)\s+(\.?\w+)\s+(-?\d+)/
|
179
|
+
data[:kern_pairs][[$1, $2]] = $3.to_i
|
180
|
+
when ["FontMetrics", "KernData", "TrackKern"],
|
181
|
+
["FontMetrics", "Composites"]
|
182
|
+
next
|
183
|
+
else
|
184
|
+
parse_generic_afm_attribute(line, data)
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
# process data parsed from AFM file to build tables which
|
189
|
+
# will be used when measuring and kerning text
|
190
|
+
data[:glyph_table] = (0..255).map do |i|
|
191
|
+
data[:glyph_widths][Encoding::WinAnsi::CHARACTERS[i]].to_i
|
192
|
+
end
|
193
|
+
|
194
|
+
character_hash = Hash[Encoding::WinAnsi::CHARACTERS.zip((0..Encoding::WinAnsi::CHARACTERS.size).to_a)]
|
195
|
+
data[:kern_pair_table] = data[:kern_pairs].inject({}) do |h,p|
|
196
|
+
h[p[0].map { |n| character_hash[n] }] = p[1]
|
197
|
+
h
|
198
|
+
end
|
199
|
+
|
200
|
+
data.each_value { |hash| hash.freeze }
|
201
|
+
data.freeze
|
202
|
+
end
|
203
|
+
|
204
|
+
def parse_generic_afm_attribute(line, hash)
|
205
|
+
line =~ /(^\w+)\s+(.*)/
|
206
|
+
key, value = $1.to_s.downcase, $2
|
207
|
+
|
208
|
+
hash[:attributes][key] = hash[:attributes][key] ? Array(hash[:attributes][key]) << value : value
|
209
|
+
end
|
210
|
+
|
155
211
|
# converts a string into an array with spacing offsets
|
156
212
|
# bewteen characters that need to be kerned
|
157
213
|
#
|
@@ -172,27 +228,12 @@ module Prawn
|
|
172
228
|
|
173
229
|
kerned.map { |e|
|
174
230
|
e = (Array === e ? e.pack("C*") : e)
|
175
|
-
e.respond_to?(:force_encoding) ? e.force_encoding(
|
231
|
+
e.respond_to?(:force_encoding) ? e.force_encoding(::Encoding::Windows_1252) : e
|
176
232
|
}
|
177
233
|
end
|
178
|
-
|
179
|
-
|
180
|
-
|
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
|
195
|
-
|
234
|
+
|
235
|
+
private
|
236
|
+
|
196
237
|
def unscaled_width_of(string)
|
197
238
|
string.bytes.inject(0) do |s,r|
|
198
239
|
s + @glyph_table[r]
|
data/lib/prawn/font/dfont.rb
CHANGED
data/lib/prawn/font/ttf.rb
CHANGED
@@ -160,7 +160,15 @@ module Prawn
|
|
160
160
|
end
|
161
161
|
|
162
162
|
def normalize_encoding(text)
|
163
|
-
|
163
|
+
begin
|
164
|
+
text.encode(::Encoding::UTF_8)
|
165
|
+
rescue => e
|
166
|
+
puts e
|
167
|
+
raise Prawn::Errors::IncompatibleStringEncoding, "Encoding " +
|
168
|
+
"#{text.encoding} can not be transparently converted to UTF-8. " +
|
169
|
+
"Please ensure the encoding of the string you are attempting " +
|
170
|
+
"to use is set correctly"
|
171
|
+
end
|
164
172
|
end
|
165
173
|
|
166
174
|
def glyph_present?(char)
|
@@ -171,7 +179,7 @@ module Prawn
|
|
171
179
|
# Returns the number of characters in +str+ (a UTF-8-encoded string).
|
172
180
|
#
|
173
181
|
def character_count(str)
|
174
|
-
str.
|
182
|
+
str.length
|
175
183
|
end
|
176
184
|
|
177
185
|
private
|