prawn-core 0.6.3 → 0.7.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|