prune 0.0.4

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 (78) hide show
  1. data/CHANGELOG.ja.txt +16 -0
  2. data/CHANGELOG.txt +16 -0
  3. data/MIT-LICENSE.ja.txt +24 -0
  4. data/MIT-LICENSE.txt +20 -0
  5. data/Manifest.txt +77 -0
  6. data/PostInstall.txt +4 -0
  7. data/README.ja.rdoc +39 -0
  8. data/README.rdoc +39 -0
  9. data/Rakefile +28 -0
  10. data/demo/english_fonts.rb +66 -0
  11. data/demo/japanese_encodings_euc_jp.rb +27 -0
  12. data/demo/japanese_encodings_shift_jis.rb +27 -0
  13. data/demo/japanese_fonts.rb +54 -0
  14. data/demo/tables.rb +52 -0
  15. data/demo/text_decoration.rb +55 -0
  16. data/lib/prune.rb +90 -0
  17. data/lib/prune/constants.rb +39 -0
  18. data/lib/prune/document.rb +101 -0
  19. data/lib/prune/elements/base.rb +55 -0
  20. data/lib/prune/elements/catalog.rb +51 -0
  21. data/lib/prune/elements/font.rb +38 -0
  22. data/lib/prune/elements/font_descriptor.rb +38 -0
  23. data/lib/prune/elements/info.rb +44 -0
  24. data/lib/prune/elements/outlines.rb +19 -0
  25. data/lib/prune/elements/page.rb +115 -0
  26. data/lib/prune/elements/pages.rb +29 -0
  27. data/lib/prune/elements/procedure_sets.rb +22 -0
  28. data/lib/prune/elements/stream.rb +34 -0
  29. data/lib/prune/errors.rb +73 -0
  30. data/lib/prune/fonts/base.rb +92 -0
  31. data/lib/prune/fonts/base_en.rb +38 -0
  32. data/lib/prune/fonts/base_ja.rb +129 -0
  33. data/lib/prune/fonts/en/courier.rb +37 -0
  34. data/lib/prune/fonts/en/helvetica.rb +168 -0
  35. data/lib/prune/fonts/en/symbol.rb +60 -0
  36. data/lib/prune/fonts/en/times_roman.rb +168 -0
  37. data/lib/prune/fonts/en/zapf_dingbats.rb +60 -0
  38. data/lib/prune/fonts/ja/ms_gothic.rb +55 -0
  39. data/lib/prune/fonts/ja/ms_mincho.rb +55 -0
  40. data/lib/prune/fonts/ja/ms_p_gothic.rb +55 -0
  41. data/lib/prune/fonts/ja/ms_p_mincho.rb +55 -0
  42. data/lib/prune/fonts/ja/ms_pr_gothic.rb +55 -0
  43. data/lib/prune/fonts/ja/ms_ui_gothic.rb +55 -0
  44. data/lib/prune/functions.rb +18 -0
  45. data/lib/prune/p_objects/aliases.rb +37 -0
  46. data/lib/prune/p_objects/base.rb +45 -0
  47. data/lib/prune/p_objects/p_array.rb +62 -0
  48. data/lib/prune/p_objects/p_dictionary.rb +83 -0
  49. data/lib/prune/p_objects/p_hexadecimal_string.rb +27 -0
  50. data/lib/prune/p_objects/p_literal_string.rb +25 -0
  51. data/lib/prune/p_objects/p_name.rb +45 -0
  52. data/lib/prune/p_objects/p_stream.rb +29 -0
  53. data/lib/prune/parsers/base.rb +13 -0
  54. data/lib/prune/parsers/document/page/table/tr_parser.rb +77 -0
  55. data/lib/prune/parsers/document/page/table_parser.rb +32 -0
  56. data/lib/prune/parsers/document/page_parser.rb +91 -0
  57. data/lib/prune/parsers/document/property_parser.rb +45 -0
  58. data/lib/prune/parsers/document_parser.rb +26 -0
  59. data/lib/prune/shapes/base.rb +52 -0
  60. data/lib/prune/shapes/line.rb +55 -0
  61. data/lib/prune/shapes/rectangle.rb +64 -0
  62. data/lib/prune/shapes/text_box.rb +223 -0
  63. data/prune.gemspec +37 -0
  64. data/script/console +10 -0
  65. data/script/console.cmd +1 -0
  66. data/script/destroy +14 -0
  67. data/script/destroy.cmd +1 -0
  68. data/script/generate +14 -0
  69. data/script/generate.cmd +1 -0
  70. data/spec/prune/p_objects/p_array_spec.rb +46 -0
  71. data/spec/prune/p_objects/p_dictionary_spec.rb +61 -0
  72. data/spec/prune/p_objects/p_stream_spec.rb +29 -0
  73. data/spec/prune_spec.rb +38 -0
  74. data/spec/spec.opts +1 -0
  75. data/spec/spec_helper.rb +11 -0
  76. data/tasks/prune.rake +38 -0
  77. data/tasks/rspec.rake +21 -0
  78. metadata +181 -0
