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.
Files changed (43) hide show
  1. data/Rakefile +1 -1
  2. data/examples/general/context_sensitive_headers.rb +37 -0
  3. data/examples/general/float.rb +11 -0
  4. data/examples/general/repeaters.rb +43 -0
  5. data/examples/m17n/chinese_text_wrapping.rb +1 -3
  6. data/examples/text/font_calculations.rb +6 -6
  7. data/examples/text/text_box.rb +80 -17
  8. data/lib/prawn/core.rb +3 -1
  9. data/lib/prawn/document/bounding_box.rb +9 -0
  10. data/lib/prawn/document/column_box.rb +13 -2
  11. data/lib/prawn/document/internals.rb +21 -3
  12. data/lib/prawn/document/snapshot.rb +7 -2
  13. data/lib/prawn/document/span.rb +3 -3
  14. data/lib/prawn/document.rb +78 -19
  15. data/lib/prawn/font/afm.rb +10 -7
  16. data/lib/prawn/font/ttf.rb +6 -4
  17. data/lib/prawn/font.rb +34 -24
  18. data/lib/prawn/graphics/cap_style.rb +5 -2
  19. data/lib/prawn/graphics/color.rb +117 -57
  20. data/lib/prawn/graphics/dash.rb +4 -2
  21. data/lib/prawn/graphics/join_style.rb +6 -3
  22. data/lib/prawn/graphics/transparency.rb +65 -18
  23. data/lib/prawn/images/jpg.rb +1 -1
  24. data/lib/prawn/images/png.rb +1 -1
  25. data/lib/prawn/object_store.rb +30 -1
  26. data/lib/prawn/reference.rb +25 -3
  27. data/lib/prawn/repeater.rb +117 -0
  28. data/lib/prawn/stamp.rb +102 -40
  29. data/lib/prawn/text/box.rb +344 -0
  30. data/lib/prawn/text.rb +255 -0
  31. data/spec/document_spec.rb +125 -4
  32. data/spec/object_store_spec.rb +33 -0
  33. data/spec/repeater_spec.rb +79 -0
  34. data/spec/stamp_spec.rb +8 -0
  35. data/spec/text_box_spec.rb +282 -69
  36. data/spec/text_spec.rb +49 -29
  37. data/spec/transparency_spec.rb +14 -0
  38. data/vendor/pdf-inspector/lib/pdf/inspector/graphics.rb +2 -2
  39. metadata +158 -155
  40. data/examples/general/measurement_units.pdf +0 -4667
  41. data/lib/prawn/document/text/box.rb +0 -90
  42. data/lib/prawn/document/text/wrapping.rb +0 -62
  43. data/lib/prawn/document/text.rb +0 -184
