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.
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