prawn-core 0.6.3 → 0.7.1
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.
- data/Rakefile +1 -1
- data/examples/general/context_sensitive_headers.rb +37 -0
- data/examples/general/float.rb +11 -0
- data/examples/general/repeaters.rb +43 -0
- data/examples/m17n/chinese_text_wrapping.rb +1 -3
- data/examples/text/font_calculations.rb +6 -6
- data/examples/text/text_box.rb +80 -17
- data/lib/prawn/core.rb +3 -1
- data/lib/prawn/document/bounding_box.rb +9 -0
- data/lib/prawn/document/column_box.rb +13 -2
- data/lib/prawn/document/internals.rb +21 -3
- data/lib/prawn/document/snapshot.rb +7 -2
- data/lib/prawn/document/span.rb +3 -3
- data/lib/prawn/document.rb +78 -19
- data/lib/prawn/font/afm.rb +10 -7
- data/lib/prawn/font/ttf.rb +6 -4
- data/lib/prawn/font.rb +34 -24
- data/lib/prawn/graphics/cap_style.rb +5 -2
- data/lib/prawn/graphics/color.rb +117 -57
- data/lib/prawn/graphics/dash.rb +4 -2
- data/lib/prawn/graphics/join_style.rb +6 -3
- data/lib/prawn/graphics/transparency.rb +65 -18
- data/lib/prawn/images/jpg.rb +1 -1
- data/lib/prawn/images/png.rb +1 -1
- data/lib/prawn/object_store.rb +30 -1
- data/lib/prawn/reference.rb +25 -3
- data/lib/prawn/repeater.rb +117 -0
- data/lib/prawn/stamp.rb +102 -40
- data/lib/prawn/text/box.rb +344 -0
- data/lib/prawn/text.rb +255 -0
- data/spec/document_spec.rb +125 -4
- data/spec/object_store_spec.rb +33 -0
- data/spec/repeater_spec.rb +79 -0
- data/spec/stamp_spec.rb +8 -0
- data/spec/text_box_spec.rb +282 -69
- data/spec/text_spec.rb +49 -29
- data/spec/transparency_spec.rb +14 -0
- data/vendor/pdf-inspector/lib/pdf/inspector/graphics.rb +2 -2
- metadata +158 -155
- data/examples/general/measurement_units.pdf +0 -4667
- data/lib/prawn/document/text/box.rb +0 -90
- data/lib/prawn/document/text/wrapping.rb +0 -62
- data/lib/prawn/document/text.rb +0 -184
data/lib/prawn/font/afm.rb
CHANGED
@@ -20,9 +20,9 @@ module Prawn
|
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
|
-
attr_reader :attributes
|
23
|
+
attr_reader :attributes #:nodoc:
|
24
24
|
|
25
|
-
def initialize(document, name, options={})
|
25
|
+
def initialize(document, name, options={}) #:nodoc:
|
26
26
|
unless BUILT_INS.include?(name)
|
27
27
|
raise Prawn::Errors::UnknownFont, "#{name} is not a known font."
|
28
28
|
end
|
@@ -45,15 +45,14 @@ module Prawn
|
|
45
45
|
@line_gap = Float(bbox[3] - bbox[1]) - (@ascender - @descender)
|
46
46
|
end
|
47
47
|
|
48
|
+
# The font bbox, as an array of integers
|
49
|
+
#
|
48
50
|
def bbox
|
49
51
|
@bbox ||= @attributes['fontbbox'].split(/\s+/).map { |e| Integer(e) }
|
50
52
|
end
|
51
53
|
|
52
|
-
#
|
53
|
-
|
54
|
-
# String *must* be encoded as WinAnsi
|
55
|
-
#
|
56
|
-
def compute_width_of(string, options={})
|
54
|
+
# NOTE: String *must* be encoded as WinAnsi
|
55
|
+
def compute_width_of(string, options={}) #:nodoc:
|
57
56
|
scale = (options[:size] || size) / 1000.0
|
58
57
|
|
59
58
|
if options[:kerning]
|
@@ -65,6 +64,8 @@ module Prawn
|
|
65
64
|
end
|
66
65
|
end
|
67
66
|
|
67
|
+
# Returns true if the font has kerning data, false otherwise
|
68
|
+
#
|
68
69
|
def has_kerning_data?
|
69
70
|
@kern_pairs.any?
|
70
71
|
end
|
@@ -72,6 +73,7 @@ module Prawn
|
|
72
73
|
# built-in fonts only work with winansi encoding, so translate the
|
73
74
|
# string. Changes the encoding in-place, so the argument itself
|
74
75
|
# is replaced with a string in WinAnsi encoding.
|
76
|
+
#
|
75
77
|
def normalize_encoding(text)
|
76
78
|
enc = Prawn::Encoding::WinAnsi.new
|
77
79
|
text.unpack("U*").collect { |i| enc[i] }.pack("C*")
|
@@ -88,6 +90,7 @@ module Prawn
|
|
88
90
|
# the string itself (or an array, if kerning is performed).
|
89
91
|
#
|
90
92
|
# The +text+ parameter must be in WinAnsi encoding (cp1252).
|
93
|
+
#
|
91
94
|
def encode_text(text, options={})
|
92
95
|
[[0, options[:kerning] ? kern(text) : text]]
|
93
96
|
end
|
data/lib/prawn/font/ttf.rb
CHANGED
@@ -22,9 +22,8 @@ module Prawn
|
|
22
22
|
@line_gap = Integer(@ttf.line_gap * scale_factor)
|
23
23
|
end
|
24
24
|
|
25
|
-
# +string+ must be UTF8-encoded.
|
26
|
-
|
27
|
-
def compute_width_of(string, options={})
|
25
|
+
# NOTE: +string+ must be UTF8-encoded.
|
26
|
+
def compute_width_of(string, options={}) #:nodoc:
|
28
27
|
scale = (options[:size] || size) / 1000.0
|
29
28
|
if options[:kerning]
|
30
29
|
kern(string).inject(0) do |s,r|
|
@@ -40,11 +39,14 @@ module Prawn
|
|
40
39
|
end * scale
|
41
40
|
end
|
42
41
|
end
|
43
|
-
|
42
|
+
|
43
|
+
# The font bbox, as an array of integers
|
44
|
+
#
|
44
45
|
def bbox
|
45
46
|
@bbox ||= @ttf.bbox.map { |i| Integer(i * scale_factor) }
|
46
47
|
end
|
47
48
|
|
49
|
+
# Returns true if the font has kerning data, false otherwise
|
48
50
|
def has_kerning_data?
|
49
51
|
@has_kerning_data
|
50
52
|
end
|
data/lib/prawn/font.rb
CHANGED
@@ -113,21 +113,18 @@ module Prawn
|
|
113
113
|
# font will be embedded twice. Since we do font subsetting, this double
|
114
114
|
# embedding won't be catastrophic, just annoying.
|
115
115
|
# ++
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
font_registry[key] ||= Font.load(self, name, options.merge(:family => family))
|
129
|
-
end
|
130
|
-
|
116
|
+
def find_font(name, options={}) #:nodoc:
|
117
|
+
if font_families.key?(name)
|
118
|
+
family, name = name, font_families[name][options[:style] || :normal]
|
119
|
+
if name.is_a?(Hash)
|
120
|
+
options = options.merge(name)
|
121
|
+
name = options[:file]
|
122
|
+
end
|
123
|
+
end
|
124
|
+
key = "#{name}:#{options[:font] || 0}"
|
125
|
+
font_registry[key] ||= Font.load(self, name, options.merge(:family => family))
|
126
|
+
end
|
127
|
+
|
131
128
|
# Hash of Font objects keyed by names
|
132
129
|
#
|
133
130
|
def font_registry #:nodoc:
|
@@ -210,6 +207,10 @@ module Prawn
|
|
210
207
|
# The options hash used to initialize the font
|
211
208
|
attr_reader :options
|
212
209
|
|
210
|
+
# Shortcut interface for constructing a font object. Filenames of the form
|
211
|
+
# *.ttf will call Font::TTF.new, *.dfont Font::DFont.new, and anything else
|
212
|
+
# will be passed through to Font::AFM.new()
|
213
|
+
#
|
213
214
|
def self.load(document,name,options={})
|
214
215
|
case name
|
215
216
|
when /\.ttf$/ then TTF.new(document, name, options)
|
@@ -231,26 +232,24 @@ module Prawn
|
|
231
232
|
@references = {}
|
232
233
|
end
|
233
234
|
|
235
|
+
# The size of the font ascender in PDF points
|
236
|
+
#
|
234
237
|
def ascender
|
235
238
|
@ascender / 1000.0 * size
|
236
239
|
end
|
237
240
|
|
241
|
+
# The size of the font descender in PDF points
|
242
|
+
#
|
238
243
|
def descender
|
239
|
-
|
244
|
+
-@descender / 1000.0 * size
|
240
245
|
end
|
241
246
|
|
247
|
+
# The size of the recommended gap between lines of text in PDF points
|
248
|
+
#
|
242
249
|
def line_gap
|
243
250
|
@line_gap / 1000.0 * size
|
244
251
|
end
|
245
252
|
|
246
|
-
def identifier_for(subset)
|
247
|
-
"#{@identifier}.#{subset}"
|
248
|
-
end
|
249
|
-
|
250
|
-
def inspect
|
251
|
-
"#{self.class.name}< #{name}: #{size} >"
|
252
|
-
end
|
253
|
-
|
254
253
|
# Normalizes the encoding of the string to an encoding supported by the
|
255
254
|
# font. The string is expected to be UTF-8 going in. It will be re-encoded
|
256
255
|
# and the new string will be returned. For an in-place (destructive)
|
@@ -261,10 +260,13 @@ module Prawn
|
|
261
260
|
|
262
261
|
# Destructive version of normalize_encoding; normalizes the encoding of a
|
263
262
|
# string in place.
|
263
|
+
#
|
264
264
|
def normalize_encoding!(str)
|
265
265
|
str.replace(normalize_encoding(str))
|
266
266
|
end
|
267
267
|
|
268
|
+
# Gets height of current font in PDF points at the given font size
|
269
|
+
#
|
268
270
|
def height_at(size)
|
269
271
|
@normalized_height ||= (@ascender - @descender + @line_gap) / 1000.0
|
270
272
|
@normalized_height * size
|
@@ -285,6 +287,14 @@ module Prawn
|
|
285
287
|
@document.page_fonts.merge!(identifier_for(subset) => @references[subset])
|
286
288
|
end
|
287
289
|
|
290
|
+
def identifier_for(subset) #:nodoc:
|
291
|
+
"#{@identifier}.#{subset}"
|
292
|
+
end
|
293
|
+
|
294
|
+
def inspect #:nodoc:
|
295
|
+
"#{self.class.name}< #{name}: #{size} >"
|
296
|
+
end
|
297
|
+
|
288
298
|
private
|
289
299
|
|
290
300
|
def size
|
@@ -9,12 +9,15 @@
|
|
9
9
|
module Prawn
|
10
10
|
module Graphics
|
11
11
|
module CapStyle
|
12
|
-
# Sets the cap_style for stroked lines and curves
|
13
|
-
#
|
14
12
|
|
15
13
|
CAP_STYLES = { :butt => 0, :round => 1, :projecting_square => 2 }
|
16
14
|
|
15
|
+
# Sets the cap style for stroked lines and curves
|
16
|
+
#
|
17
17
|
# style is one of :butt, :round, or :projecting_square
|
18
|
+
#
|
19
|
+
# NOTE: If this method is never called, :butt will be used by default.
|
20
|
+
#
|
18
21
|
def cap_style(style=nil)
|
19
22
|
return @cap_style || :butt if style.nil?
|
20
23
|
|
data/lib/prawn/graphics/color.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# encoding: utf-8
|
1
|
+
# encoding: utf-8
|
2
2
|
|
3
3
|
# color.rb : Implements color handling
|
4
4
|
#
|
@@ -8,41 +8,41 @@
|
|
8
8
|
#
|
9
9
|
module Prawn
|
10
10
|
module Graphics
|
11
|
-
module Color
|
12
|
-
|
13
|
-
# Sets the fill color.
|
11
|
+
module Color
|
12
|
+
|
13
|
+
# Sets the fill color.
|
14
14
|
#
|
15
|
-
# If a single argument is provided, it should be a 6 digit HTML color
|
15
|
+
# If a single argument is provided, it should be a 6 digit HTML color
|
16
16
|
# code.
|
17
|
-
#
|
17
|
+
#
|
18
18
|
# pdf.fill_color "f0ffc1"
|
19
19
|
#
|
20
20
|
# If 4 arguments are provided, the color is assumed to be a CMYK value
|
21
21
|
# Values range from 0 - 100.
|
22
|
-
#
|
22
|
+
#
|
23
23
|
# pdf.fill_color 0, 99, 95, 0
|
24
24
|
#
|
25
|
-
def fill_color(*color)
|
26
|
-
return @fill_color if color.empty?
|
27
|
-
@fill_color = process_color(*color)
|
25
|
+
def fill_color(*color)
|
26
|
+
return @fill_color if color.empty?
|
27
|
+
@fill_color = process_color(*color)
|
28
28
|
set_fill_color
|
29
|
-
end
|
29
|
+
end
|
30
30
|
|
31
|
-
alias_method :fill_color=, :fill_color
|
31
|
+
alias_method :fill_color=, :fill_color
|
32
32
|
|
33
33
|
# Sets the line stroking color. 6 digit HTML color codes are used.
|
34
34
|
#
|
35
|
-
# If a single argument is provided, it should be a 6 digit HTML color
|
35
|
+
# If a single argument is provided, it should be a 6 digit HTML color
|
36
36
|
# code.
|
37
|
-
#
|
37
|
+
#
|
38
38
|
# pdf.stroke_color "f0ffc1"
|
39
39
|
#
|
40
40
|
# If 4 arguments are provided, the color is assumed to be a CMYK value
|
41
41
|
# Values range from 0 - 100.
|
42
|
-
#
|
42
|
+
#
|
43
43
|
# pdf.stroke_color 0, 99, 95, 0
|
44
44
|
#
|
45
|
-
def stroke_color(*color)
|
45
|
+
def stroke_color(*color)
|
46
46
|
return @stroke_color if color.empty?
|
47
47
|
@stroke_color = process_color(*color)
|
48
48
|
set_stroke_color
|
@@ -57,30 +57,30 @@ module Prawn
|
|
57
57
|
# fill_and_stroke_some_method(*args) #=> some_method(*args); fill_and_stroke
|
58
58
|
#
|
59
59
|
def method_missing(id,*args,&block)
|
60
|
-
case(id.to_s)
|
60
|
+
case(id.to_s)
|
61
61
|
when /^fill_and_stroke_(.*)/
|
62
62
|
send($1,*args,&block); fill_and_stroke
|
63
63
|
when /^stroke_(.*)/
|
64
|
-
send($1,*args,&block); stroke
|
64
|
+
send($1,*args,&block); stroke
|
65
65
|
when /^fill_(.*)/
|
66
66
|
send($1,*args,&block); fill
|
67
67
|
else
|
68
68
|
super
|
69
69
|
end
|
70
|
-
end
|
71
|
-
|
70
|
+
end
|
71
|
+
|
72
72
|
module_function
|
73
|
-
|
74
|
-
# Converts RGB value array to hex string suitable for use with fill_color
|
75
|
-
# and stroke_color
|
73
|
+
|
74
|
+
# Converts RGB value array to hex string suitable for use with fill_color
|
75
|
+
# and stroke_color
|
76
76
|
#
|
77
77
|
# >> Prawn::Graphics::Color.rgb2hex([255,120,8])
|
78
|
-
# => "ff7808"
|
78
|
+
# => "ff7808"
|
79
79
|
#
|
80
80
|
def rgb2hex(rgb)
|
81
81
|
rgb.map { |e| "%02x" % e }.join
|
82
|
-
end
|
83
|
-
|
82
|
+
end
|
83
|
+
|
84
84
|
# Converts hex string into RGB value array:
|
85
85
|
#
|
86
86
|
# >> Prawn::Graphics::Color.hex2rgb("ff7808")
|
@@ -89,53 +89,113 @@ module Prawn
|
|
89
89
|
def hex2rgb(hex)
|
90
90
|
r,g,b = hex[0..1], hex[2..3], hex[4..5]
|
91
91
|
[r,g,b].map { |e| e.to_i(16) }
|
92
|
-
end
|
93
|
-
|
92
|
+
end
|
93
|
+
|
94
94
|
private
|
95
|
-
|
96
|
-
def process_color(*color)
|
95
|
+
|
96
|
+
def process_color(*color)
|
97
97
|
case(color.size)
|
98
|
-
when 1
|
99
|
-
color[0]
|
98
|
+
when 1
|
99
|
+
color[0]
|
100
100
|
when 4
|
101
101
|
color
|
102
102
|
else
|
103
|
-
raise ArgumentError, 'wrong number of arguments supplied'
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
def set_fill_color
|
108
|
-
case @fill_color
|
109
|
-
when String
|
110
|
-
r,g,b = hex2rgb(@fill_color)
|
111
|
-
add_content "%.3f %.3f %.3f rg" %
|
112
|
-
[r / 255.0, g / 255.0, b / 255.0]
|
113
|
-
when Array
|
114
|
-
c,m,y,k = *@fill_color
|
115
|
-
add_content "%.3f %.3f %.3f %.3f k" %
|
116
|
-
[c / 100.0, m / 100.0, y / 100.0, k / 100.0]
|
103
|
+
raise ArgumentError, 'wrong number of arguments supplied'
|
117
104
|
end
|
118
105
|
end
|
119
106
|
|
120
|
-
def
|
121
|
-
case
|
107
|
+
def color_type(color)
|
108
|
+
case color
|
122
109
|
when String
|
123
|
-
|
124
|
-
add_content "%.3f %.3f %.3f RG" %
|
125
|
-
[r / 255.0, g / 255.0, b / 255.0]
|
110
|
+
:RGB
|
126
111
|
when Array
|
127
|
-
|
128
|
-
|
129
|
-
|
112
|
+
:CMYK
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def normalize_color(color)
|
117
|
+
case color_type(color)
|
118
|
+
when :RGB
|
119
|
+
r,g,b = hex2rgb(color)
|
120
|
+
[r / 255.0, g / 255.0, b / 255.0]
|
121
|
+
when :CMYK
|
122
|
+
c,m,y,k = *color
|
123
|
+
[c / 100.0, m / 100.0, y / 100.0, k / 100.0]
|
130
124
|
end
|
131
|
-
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def color_to_s(color)
|
128
|
+
normalize_color(color).map { |c| '%.3f' % c }.join(' ')
|
129
|
+
end
|
132
130
|
|
133
|
-
def
|
131
|
+
def color_space(color)
|
132
|
+
case color_type(color)
|
133
|
+
when :RGB
|
134
|
+
:DeviceRGB
|
135
|
+
when :CMYK
|
136
|
+
:DeviceCMYK
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
COLOR_SPACES = [:DeviceRGB, :DeviceCMYK, :Pattern]
|
141
|
+
|
142
|
+
def set_color_space(type, color_space)
|
143
|
+
# don't set the same color space again
|
144
|
+
return if @color_space[type] == color_space
|
145
|
+
@color_space[type] = color_space
|
146
|
+
|
147
|
+
unless COLOR_SPACES.include?(color_space)
|
148
|
+
raise ArgumentError, "unknown color space: '#{color_space}'"
|
149
|
+
end
|
150
|
+
|
151
|
+
operator = case type
|
152
|
+
when :fill
|
153
|
+
'cs'
|
154
|
+
when :stroke
|
155
|
+
'CS'
|
156
|
+
else
|
157
|
+
raise ArgumentError, "unknown type '#{type}'"
|
158
|
+
end
|
159
|
+
|
160
|
+
add_content "/#{color_space} #{operator}"
|
161
|
+
end
|
162
|
+
|
163
|
+
def set_color(type, color, options = {})
|
164
|
+
operator = case type
|
165
|
+
when :fill
|
166
|
+
'scn'
|
167
|
+
when :stroke
|
168
|
+
'SCN'
|
169
|
+
else
|
170
|
+
raise ArgumentError, "unknown type '#{type}'"
|
171
|
+
end
|
172
|
+
|
173
|
+
if options[:pattern]
|
174
|
+
set_color_space type, :Pattern
|
175
|
+
add_content "/#{color} #{operator}"
|
176
|
+
else
|
177
|
+
set_color_space type, color_space(color)
|
178
|
+
color = color_to_s(color)
|
179
|
+
add_content "#{color} #{operator}"
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def set_fill_color
|
184
|
+
set_color :fill, @fill_color
|
185
|
+
end
|
186
|
+
|
187
|
+
def set_stroke_color
|
188
|
+
set_color :stroke, @stroke_color
|
189
|
+
end
|
190
|
+
|
191
|
+
def update_colors
|
192
|
+
@color_space = {}
|
134
193
|
@fill_color ||= "000000"
|
135
194
|
@stroke_color ||= "000000"
|
136
195
|
set_fill_color
|
137
196
|
set_stroke_color
|
138
|
-
end
|
197
|
+
end
|
139
198
|
end
|
140
199
|
end
|
141
200
|
end
|
201
|
+
|
data/lib/prawn/graphics/dash.rb
CHANGED
@@ -40,13 +40,15 @@ module Prawn
|
|
40
40
|
|
41
41
|
alias_method :dash=, :dash
|
42
42
|
|
43
|
-
#
|
43
|
+
# Stops dashing, restoring solid stroked lines and curves
|
44
|
+
#
|
44
45
|
def undash
|
45
46
|
@dash = undash_hash
|
46
47
|
write_stroke_dash
|
47
48
|
end
|
48
49
|
|
49
|
-
# Returns
|
50
|
+
# Returns when stroke is dashed, false otherwise
|
51
|
+
#
|
50
52
|
def dashed?
|
51
53
|
dash != undash_hash
|
52
54
|
end
|
@@ -9,12 +9,15 @@
|
|
9
9
|
module Prawn
|
10
10
|
module Graphics
|
11
11
|
module JoinStyle
|
12
|
-
# Sets the join_style for stroked lines and curves
|
13
|
-
#
|
14
|
-
|
15
12
|
JOIN_STYLES = { :miter => 0, :round => 1, :bevel => 2 }
|
16
13
|
|
14
|
+
# Sets the join style for stroked lines and curves
|
15
|
+
#
|
17
16
|
# style is one of :miter, :round, or :bevel
|
17
|
+
#
|
18
|
+
# NOTE: if this method is never called, :miter will be used for join style
|
19
|
+
# throughout the document
|
20
|
+
#
|
18
21
|
def join_style(style=nil)
|
19
22
|
return @join_style || :miter if style.nil?
|
20
23
|
|
@@ -9,35 +9,61 @@
|
|
9
9
|
|
10
10
|
module Prawn
|
11
11
|
module Graphics
|
12
|
+
|
13
|
+
# The Prawn::Transparency module is used to place transparent
|
14
|
+
# content on the page. It has the capacity for separate
|
15
|
+
# transparency values for stroked content and all other content.
|
16
|
+
#
|
17
|
+
# Example:
|
18
|
+
# # both the fill and stroke will be at 50% opacity
|
19
|
+
# pdf.transparent(0.5) do
|
20
|
+
# pdf.text("hello world")
|
21
|
+
# pdf.fill_and_stroke_circle_at([x, y], :radius => 25)
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# # the fill will be at 50% opacity, but the stroke will
|
25
|
+
# # be at 75% opacity
|
26
|
+
# pdf.transparent(0.5, 0.75) do
|
27
|
+
# pdf.text("hello world")
|
28
|
+
# pdf.fill_and_stroke_circle_at([x, y], :radius => 25)
|
29
|
+
# end
|
30
|
+
#
|
12
31
|
module Transparency
|
13
32
|
|
33
|
+
# Sets the <tt>opacity</tt> and <tt>stroke_opacity</tt> for all
|
34
|
+
# the content within the <tt>block</tt>
|
35
|
+
# If <tt>stroke_opacity</tt> is not provided, then it takes on
|
36
|
+
# the same value as <tt>opacity</tt>
|
37
|
+
#
|
38
|
+
# Valid ranges for both paramters are 0.0 to 1.0
|
39
|
+
#
|
40
|
+
# Example:
|
41
|
+
# # both the fill and stroke will be at 50% opacity
|
42
|
+
# pdf.transparent(0.5) do
|
43
|
+
# pdf.text("hello world")
|
44
|
+
# pdf.fill_and_stroke_circle_at([x, y], :radius => 25)
|
45
|
+
# end
|
46
|
+
#
|
47
|
+
# # the fill will be at 50% opacity, but the stroke will
|
48
|
+
# # be at 75% opacity
|
49
|
+
# pdf.transparent(0.5, 0.75) do
|
50
|
+
# pdf.text("hello world")
|
51
|
+
# pdf.fill_and_stroke_circle_at([x, y], :radius => 25)
|
52
|
+
# end
|
53
|
+
#
|
14
54
|
def transparent(opacity, stroke_opacity=opacity, &block)
|
15
55
|
min_version(1.4)
|
16
56
|
|
17
|
-
|
18
|
-
|
19
|
-
if opacity_dictionary_registry[key]
|
20
|
-
opacity_dictionary = opacity_dictionary_registry[key][:obj]
|
21
|
-
opacity_dictionary_name = opacity_dictionary_registry[key][:name]
|
22
|
-
else
|
23
|
-
opacity_dictionary = ref!(:Type => :ExtGState,
|
24
|
-
:CA => stroke_opacity,
|
25
|
-
:ca => opacity
|
26
|
-
)
|
27
|
-
|
28
|
-
opacity_dictionary_name = "Tr#{next_opacity_dictionary_id}"
|
29
|
-
opacity_dictionary_registry[key] = { :name => opacity_dictionary_name,
|
30
|
-
:obj => opacity_dictionary }
|
31
|
-
end
|
32
|
-
|
33
|
-
page_ext_gstates.merge!(opacity_dictionary_name => opacity_dictionary)
|
57
|
+
opacity = [[opacity, 0.0].max, 1.0].min
|
58
|
+
stroke_opacity = [[stroke_opacity, 0.0].max, 1.0].min
|
34
59
|
|
35
60
|
# push a new graphics context onto the graphics context stack
|
36
61
|
add_content "q"
|
37
|
-
add_content "/#{opacity_dictionary_name} gs"
|
62
|
+
add_content "/#{opacity_dictionary_name(opacity, stroke_opacity)} gs"
|
38
63
|
|
39
64
|
yield if block_given?
|
40
65
|
|
66
|
+
# restore the previous graphics context
|
41
67
|
add_content "Q"
|
42
68
|
end
|
43
69
|
|
@@ -51,6 +77,27 @@ module Prawn
|
|
51
77
|
opacity_dictionary_registry.length + 1
|
52
78
|
end
|
53
79
|
|
80
|
+
def opacity_dictionary_name(opacity, stroke_opacity)
|
81
|
+
key = "#{opacity}_#{stroke_opacity}"
|
82
|
+
|
83
|
+
if opacity_dictionary_registry[key]
|
84
|
+
dictionary = opacity_dictionary_registry[key][:obj]
|
85
|
+
dictionary_name = opacity_dictionary_registry[key][:name]
|
86
|
+
else
|
87
|
+
dictionary = ref!(:Type => :ExtGState,
|
88
|
+
:CA => stroke_opacity,
|
89
|
+
:ca => opacity
|
90
|
+
)
|
91
|
+
|
92
|
+
dictionary_name = "Tr#{next_opacity_dictionary_id}"
|
93
|
+
opacity_dictionary_registry[key] = { :name => dictionary_name,
|
94
|
+
:obj => dictionary }
|
95
|
+
end
|
96
|
+
|
97
|
+
page_ext_gstates.merge!(dictionary_name => dictionary)
|
98
|
+
dictionary_name
|
99
|
+
end
|
100
|
+
|
54
101
|
end
|
55
102
|
end
|
56
103
|
end
|
data/lib/prawn/images/jpg.rb
CHANGED
data/lib/prawn/images/png.rb
CHANGED
data/lib/prawn/object_store.rb
CHANGED
@@ -6,7 +6,7 @@
|
|
6
6
|
#
|
7
7
|
# This is free software. Please see the LICENSE and COPYING files for details.
|
8
8
|
module Prawn
|
9
|
-
class ObjectStore
|
9
|
+
class ObjectStore #:nodoc:
|
10
10
|
include Enumerable
|
11
11
|
|
12
12
|
def initialize(info={})
|
@@ -59,5 +59,34 @@ module Prawn
|
|
59
59
|
end
|
60
60
|
alias_method :length, :size
|
61
61
|
|
62
|
+
def compact
|
63
|
+
# Clear live markers
|
64
|
+
each { |o| o.live = false }
|
65
|
+
|
66
|
+
# Recursively mark reachable objects live, starting from the roots
|
67
|
+
# (the only objects referenced in the trailer)
|
68
|
+
root.mark_live
|
69
|
+
info.mark_live
|
70
|
+
|
71
|
+
# Renumber live objects to eliminate gaps (shrink the xref table)
|
72
|
+
if @objects.any?{ |_, o| !o.live }
|
73
|
+
new_id = 1
|
74
|
+
new_objects = {}
|
75
|
+
new_identifiers = []
|
76
|
+
|
77
|
+
each do |obj|
|
78
|
+
if obj.live
|
79
|
+
obj.identifier = new_id
|
80
|
+
new_objects[new_id] = obj
|
81
|
+
new_identifiers << new_id
|
82
|
+
new_id += 1
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
@objects = new_objects
|
87
|
+
@identifiers = new_identifiers
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
62
91
|
end
|
63
92
|
end
|