@@ -1,90 +0,0 @@
1
- # encoding: utf-8
2
-
3
- # text/box.rb : Implements simple text boxes
4
- #
5
- # Copyright September 2008, Gregory Brown. All Rights Reserved.
6
- #
7
- # This is free software. Please see the LICENSE and COPYING files for details.
8
-
9
- module Prawn
10
- class Document
11
-
12
- # Defines an invisible rectangle which you can flow text in. When the
13
- # text overflows the box, you can either display :ellipses, :truncate
14
- # the text, or allow it to overflow the bottom boundary with :expand.
15
- #
16
- # text_box "Oh hai text box. " * 200,
17
- # :width => 300, :height => font.height * 5,
18
- # :overflow => :ellipses,
19
- # :at => [100,bounds.top]
20
- #
21
- def text_box(text,options)
22
- Text::Box.new(text, options.merge(:for => self)).render
23
- end
24
-
25
- module Text
26
- # FIXME: requires documentation
27
- #
28
- class Box #:nodoc:
29
- def initialize(text,options={})
30
- @document = options[:for]
31
- @text = text
32
- @at = options[:at] || [0, @document.y - @document.bounds.absolute_bottom]
33
- @width = options[:width] || @document.bounds.width
34
- @height = options[:height]
35
- @overflow = options[:overflow] || :truncate
36
- end
37
-
38
- attr_reader :text, :width, :height, :overflow
39
-
40
- def render
41
- x,y = @at
42
-
43
- if @overflow == :expand
44
- @text = naive_wrap_text
45
- else
46
- original_y = @document.y
47
- fit_text_to_box
48
- end
49
-
50
- @document.bounding_box([x,@document.bounds.top],
51
- :width => @width, :height => @document.bounds.height) do
52
- @document.y = @document.bounds.absolute_bottom + y
53
- @document.text @text
54
- end
55
-
56
- unless @overflow == :expand
57
- @document.y = y + @document.bounds.absolute_bottom - @height
58
- end
59
-
60
- @excess_text
61
- end
62
-
63
- private
64
-
65
- def fit_text_to_box
66
- text = naive_wrap_text
67
-
68
- max_lines = (@height / @document.font.height).floor
69
-
70
- lines = text.lines.to_a
71
-
72
- if lines.length > max_lines
73
- @text = lines[0...max_lines].join
74
- case(@overflow)
75
- when :ellipses
76
- @text[-3..-1] = "..." if @text.size > 3
77
- end
78
- @excess_text = @document.naive_unwrap(lines[max_lines..-1].join)
79
- else
80
- @excess_text = ""
81
- end
82
- end
83
-
84
- def naive_wrap_text
85
- @document.naive_wrap(@text, @width, @document.font_size)
86
- end
87
- end
88
- end
89
- end
90
- end
@@ -1,62 +0,0 @@
1
- # encoding: utf-8
2
-
3
- # wrapping.rb : Implementation of naive text wrap
4
- #
5
- # Copyright May 2008, Michael Daines. All Rights Reserved.
6
- #
7
- # This is free software. Please see the LICENSE and COPYING files for details.
8
- module Prawn
9
- class Document
10
- module Text
11
- module Wrapping #:nodoc:
12
- ruby_18 { $KCODE="U" }
13
-
14
- # Gets height of text in PDF points at current font size.
15
- # Text +:line_width+ must be specified in PDF points.
16
- #
17
- # If using an AFM, string *must* be encoded as WinAnsi
18
- # (Use normalize_encoding to convert)
19
- #
20
- def height_of(string, line_width, size=font_size)
21
- string = naive_wrap(string, line_width, size)
22
- string.lines.to_a.length * font.height_at(size)
23
- end
24
-
25
- # TODO: Replace with TeX optimal algorithm
26
- def naive_wrap(string, line_width, font_size, options = {})
27
- scan_pattern = options[:mode] == :character ? /./ : /\S+|\s+/
28
-
29
- output = ""
30
- string.lines.each do |line|
31
- accumulated_width = 0
32
- segments = line.scan(scan_pattern)
33
-
34
- segments.each do |segment|
35
- segment_width = width_of(segment, :size => font_size, :kerning => options[:kerning])
36
-
37
- if (accumulated_width + segment_width).round > line_width.round
38
- output = "#{output.sub(/[ \t]*\n?(\n*)\z/, "\n\\1")}"
39
-
40
- if segment =~ /\s/
41
- accumulated_width = 0
42
- else
43
- output << segment
44
- accumulated_width = segment_width
45
- end
46
- else
47
- output << segment
48
- accumulated_width += segment_width
49
- end
50
- end
51
- end
52
-
53
- output
54
- end
55
-
56
- def naive_unwrap(string)
57
- string.gsub(/(\S)\n/, '\1 ')
58
- end
59
- end
60
- end
61
- end
62
- end
@@ -1,184 +0,0 @@
1
- # encoding: utf-8
2
-
3
- # text.rb : Implements PDF text primitives
4
- #
5
- # Copyright May 2008, Gregory Brown. All Rights Reserved.
6
- #
7
- # This is free software. Please see the LICENSE and COPYING files for details.
8
- require "zlib"
9
- require "prawn/document/text/box"
10
- require "prawn/document/text/wrapping"
11
-
12
- module Prawn
13
- class Document
14
- module Text
15
- include Wrapping
16
-
17
- # Draws text on the page. If a point is specified via the +:at+
18
- # option the text will begin exactly at that point, and the string is
19
- # assumed to be pre-formatted to properly fit the page.
20
- #
21
- # pdf.text "Hello World", :at => [100,100]
22
- # pdf.text "Goodbye World", :at => [50,50], :size => 16
23
- #
24
- # When +:at+ is not specified, Prawn attempts to wrap the text to
25
- # fit within your current bounding box (or margin_box if no bounding box
26
- # is being used ). Text will flow onto the next page when it reaches
27
- # the bottom of the bounding box. Text wrap in Prawn does not re-flow
28
- # linebreaks, so if you want fully automated text wrapping, be sure to
29
- # remove newlines before attempting to draw your string.
30
- #
31
- # pdf.text "Will be wrapped when it hits the edge of your bounding box"
32
- # pdf.text "This will be centered", :align => :center
33
- # pdf.text "This will be right aligned", :align => :right
34
- #
35
- # Wrapping is done by splitting words by spaces by default. If your text
36
- # does not contain spaces, you can wrap based on characters instead:
37
- #
38
- # pdf.text "This will be wrapped by character", :wrap => :character
39
- #
40
- # If your font contains kerning pairs data that Prawn can parse, the
41
- # text will be kerned by default. You can disable this feature by passing
42
- # <tt>:kerning => false</tt>.
43
- #
44
- # === Text Positioning Details:
45
- #
46
- # When using the :at parameter, Prawn will position your text by the
47
- # left-most edge of its baseline, and flow along a single line. (This
48
- # means that :align will not work)
49
- #
50
- #
51
- # Otherwise, the text is positioned at font.ascender below the baseline,
52
- # making it easy to use this method within bounding boxes and spans.
53
- #
54
- # == Rotation
55
- #
56
- # Text can be rotated before it is placed on the canvas by specifying the
57
- # +:rotate+ option with a given angle. Rotation occurs counter-clockwise.
58
- #
59
- # == Encoding
60
- #
61
- # Note that strings passed to this function should be encoded as UTF-8.
62
- # If you get unexpected characters appearing in your rendered document,
63
- # check this.
64
- #
65
- # If the current font is a built-in one, although the string must be
66
- # encoded as UTF-8, only characters that are available in WinAnsi
67
- # are allowed.
68
- #
69
- # If an empty box is rendered to your PDF instead of the character you
70
- # wanted it usually means the current font doesn't include that character.
71
- #
72
- def text(text,options={})
73
- # we'll be messing with the strings encoding, don't change the users
74
- # original string
75
- text = text.to_s.dup
76
-
77
- save_font do
78
- options = @text_options.merge(options)
79
- process_text_options(options)
80
-
81
- font.normalize_encoding!(text) unless @skip_encoding
82
-
83
- if options[:at]
84
-
85
- if options[:align]
86
- raise ArgumentError, "The :align option does not work with :at"
87
- end
88
-
89
- x,y = translate(options[:at])
90
- font_size(options[:size]) { add_text_content(text,x,y,options) }
91
- else
92
- if options[:rotate]
93
- raise ArgumentError, "Rotated text may only be used with :at"
94
- end
95
- wrapped_text(text,options)
96
- end
97
- end
98
- end
99
-
100
- private
101
-
102
- def process_text_options(options)
103
- Prawn.verify_options [:style, :kerning, :size, :at, :wrap,
104
- :leading, :align, :rotate, :final_gap ], options
105
-
106
- if options[:style]
107
- raise "Bad font family" unless font.family
108
- font(font.family,:style => options[:style])
109
- end
110
-
111
- unless options.key?(:kerning)
112
- options[:kerning] = font.has_kerning_data?
113
- end
114
-
115
- options[:size] ||= font_size
116
- end
117
-
118
- def move_text_position(dy)
119
- bottom = @bounding_box.stretchy? ? @margin_box.absolute_bottom :
120
- @bounding_box.absolute_bottom
121
-
122
- @bounding_box.move_past_bottom if (y - dy) < bottom
123
-
124
- self.y -= dy
125
- end
126
-
127
- def wrapped_text(text,options)
128
- options[:align] ||= :left
129
-
130
- font_size(options[:size]) do
131
- text = naive_wrap(text, bounds.width, font_size,
132
- :kerning => options[:kerning], :mode => options[:wrap])
133
-
134
- lines = text.lines.to_a
135
- last_gap_before = options.fetch(:final_gap, true) ? lines.length : lines.length-1
136
-
137
- lines.each_with_index do |e,i|
138
- move_text_position(font.ascender)
139
-
140
- line_width = width_of(e, :kerning => options[:kerning])
141
- case(options[:align])
142
- when :left
143
- x = @bounding_box.left_side
144
- when :center
145
- x = @bounding_box.left_side +
146
- (@bounding_box.width - line_width) / 2.0
147
- when :right
148
- x = @bounding_box.right_side - line_width
149
- end
150
-
151
- add_text_content(e,x,y,options)
152
-
153
- if i < last_gap_before
154
- move_text_position(font.line_gap - font.descender)
155
- move_text_position(options[:leading]) if options[:leading]
156
- end
157
- end
158
- end
159
- end
160
-
161
- def add_text_content(text, x, y, options)
162
- chunks = font.encode_text(text,options)
163
-
164
- add_content "\nBT"
165
- if options[:rotate]
166
- rad = options[:rotate].to_i * Math::PI / 180
167
- arr = [ Math.cos(rad), Math.sin(rad), -Math.sin(rad), Math.cos(rad), x, y ]
168
- add_content "%.3f %.3f %.3f %.3f %.3f %.3f Tm" % arr
169
- else
170
- add_content "#{x} #{y} Td"
171
- end
172
-
173
- chunks.each do |(subset, string)|
174
- font.add_to_current_page(subset)
175
- add_content "/#{font.identifier_for(subset)} #{font_size} Tf"
176
-
177
- operation = options[:kerning] && string.is_a?(Array) ? "TJ" : "Tj"
178
- add_content Prawn::PdfObject(string, true) << " " << operation
179
- end
180
- add_content "ET\n"
181
- end
182
- end
183
- end
184
- end