srdperu-prawn-format 0.1.1.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/lib/prawn/format/effects/link.rb +34 -0
- data/lib/prawn/format/effects/underline.rb +32 -0
- data/lib/prawn/format/instructions/base.rb +62 -0
- data/lib/prawn/format/instructions/tag_close.rb +52 -0
- data/lib/prawn/format/instructions/tag_open.rb +95 -0
- data/lib/prawn/format/instructions/text.rb +89 -0
- data/lib/prawn/format/layout_builder.rb +113 -0
- data/lib/prawn/format/lexer.rb +240 -0
- data/lib/prawn/format/line.rb +99 -0
- data/lib/prawn/format/parser.rb +181 -0
- data/lib/prawn/format/state.rb +189 -0
- data/lib/prawn/format/text_object.rb +107 -0
- data/lib/prawn/format/version.rb +5 -0
- data/lib/prawn/format.rb +229 -0
- metadata +90 -0
@@ -0,0 +1,34 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Prawn
|
4
|
+
module Format
|
5
|
+
module Effects
|
6
|
+
|
7
|
+
class Link
|
8
|
+
def initialize(target, x)
|
9
|
+
@target = target
|
10
|
+
@x = x
|
11
|
+
end
|
12
|
+
|
13
|
+
def finish(document, draw_state)
|
14
|
+
x1 = draw_state[:real_x] + @x
|
15
|
+
x2 = draw_state[:real_x] + draw_state[:dx]
|
16
|
+
y = draw_state[:real_y] + draw_state[:dy]
|
17
|
+
|
18
|
+
rect = [x1, y + draw_state[:line].descent, x2, y + draw_state[:line].ascent]
|
19
|
+
if @target.match(/^#/)
|
20
|
+
document.link_annotation(rect, :Dest => @target.sub(/^#/,""), :Border => [0,0,0])
|
21
|
+
else
|
22
|
+
document.link_annotation(rect, :Border => [0,0,0], :A => { :Type => :Action, :S => :URI, :URI => Prawn::LiteralString.new(@target) } )
|
23
|
+
end #new
|
24
|
+
end
|
25
|
+
|
26
|
+
def wrap(document, draw_state)
|
27
|
+
finish(document, draw_state)
|
28
|
+
@x = 0
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Prawn
|
4
|
+
module Format
|
5
|
+
module Effects
|
6
|
+
|
7
|
+
class Underline
|
8
|
+
def initialize(from, state)
|
9
|
+
@from = from
|
10
|
+
@state = state
|
11
|
+
end
|
12
|
+
|
13
|
+
def finish(document, draw_state)
|
14
|
+
x1 = draw_state[:x] + @from
|
15
|
+
x2 = draw_state[:x] + draw_state[:dx]
|
16
|
+
y = draw_state[:y] + draw_state[:dy] - 2
|
17
|
+
|
18
|
+
document.stroke_color(@state.color)
|
19
|
+
document.move_to(x1, y)
|
20
|
+
document.line_to(x2, y)
|
21
|
+
document.stroke
|
22
|
+
end
|
23
|
+
|
24
|
+
def wrap(document, draw_state)
|
25
|
+
finish(document, draw_state)
|
26
|
+
@from = 0
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Prawn
|
4
|
+
module Format
|
5
|
+
module Instructions
|
6
|
+
|
7
|
+
class Base
|
8
|
+
attr_reader :state, :ascent, :descent
|
9
|
+
|
10
|
+
def initialize(state)
|
11
|
+
@state = state
|
12
|
+
state.document.font_size(state.font_size) do
|
13
|
+
@height = state.font.height
|
14
|
+
@ascent = state.font.ascender
|
15
|
+
@descent = state.font.descender
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def spaces
|
20
|
+
0
|
21
|
+
end
|
22
|
+
|
23
|
+
def width(*args)
|
24
|
+
0
|
25
|
+
end
|
26
|
+
|
27
|
+
def height(*args)
|
28
|
+
@height
|
29
|
+
end
|
30
|
+
|
31
|
+
def break?
|
32
|
+
false
|
33
|
+
end
|
34
|
+
|
35
|
+
def force_break?
|
36
|
+
false
|
37
|
+
end
|
38
|
+
|
39
|
+
def discardable?
|
40
|
+
false
|
41
|
+
end
|
42
|
+
|
43
|
+
def start_verbatim?
|
44
|
+
false
|
45
|
+
end
|
46
|
+
|
47
|
+
def end_verbatim?
|
48
|
+
false
|
49
|
+
end
|
50
|
+
|
51
|
+
def style
|
52
|
+
{}
|
53
|
+
end
|
54
|
+
|
55
|
+
def accumulate(list)
|
56
|
+
list.push(self)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'prawn/format/instructions/base'
|
4
|
+
|
5
|
+
module Prawn
|
6
|
+
module Format
|
7
|
+
module Instructions
|
8
|
+
|
9
|
+
class TagClose < Base
|
10
|
+
def self.close(state, tag, draw_state)
|
11
|
+
closer = new(state, tag)
|
12
|
+
closer.draw(state.document, draw_state)
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_reader :tag
|
16
|
+
|
17
|
+
def initialize(state, tag)
|
18
|
+
super(state)
|
19
|
+
@tag = tag
|
20
|
+
end
|
21
|
+
|
22
|
+
def [](property)
|
23
|
+
@tag[:style][property]
|
24
|
+
end
|
25
|
+
|
26
|
+
def draw(document, draw_state, options={})
|
27
|
+
(@tag[:effects] || []).each do |effect|
|
28
|
+
effect.finish(document, draw_state)
|
29
|
+
draw_state[:pending_effects].delete(effect)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def break?
|
34
|
+
force_break?
|
35
|
+
end
|
36
|
+
|
37
|
+
def style
|
38
|
+
@tag[:style]
|
39
|
+
end
|
40
|
+
|
41
|
+
def force_break?
|
42
|
+
@tag[:style][:display] == :break
|
43
|
+
end
|
44
|
+
|
45
|
+
def end_verbatim?
|
46
|
+
@tag[:style][:white_space] == :pre
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'prawn/format/instructions/base'
|
4
|
+
require 'prawn/format/effects/link'
|
5
|
+
require 'prawn/format/effects/underline'
|
6
|
+
|
7
|
+
module Prawn
|
8
|
+
module Format
|
9
|
+
module Instructions
|
10
|
+
|
11
|
+
class TagOpen < Base
|
12
|
+
attr_reader :tag
|
13
|
+
|
14
|
+
def initialize(state, tag)
|
15
|
+
super(state)
|
16
|
+
@tag = tag
|
17
|
+
end
|
18
|
+
|
19
|
+
def draw(document, draw_state, options={})
|
20
|
+
draw_width(document, draw_state)
|
21
|
+
draw_destination(document, draw_state)
|
22
|
+
draw_link(document, draw_state)
|
23
|
+
draw_underline(document, draw_state)
|
24
|
+
end
|
25
|
+
|
26
|
+
def start_verbatim?
|
27
|
+
@tag[:style][:white_space] == :pre
|
28
|
+
end
|
29
|
+
|
30
|
+
def style
|
31
|
+
@tag[:style]
|
32
|
+
end
|
33
|
+
|
34
|
+
def width
|
35
|
+
@state.width
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def draw_width(document, draw_state)
|
41
|
+
if width > 0
|
42
|
+
draw_state[:dx] += width
|
43
|
+
draw_state[:text].move_to(draw_state[:dx], draw_state[:dy])
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def draw_destination(document, draw_state)
|
48
|
+
return unless tag[:style][:anchor]
|
49
|
+
|
50
|
+
x = draw_state[:real_x]
|
51
|
+
y = draw_state[:real_y] + draw_state[:dy] + ascent
|
52
|
+
|
53
|
+
label, destination = case tag[:style][:anchor]
|
54
|
+
when /^zoom=([\d\.]+):(.*)$/
|
55
|
+
[$2, document.dest_xyz(x, y, $1.to_f)]
|
56
|
+
when /^fit:(.*)$/
|
57
|
+
[$1, document.dest_fit]
|
58
|
+
when /^fith:(.*)$/
|
59
|
+
[$1, document.dest_fit_horizontally(y)]
|
60
|
+
when /^fitv:(.*)$/
|
61
|
+
[$1, document.dest_fit_vertically(x)]
|
62
|
+
when /^fitb:(.*)$/
|
63
|
+
[$1, document.dest_fit_bounds]
|
64
|
+
when /^fitbh:(.*)$/
|
65
|
+
[$1, document.dest_fit_bounds_horizontally(y)]
|
66
|
+
when /^fitbv:(.*)$/
|
67
|
+
[$1, document.dest_fit_bounds_vertically(x)]
|
68
|
+
else
|
69
|
+
[tag[:style][:anchor], document.dest_fit_bounds]
|
70
|
+
end
|
71
|
+
|
72
|
+
document.add_dest(label, destination)
|
73
|
+
end
|
74
|
+
|
75
|
+
def draw_link(document, draw_state)
|
76
|
+
return unless tag[:style][:target]
|
77
|
+
add_effect(Effects::Link.new(tag[:style][:target], draw_state[:dx]), draw_state)
|
78
|
+
end
|
79
|
+
|
80
|
+
def draw_underline(document, draw_state)
|
81
|
+
return unless tag[:style][:text_decoration] == :underline
|
82
|
+
add_effect(Effects::Underline.new(draw_state[:dx], @state), draw_state)
|
83
|
+
end
|
84
|
+
|
85
|
+
def add_effect(effect, draw_state)
|
86
|
+
tag[:effects] ||= []
|
87
|
+
tag[:effects].push(effect)
|
88
|
+
|
89
|
+
draw_state[:pending_effects].push(effect)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'prawn/format/instructions/base'
|
4
|
+
|
5
|
+
module Prawn
|
6
|
+
module Format
|
7
|
+
module Instructions
|
8
|
+
|
9
|
+
class Text < Base
|
10
|
+
attr_reader :text
|
11
|
+
|
12
|
+
def initialize(state, text, options={})
|
13
|
+
super(state)
|
14
|
+
@text = text
|
15
|
+
@break = options.key?(:break) ? options[:break] : text.index(/[-\xE2\x80\x94\s]/)
|
16
|
+
@discardable = options.key?(:discardable) ? options[:discardable] : text.index(/\s/)
|
17
|
+
state.font.normalize_encoding(@text) if options.fetch(:normalize, true)
|
18
|
+
end
|
19
|
+
|
20
|
+
def dup
|
21
|
+
self.class.new(state, @text.dup, :normalize => false,
|
22
|
+
:break => @break, :discardable => @discardable)
|
23
|
+
end
|
24
|
+
|
25
|
+
def accumulate(list)
|
26
|
+
if list.last.is_a?(Text) && list.last.state == state
|
27
|
+
list.last.text << @text
|
28
|
+
else
|
29
|
+
list.push(dup)
|
30
|
+
end
|
31
|
+
|
32
|
+
return list
|
33
|
+
end
|
34
|
+
|
35
|
+
def spaces
|
36
|
+
@spaces ||= @text.scan(/ /).length
|
37
|
+
end
|
38
|
+
|
39
|
+
def height(ignore_discardable=false)
|
40
|
+
if ignore_discardable && discardable?
|
41
|
+
0
|
42
|
+
else
|
43
|
+
@height
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def break?
|
48
|
+
@break
|
49
|
+
end
|
50
|
+
|
51
|
+
def discardable?
|
52
|
+
@discardable
|
53
|
+
end
|
54
|
+
|
55
|
+
def compatible?(with)
|
56
|
+
with.is_a?(self.class) && with.state == state
|
57
|
+
end
|
58
|
+
|
59
|
+
def width(type=:all)
|
60
|
+
@width ||= @state.font.compute_width_of(@text, :size => @state.font_size, :kerning => @state.kerning?)
|
61
|
+
|
62
|
+
case type
|
63
|
+
when :discardable then discardable? ? @width : 0
|
64
|
+
when :nondiscardable then discardable? ? 0 : @width
|
65
|
+
else @width
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def to_s
|
70
|
+
@text
|
71
|
+
end
|
72
|
+
|
73
|
+
def draw(document, draw_state, options={})
|
74
|
+
@state.apply!(draw_state[:text], draw_state[:cookies])
|
75
|
+
|
76
|
+
encoded_text = @state.font.encode_text(@text, :kerning => @state.kerning?)
|
77
|
+
encoded_text.each do |subset, chunk|
|
78
|
+
@state.apply_font!(draw_state[:text], draw_state[:cookies], subset)
|
79
|
+
draw_state[:text].show(chunk)
|
80
|
+
end
|
81
|
+
draw_state[:dx] += width
|
82
|
+
|
83
|
+
draw_state[:dx] += draw_state[:padding] * spaces if draw_state[:padding]
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'prawn/format/line'
|
4
|
+
require 'prawn/format/parser'
|
5
|
+
|
6
|
+
module Prawn
|
7
|
+
module Format
|
8
|
+
class LayoutBuilder
|
9
|
+
attr_reader :document, :options
|
10
|
+
|
11
|
+
def initialize(document, text, options={})
|
12
|
+
@document = document
|
13
|
+
@options = options
|
14
|
+
@tags = document.tags.merge(options[:tags] || {})
|
15
|
+
@styles = document.styles.merge(options[:styles] || {})
|
16
|
+
style = document.default_style.merge(options[:default_style] || {})
|
17
|
+
|
18
|
+
translate_prawn_options(style, options)
|
19
|
+
|
20
|
+
@parser = Parser.new(@document, text,
|
21
|
+
:tags => @tags, :styles => @styles, :style => style)
|
22
|
+
|
23
|
+
@state = {}
|
24
|
+
end
|
25
|
+
|
26
|
+
def done?
|
27
|
+
@parser.eos?
|
28
|
+
end
|
29
|
+
|
30
|
+
def word_wrap(width, options={}, &block)
|
31
|
+
if options[:height] && block
|
32
|
+
raise ArgumentError, "cannot specify both height and a block"
|
33
|
+
elsif options[:height]
|
34
|
+
block = Proc.new { |l, h| h > options[:height] }
|
35
|
+
elsif block.nil?
|
36
|
+
block = Proc.new { |l, h| false }
|
37
|
+
end
|
38
|
+
|
39
|
+
lines = []
|
40
|
+
total_height = 0
|
41
|
+
|
42
|
+
while (line = self.next(width))
|
43
|
+
if block[line, total_height + line.height]
|
44
|
+
unget(line)
|
45
|
+
break
|
46
|
+
end
|
47
|
+
|
48
|
+
total_height += line.height
|
49
|
+
lines.push(line)
|
50
|
+
end
|
51
|
+
|
52
|
+
return lines
|
53
|
+
end
|
54
|
+
|
55
|
+
def fill(x, y, width, fill_options={}, &block)
|
56
|
+
lines = word_wrap(width, fill_options, &block)
|
57
|
+
draw_options = options.merge(fill_options).merge(:state => @state)
|
58
|
+
@state = document.draw_lines(x, y, width, lines, draw_options)
|
59
|
+
@state.delete(:cookies)
|
60
|
+
return @state[:dy] + y
|
61
|
+
end
|
62
|
+
|
63
|
+
def next(line_width=nil)
|
64
|
+
line = []
|
65
|
+
width = 0
|
66
|
+
break_at = nil
|
67
|
+
|
68
|
+
while (instruction = @parser.next)
|
69
|
+
next if !@parser.verbatim? && line.empty? && instruction.discardable? # ignore discardables at line start
|
70
|
+
line.push(instruction)
|
71
|
+
|
72
|
+
if instruction.break?
|
73
|
+
width += instruction.width(:nondiscardable)
|
74
|
+
break_at = line.length if line_width && width <= line_width
|
75
|
+
width += instruction.width(:discardable)
|
76
|
+
else
|
77
|
+
width += instruction.width
|
78
|
+
end
|
79
|
+
|
80
|
+
if instruction.force_break? || line_width && width >= line_width
|
81
|
+
break_at ||= line.length
|
82
|
+
|
83
|
+
@parser.push(line.pop) while line.length > break_at
|
84
|
+
hard_break = instruction.force_break? || @parser.eos?
|
85
|
+
|
86
|
+
return Line.new(line, hard_break)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
Line.new(line, true) if line.any?
|
91
|
+
end
|
92
|
+
|
93
|
+
def unget(line)
|
94
|
+
line.source.reverse_each { |instruction| @parser.push(instruction) }
|
95
|
+
end
|
96
|
+
|
97
|
+
def translate_prawn_options(style, options)
|
98
|
+
style[:kerning] = options[:kerning] if options.key?(:kerning)
|
99
|
+
style[:font_size] = options[:size] if options.key?(:size)
|
100
|
+
|
101
|
+
case options[:style]
|
102
|
+
when :bold then
|
103
|
+
style[:font_weight] = :bold
|
104
|
+
when :italic then
|
105
|
+
style[:font_style] = :italic
|
106
|
+
when :bold_italic then
|
107
|
+
style[:font_weight] = :bold
|
108
|
+
style[:font_style] = :italic
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|