@@ -0,0 +1,91 @@
1
+ # -*- coding:utf-8 -*-
2
+
3
+ module Prune
4
+ module Parsers
5
+ # Parser for directive "page".
6
+ class PageParser < Base
7
+ attr_reader :page
8
+
9
+ # Initialize.
10
+ def initialize(document, size, options = {})
11
+ @document = document
12
+ # Check document size.
13
+ raise DocumenteSizeError unless DOCUMENT_SIZES.has_key?(size)
14
+ document_size = DOCUMENT_SIZES[size]
15
+ # Create a new page.
16
+ width, height = document_size.collect{|mm| mm_to_pt(mm)}
17
+ @page = Page.new(@document, [0.0, 0.0, width, height], options)
18
+ # Add page to pages.
19
+ @document.pages << @page
20
+ end
21
+
22
+ protected
23
+ # Draw rectangle.
24
+ def rect(x, y, width, height, options = {})
25
+ rect = Shapes::Rectangle.new(@page, [x, y, width, height], options)
26
+ rect.render
27
+ rect
28
+ end
29
+
30
+ # Div tag.
31
+ def div(string, options = {})
32
+ # Convert width and height.
33
+ if options[:width].nil?
34
+ options[:width] = @page.width
35
+ else
36
+ options[:width] = mm_to_pt(options[:width])
37
+ end
38
+ unless options[:height].nil?
39
+ options[:height] = mm_to_pt(options[:height])
40
+ end
41
+ div = Shapes::TextBox.new(@page, string, options)
42
+ div.render
43
+ @page.y -= div.height
44
+ div
45
+ end
46
+
47
+ # Span tag.
48
+ def span(string, options = {})
49
+ # Convert width and height.
50
+ unless options[:width].nil?
51
+ options[:width] = mm_to_pt(options[:width])
52
+ end
53
+ unless options[:height].nil?
54
+ options[:height] = mm_to_pt(options[:height])
55
+ end
56
+ span = Shapes::TextBox.new(@page, string, options)
57
+ span.render
58
+ @page.x += span.width
59
+ span
60
+ end
61
+
62
+ # Br tag.
63
+ def br
64
+ lf
65
+ end
66
+
67
+ # Goto tag(mm).
68
+ def goto(x, y)
69
+ @page.x = mm_to_pt(x)
70
+ @page.y = @page.height - mm_to_pt(y)
71
+ end
72
+
73
+ # Line feed.
74
+ def lf(feed = 1)
75
+ feed = 1 unless feed < 0
76
+ @page.x = @page.margin_left
77
+ font_size = @page.current_font[:size]
78
+ feed.times do
79
+ @page.y -= font_size + (font_size / 5)
80
+ end
81
+ end
82
+
83
+ # Table tag.
84
+ def table(options = {}, &block)
85
+ table_parser = TableParser.new(@page, options)
86
+ table_parser.instance_eval(&block)
87
+ end
88
+ end
89
+ end
90
+ end
91
+
@@ -0,0 +1,45 @@
1
+ # -*- coding:utf-8 -*-
2
+
3
+ module Prune
4
+ module Parsers
5
+ # Parser for directive "property".
6
+ class PropertyParser < Base
7
+ # Initialize.
8
+ def initialize(document)
9
+ @document = document
10
+ end
11
+
12
+ protected
13
+ # Set title.
14
+ def title(title)
15
+ @document.info.title = title
16
+ end
17
+
18
+ # Set subject.
19
+ def subject(subject)
20
+ @document.info.subject = subject
21
+ end
22
+
23
+ # Set author.
24
+ def author(author)
25
+ @document.info.author = author
26
+ end
27
+
28
+ # Set PDF version.
29
+ def version(version)
30
+ valid_versions = %W[1.0 1.1 1.2 1.3 1.4 1.5 1.6 1.7]
31
+ raise PdfVersionError unless valid_versions.include?(version)
32
+ @document.version = version
33
+ @document.catalog.version = version
34
+ end
35
+
36
+ # Set language.
37
+ def language(language)
38
+ valid_langs = %W[en-US ja-JP]
39
+ raise PdfLanguageError unless valid_langs.include?(language)
40
+ @document.catalog.lang = pl(language)
41
+ end
42
+ end
43
+ end
44
+ end
45
+
@@ -0,0 +1,26 @@
1
+ # -*- coding:utf-8 -*-
2
+
3
+ module Prune
4
+ module Parsers
5
+ # Parser for Prune DSL.
6
+ class DocumentParser < Base
7
+ def initialize(document)
8
+ @document = document
9
+ end
10
+
11
+ protected
12
+ def property(&block)
13
+ unless @property_parser
14
+ @property_parser = PropertyParser.new(@document)
15
+ end
16
+ @property_parser.instance_eval(&block)
17
+ end
18
+
19
+ def page(size = :A4, options = {}, &block)
20
+ @page_parser = PageParser.new(@document, size, options)
21
+ @page_parser.instance_eval(&block)
22
+ end
23
+ end
24
+ end
25
+ end
26
+
@@ -0,0 +1,52 @@
1
+ # -*- coding:utf-8 -*-
2
+
3
+ module Prune
4
+ module Shapes
5
+ class Base
6
+ include Errors
7
+ include Functions
8
+
9
+ attr_accessor :id
10
+ attr_accessor :x
11
+ attr_accessor :y
12
+ attr_accessor :width
13
+ attr_accessor :height
14
+
15
+ # Initialize.
16
+ def initialize(page)
17
+ @page = page
18
+ @document = @page.document
19
+ raise DuplicateIdError if @page.shape_ids.include?(id)
20
+ @id = "shape_#{@page.shape_ids.size}".to_sym
21
+ @x = 0.0
22
+ @y = 0.0
23
+ @width = 0.0
24
+ @height = 0.0
25
+ end
26
+
27
+ # Render shapes.
28
+ def render(stream)
29
+ stream.each{|line| @page.stream << line}
30
+ @page.shape_ids << @id
31
+ @page.shapes.update(@id => self)
32
+ self
33
+ end
34
+
35
+ private
36
+ # Convert to String.
37
+ def to_s
38
+ "Shape<#{@id}:#{self.class.to_s}>"
39
+ end
40
+
41
+ # Convert color string.
42
+ def convert_color_str(color_str)
43
+ raise ColorFormatError unless /\A#[0-9a-fA-F]{6}\z/ === color_str
44
+ r = color_str[1, 2].to_i(16) / 255.0
45
+ g = color_str[3, 2].to_i(16) / 255.0
46
+ b = color_str[5, 2].to_i(16) / 255.0
47
+ "%.2f %.2f %.2f" % [r, g, b]
48
+ end
49
+ end
50
+ end
51
+ end
52
+
@@ -0,0 +1,55 @@
1
+ # -*- coding:utf-8 -*-
2
+
3
+ module Prune
4
+ module Shapes
5
+ class Line < Base
6
+ STYLES = {
7
+ :solid => "[] 0 d",
8
+ :dashed => "[5 5] 0 d"
9
+ } unless const_defined?(:STYLES)
10
+
11
+ # Draw line.
12
+ def initialize(page, from_x, from_y, to_x, to_y, options = {})
13
+ super(page)
14
+ # Check arguments.
15
+ raise LineArgumentError unless from_x.is_a?(Numeric)
16
+ raise LineArgumentError unless from_y.is_a?(Numeric)
17
+ raise LineArgumentError unless to_x.is_a?(Numeric)
18
+ raise LineArgumentError unless to_y.is_a?(Numeric)
19
+ # Set instance variables.
20
+ @id = options[:id] if options.key?(:id)
21
+ @x = from_x
22
+ @y = from_y
23
+ @to_x = to_x
24
+ @to_y = to_y
25
+ @width = to_x - from_x
26
+ @height = -(to_y - from_y)
27
+ @options = options
28
+ # Parse options.
29
+ @style = options[:style] || :none
30
+ @width = options[:width] || 1.0
31
+ @color = options[:color] || "#000000"
32
+ end
33
+
34
+ # Render shape.
35
+ def render
36
+ return [] if @style == :none
37
+ return [] if @width <= 0.0
38
+ raise LineStyleError unless STYLES.include?(@style)
39
+ stream = []
40
+ stream << "q"
41
+ stream << "2 J"
42
+ stream << "0 j"
43
+ stream << STYLES[@style]
44
+ stream << "%s RG" % convert_color_str(@color)
45
+ stream << "%.2f w" % width
46
+ stream << "%.2f %.2f m" % [@x, @y]
47
+ stream << "%.2f %.2f l" % [@to_x, @to_y]
48
+ stream << "s"
49
+ stream << "Q"
50
+ super(stream)
51
+ end
52
+ end
53
+ end
54
+ end
55
+
@@ -0,0 +1,64 @@
1
+ # -*- coding:utf-8 -*-
2
+
3
+ module Prune
4
+ module Shapes
5
+ class Rectangle < Base
6
+ STYLES = {
7
+ :solid => "[] 0 d",
8
+ :dashed => "[5 5] 0 d"
9
+ } unless const_defined?(:STYLES)
10
+
11
+ # Draw rectangle.
12
+ def initialize(page, x, y, width, height, options = {})
13
+ super(page)
14
+ # Check arguments.
15
+ raise RectangleArgumentError unless x.is_a?(Numeric)
16
+ raise RectangleArgumentError unless y.is_a?(Numeric)
17
+ raise RectangleArgumentError unless width.is_a?(Numeric)
18
+ raise RectangleArgumentError unless height.is_a?(Numeric)
19
+
20
+ # Set instance variables.
21
+ @id = options[:id] if options.key?(:id)
22
+ @x = x
23
+ @y = y
24
+ @width = width
25
+ @height = height
26
+ @options = options
27
+
28
+ # Parse options.
29
+ @style = options[:style] || :none
30
+ @border_width = options[:width] || 1.0
31
+ @border_color = options[:color] || "#000000"
32
+ @fill = options[:fill] || false
33
+ @fill_color = options[:fill_color] || "#000000"
34
+ end
35
+
36
+ # Render shape.
37
+ def render
38
+ stream = []
39
+ boundary = [@x, @y, @width, -@height]
40
+ stream << "q"
41
+ # Set fill color.
42
+ if @fill
43
+ stream << "%s rg" % convert_color_str(@fill_color)
44
+ stream << "%.2f %.2f %.2f %.2f re" % boundary
45
+ stream << "f"
46
+ end
47
+ # Draw boundary.
48
+ unless @style == :none
49
+ raise RectangleStyleError unless STYLES.include?(@style)
50
+ stream << "2 J"
51
+ stream << "0 j"
52
+ stream << STYLES[@style]
53
+ stream << "%s RG" % convert_color_str(@border_color)
54
+ stream << "%.2f w" % @border_width
55
+ stream << "%.2f %.2f %.2f %.2f re" % boundary
56
+ stream << "s"
57
+ end
58
+ stream << "Q"
59
+ super(stream)
60
+ end
61
+ end
62
+ end
63
+ end
64
+
@@ -0,0 +1,223 @@
1
+ # -*- coding:utf-8 -*-
2
+
3
+ module Prune
4
+ module Shapes
5
+ class TextBox < Base
6
+ attr_accessor :width
7
+ attr_accessor :height
8
+
9
+ # Initialize.
10
+ def initialize(page, string, options = {})
11
+ super(page)
12
+ # Set instance variables.
13
+ @id = options[:id] if options.key?(:id)
14
+ @x = @page.x
15
+ @y = @page.y
16
+ @options = options
17
+ # Erase "\r\n" and "\r" line feeds.
18
+ @string = string.gsub(/\r\n/, "\n").gsub(/\r/, "\n")
19
+ @string << "\n" unless /\n\z/ === @string
20
+ # Parse font options.
21
+ Elements::Page::FONT_OPTIONS.each do |sym|
22
+ @page.current_font[sym] = @page.default_font[sym]
23
+ if options.key?(:font) && options[:font].key?(sym)
24
+ @page.current_font[sym] = options[:font][sym]
25
+ end
26
+ end
27
+ # Get font.
28
+ @font = get_font(@page.current_font[:name],
29
+ :bold => @page.current_font[:bold],
30
+ :italic => @page.current_font[:italic])
31
+ raise FontNotSpecifiedError if @font.nil?
32
+ @font_size = @page.current_font[:size]
33
+ # Text align.
34
+ @text_align = options[:text_align] || :left
35
+ @width = get_width(@string, @font, @font_size, options[:width])
36
+ @height = get_height(@string, @font, @font_size, options[:height])
37
+ end
38
+
39
+ # Render shape.
40
+ def render
41
+ # Define local variables.
42
+ current_x = @x
43
+ current_y = @y
44
+ boundary = [@x, @y, @width, @height]
45
+
46
+ # Render background color.
47
+ render_background_color(boundary)
48
+ # Render borders.
49
+ render_rectangle_border(boundary)
50
+ render_top_border(boundary)
51
+ render_left_border(boundary)
52
+ render_right_border(boundary)
53
+ render_bottom_border(boundary)
54
+
55
+ # Render String.
56
+ stream = []
57
+ current_y -= @font_size
58
+ stream << "q"
59
+ # Set colors.
60
+ stroke_color = @page.current_font[:stroke_color]
61
+ fill_color = @page.current_font[:fill_color]
62
+ mode = font_mode(@page.current_font[:mode])
63
+ stream << "%s RG" % convert_color_str(stroke_color)
64
+ stream << "%s rg" % convert_color_str(fill_color)
65
+
66
+ # Write String.
67
+ x_before_align = current_x
68
+ @string.scan(/([^\n]*)\n/) do |token|
69
+ line = $1
70
+ line_width = @font.width(line, @font_size)
71
+ current_x =
72
+ x_of_text_align(line_width, @width, x_before_align, @text_align)
73
+ # Set to stream.
74
+ if line.size > 0
75
+ stream << "BT"
76
+ stream << "%s %d Tf" % [@font.name, @font_size]
77
+ stream << "%.2f %.2f Td" % [current_x, current_y]
78
+ stream << "%d Tr" % mode
79
+ stream << "%s Tj" % @font.decode(line)
80
+ stream << "ET"
81
+ end
82
+ current_y -= @font_size
83
+ end
84
+ stream << "Q"
85
+ super(stream)
86
+ end
87
+
88
+ private
89
+ # Get font from options.
90
+ def get_font(name, options)
91
+ raise UnexistingFontError if name.nil?
92
+ font_name = name.to_s
93
+ # Font name shold contain only lower case alphabets and underscores.
94
+ raise UnexistingFontError unless
95
+ /\A[a-z][a-z_]*[a-z]\z/ === font_name
96
+ # Font name should contain one underscore at a time.
97
+ raise UnexistingFontError if /_{2,}/ === font_name
98
+ # Constantize font.
99
+ font_class_name = font_name.gsub(/(?:^|_)(.)/){$1.upcase}
100
+ font_class = eval("Fonts::#{font_class_name}") rescue UnexistingFontError
101
+ # Find font instance in document.
102
+ font_key = font_class.key(options)
103
+ if @document.fonts.key?(font_key)
104
+ font = @document.fonts[font_key]
105
+ else
106
+ font = font_class.new(@document, options)
107
+ @document.fonts.update(font_key => font)
108
+ end
109
+ # Set font in page instance.
110
+ @page.set_font(font_key, font)
111
+ font
112
+ end
113
+
114
+ # Render background color.
115
+ def render_background_color(boundary)
116
+ color = @options[:background_color]
117
+ return unless color
118
+ x, y, width, height = boundary
119
+ fill_rect = Rectangle.new(@page, x, y, width, height,
120
+ :fill => true, :fill_color => color)
121
+ fill_rect.render
122
+ end
123
+
124
+ # Render rectangle border.
125
+ def render_rectangle_border(boundary)
126
+ return unless @options[:border]
127
+ return if @options[:border_top]
128
+ return if @options[:border_right]
129
+ return if @options[:border_left]
130
+ return if @options[:border_bottom]
131
+ x, y, width, height = boundary
132
+ border_rect = Rectangle.new(@page, x, y, width, height,
133
+ @options[:border])
134
+ border_rect.render
135
+ end
136
+
137
+ # Render top border.
138
+ def render_top_border(boundary)
139
+ return unless @options[:border_top]
140
+ x, y, width, height = boundary
141
+ line = Line.new(@page, x, y, x + width, y,
142
+ @options[:border_top])
143
+ line.render
144
+ end
145
+
146
+ # Render left border.
147
+ def render_left_border(boundary)
148
+ return unless @options[:border_left]
149
+ x, y, width, height = boundary
150
+ line = Line.new(@page, x, y, x, y - height,
151
+ @options[:border_left])
152
+ line.render
153
+ end
154
+
155
+ # Render right border.
156
+ def render_right_border(boundary)
157
+ return unless @options[:border_right]
158
+ x, y, width, height = boundary
159
+ line = Line.new(@page, x + width, y, x + width, y - height,
160
+ @options[:border_right])
161
+ line.render
162
+ end
163
+
164
+ # Render bottom border.
165
+ def render_bottom_border(boundary)
166
+ return unless @options[:border_bottom]
167
+ x, y, width, height = boundary
168
+ line = Line.new(@page, x, y - height, x + width, y - height,
169
+ @options[:border_bottom])
170
+ line.render
171
+ end
172
+
173
+ # Get width(mm).
174
+ def get_width(string, font, font_size, default_width)
175
+ return default_width unless default_width.nil?
176
+ width = 0.0
177
+ string.scan(/([^\n]*)\n/) do |token|
178
+ token_width = font.width(token[0], font_size)
179
+ width = token_width if token_width > width
180
+ end
181
+ width += font_size / 10.0
182
+ end
183
+
184
+ # Get height(mm).
185
+ def get_height(string, font, font_size, default_height)
186
+ return default_height unless default_height.nil?
187
+ height = string.count("\n") * font_size
188
+ height += font_size / 5.0
189
+ end
190
+
191
+ # Get font mode.
192
+ def font_mode(mode)
193
+ case mode
194
+ when :fill
195
+ 0
196
+ when :stroke
197
+ 1
198
+ when :fill_and_stroke
199
+ 2
200
+ else
201
+ raise FontModeError
202
+ end
203
+ end
204
+
205
+ # Get text align.
206
+ def x_of_text_align(string_width, width, x, text_align)
207
+ return x if text_align == :left
208
+ return x if width.nil?
209
+ return x if width <= string_width
210
+ # Calculate x position.
211
+ case text_align
212
+ when :center
213
+ x + ((width - string_width) / 2.0)
214
+ when :right
215
+ x + (width - string_width)
216
+ else
217
+ raise TextAlignError
218
+ end
219
+ end
220
+ end
221
+ end
222
+ end
223
+