prawn-manual_builder 0.3.1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,394 +0,0 @@
1
- # encoding: UTF-8
2
-
3
- module Prawn
4
- module ManualBuilder
5
- # The Prawn::ManualBuilder::Example class holds all the helper methods
6
- # used to generate manuals.
7
- #
8
- # The overall structure is to have single example files grouped by package
9
- # folders. Each package has a package builder file (with the same name as the
10
- # package folder) that defines the inner structure of subsections and
11
- # examples. The manual is then built by loading all the packages and some
12
- # standalone pages.
13
- #
14
- # To see one of the examples check manual/basic_concepts/cursor.rb
15
- #
16
- # To see one of the package builders check
17
- # manual/basic_concepts/basic_concepts.rb
18
- #
19
- # To see how the manual is built check manual/manual/manual.rb (Yes that's a
20
- # whole load of manuals)
21
- class Example < Prawn::ManualBuilder.document_class
22
-
23
- # Values used for the manual design:
24
-
25
- # This is the default value for the margin box
26
- #
27
- BOX_MARGIN = 36
28
-
29
- # Additional indentation to keep the line measure with a reasonable size
30
- #
31
- INNER_MARGIN = 30
32
-
33
- # Vertical Rhythm settings
34
- #
35
- RHYTHM = 10
36
- LEADING = 2
37
-
38
- # Colors
39
- #
40
- BLACK = "000000"
41
- LIGHT_GRAY = "F2F2F2"
42
- GRAY = "DDDDDD"
43
- DARK_GRAY = "333333"
44
- BROWN = "A4441C"
45
- ORANGE = "F28157"
46
- LIGHT_GOLD = "FBFBBE"
47
- DARK_GOLD = "EBE389"
48
- BLUE = "0000D0"
49
-
50
- # Used to generate the url for the example files
51
- #
52
- MANUAL_URL = "http://github.com/prawnpdf/prawn/tree/master/manual"
53
-
54
-
55
- # Loads a package. Used on the manual.
56
- #
57
- def load_package(package)
58
- load_file(package, package)
59
- end
60
-
61
- # Loads a page with outline support. Used on the manual.
62
- #
63
- def load_page(package, page)
64
- load_file(package, page)
65
-
66
- outline.define do
67
- section(page.gsub("_", " ").capitalize, :destination => page_number)
68
- end
69
- end
70
-
71
- # Opens a file in a given package and evals the source
72
- #
73
- def load_file(package, file)
74
- start_new_page
75
- example = ExampleFile.new(package, "#{file}.rb")
76
- eval example.generate_block_source, nil, File.join(Prawn::ManualBuilder.manual_dir, example.parent_folder_name, example.filename), example.generated_block_line
77
- end
78
-
79
-
80
- # Creates a new ExamplePackage object and yields it to a block in order for
81
- # it to be populated with examples, sections and some introduction text.
82
- # Used on the package files.
83
- #
84
- def package(package, &block)
85
- ep = ExamplePackage.new(package)
86
- ep.instance_eval(&block)
87
- ep.render(self)
88
- end
89
-
90
- # Renders an ExamplePackage cover page.
91
- #
92
- # Starts a new page and renders the package introduction text.
93
- #
94
- def render_package_cover(package)
95
- header(package.name)
96
- instance_eval &(package.intro_block)
97
-
98
- outline.define do
99
- section(package.name, :destination => page_number, :closed => true)
100
- end
101
- end
102
-
103
- # Add the ExampleSection to the document outline within the appropriate
104
- # package.
105
- #
106
- def render_section(section)
107
- outline.add_subsection_to(section.package_name) do
108
- outline.section(section.name, :closed => true)
109
- end
110
- end
111
-
112
- # Renders an ExampleFile.
113
- #
114
- # Starts a new page and renders an introductory text, the example source and
115
- # evaluates the example source inline whenever that is appropriate according
116
- # to the ExampleFile directives.
117
- #
118
- def render_example(example)
119
- start_new_page
120
-
121
- outline.add_subsection_to(example.parent_name) do
122
- outline.page(:destination => page_number, :title => example.name)
123
- end
124
-
125
- example_header(example.parent_folder_name, example.filename)
126
-
127
- prose(example.introduction_text)
128
-
129
- code(example.source)
130
-
131
- if example.eval?
132
- eval_code(example)
133
- else
134
- source_link(example)
135
- end
136
-
137
- reset_settings
138
- end
139
-
140
- # Render the example header. Used on the example pages of the manual
141
- #
142
- def example_header(package, example)
143
- header_box do
144
- register_fonts
145
- font('DejaVu', :size => 18) do
146
- formatted_text([ { :text => package, :color => BROWN },
147
- { :text => "/", :color => BROWN },
148
- { :text => example, :color => ORANGE }
149
- ], :valign => :center)
150
- end
151
- end
152
- end
153
-
154
- # Register fonts used on the manual
155
- #
156
- def register_fonts
157
- kai_file = "#{Prawn::ManualBuilder::DATADIR}/fonts/gkai00mp.ttf"
158
- font_families["Kai"] = {
159
- :normal => { :file => kai_file, :font => "Kai" }
160
- }
161
-
162
- dejavu_file = "#{Prawn::ManualBuilder::DATADIR}/fonts/DejaVuSans.ttf"
163
- font_families["DejaVu"] = {
164
- :normal => { :file => dejavu_file, :font => "DejaVu" }
165
- }
166
- end
167
-
168
- # Render a block of text after processing code tags and URLs to be used with
169
- # the inline_format option.
170
- #
171
- # Used on the introducory text for example pages of the manual and on
172
- # package pages intro
173
- #
174
- def prose(str)
175
-
176
- # Process the <code> tags
177
- str.gsub!(/<code>([^<]+?)<\/code>/,
178
- "<font name='Courier'><b>\\1<\/b><\/font>")
179
-
180
- # Process the links
181
- str.gsub!(/(https?:\/\/\S+)/,
182
- "<color rgb='#{BLUE}'><link href=\"\\1\">\\1</link></color>")
183
-
184
- inner_box do
185
- font("Helvetica", :size => 11) do
186
- str.split(/\n\n+/).each do |paragraph|
187
-
188
- text(paragraph.gsub(/\s+/," "),
189
- :align => :justify,
190
- :inline_format => true,
191
- :leading => LEADING,
192
- :color => DARK_GRAY)
193
-
194
- move_down(RHYTHM)
195
- end
196
- end
197
- end
198
-
199
- move_down(RHYTHM)
200
- end
201
-
202
- # Render a code block. Used on the example pages of the manual
203
- #
204
- def code(str)
205
- pre_text = str.gsub(' ', Prawn::Text::NBSP)
206
- pre_text = ::CodeRay.scan(pre_text, :ruby).to_prawn
207
-
208
- font('Courier', :size => 9.5) do
209
- colored_box(pre_text, :fill_color => DARK_GRAY)
210
- end
211
- end
212
-
213
- # Renders a dashed line and evaluates the code inline
214
- #
215
- def eval_code(example)
216
- source = example.source
217
- move_down(RHYTHM)
218
-
219
- dash(3)
220
- stroke_color(BROWN)
221
- stroke_horizontal_line(-BOX_MARGIN, bounds.width + BOX_MARGIN)
222
- stroke_color(BLACK)
223
- undash
224
-
225
- move_down(RHYTHM*3)
226
- begin
227
- eval source, nil, File.join(Prawn::ManualBuilder.manual_dir, example.parent_folder_name, example.filename), example.generated_block_line
228
- rescue => e
229
- puts "Error evaluating example: #{e.message}"
230
- puts
231
- puts "---- Source: ----"
232
- puts source
233
- end
234
- end
235
-
236
- # Renders a box with the link for the example file
237
- #
238
- def source_link(example)
239
- url = "#{MANUAL_URL}/#{example.parent_folder_name}/#{example.filename}"
240
-
241
- reason = [{ :text => "This code snippet was not evaluated inline. " +
242
- "You may see its output by running the " +
243
- "example file located here:\n",
244
- :color => DARK_GRAY },
245
-
246
- { :text => url,
247
- :color => BLUE,
248
- :link => url}
249
- ]
250
-
251
- font('Helvetica', :size => 9) do
252
- colored_box(reason,
253
- :fill_color => LIGHT_GOLD,
254
- :stroke_color => DARK_GOLD,
255
- :leading => LEADING*3)
256
- end
257
- end
258
-
259
- # Render a page header. Used on the manual lone pages and package
260
- # introductory pages
261
- #
262
- def header(str)
263
- header_box do
264
- register_fonts
265
- font('DejaVu', :size => 24) do
266
- text(str, :color => BROWN, :valign => :center)
267
- end
268
- end
269
- end
270
-
271
- # Render the arguments as a bulleted list. Used on the manual package
272
- # introductory pages
273
- #
274
- def list(*items)
275
- move_up(RHYTHM)
276
-
277
- inner_box do
278
- font("Helvetica", :size => 11) do
279
- items.each do |li|
280
- float { text("•", :color => DARK_GRAY) }
281
- indent(RHYTHM) do
282
- text(li.gsub(/\s+/," "),
283
- :inline_format => true,
284
- :color => DARK_GRAY,
285
- :leading => LEADING)
286
- end
287
-
288
- move_down(RHYTHM)
289
- end
290
- end
291
- end
292
- end
293
-
294
- # Renders the page-wide headers
295
- #
296
- def header_box(&block)
297
- bounding_box([-bounds.absolute_left, cursor + BOX_MARGIN],
298
- :width => bounds.absolute_left + bounds.absolute_right,
299
- :height => BOX_MARGIN*2 + RHYTHM*2) do
300
-
301
- fill_color LIGHT_GRAY
302
- fill_rectangle([bounds.left, bounds.top],
303
- bounds.right,
304
- bounds.top - bounds.bottom)
305
- fill_color BLACK
306
-
307
- indent(BOX_MARGIN + INNER_MARGIN, &block)
308
- end
309
-
310
- stroke_color GRAY
311
- stroke_horizontal_line(-BOX_MARGIN, bounds.width + BOX_MARGIN, :at => cursor)
312
- stroke_color BLACK
313
-
314
- move_down(RHYTHM*3)
315
- end
316
-
317
- # Renders a Bounding Box for the inner margin
318
- #
319
- def inner_box(&block)
320
- bounding_box([INNER_MARGIN, cursor],
321
- :width => bounds.width - INNER_MARGIN*2,
322
- &block)
323
- end
324
-
325
- # Renders a Bounding Box with some background color and the formatted text
326
- # inside it
327
- #
328
- def colored_box(box_text, options={})
329
- options = { :fill_color => DARK_GRAY,
330
- :stroke_color => nil,
331
- :text_color => LIGHT_GRAY,
332
- :leading => LEADING
333
- }.merge(options)
334
-
335
- register_fonts
336
- text_options = { :leading => options[:leading],
337
- :fallback_fonts => ["DejaVu", "Kai"]
338
- }
339
-
340
- box_height = height_of_formatted(box_text, text_options)
341
-
342
- bounding_box([INNER_MARGIN + RHYTHM, cursor],
343
- :width => bounds.width - (INNER_MARGIN+RHYTHM)*2) do
344
-
345
- fill_color options[:fill_color]
346
- stroke_color options[:stroke_color] || options[:fill_color]
347
- fill_and_stroke_rounded_rectangle(
348
- [bounds.left - RHYTHM, cursor],
349
- bounds.left + bounds.right + RHYTHM*2,
350
- box_height + RHYTHM*2,
351
- 5
352
- )
353
- fill_color BLACK
354
- stroke_color BLACK
355
-
356
- pad(RHYTHM) do
357
- formatted_text(box_text, text_options)
358
- end
359
- end
360
-
361
- move_down(RHYTHM*2)
362
- end
363
-
364
- # Draws X and Y axis rulers beginning at the margin box origin. Used on
365
- # examples.
366
- #
367
- def stroke_axis(options={})
368
- super({:height => (cursor - 20).to_i}.merge(options))
369
- end
370
-
371
- # Reset some of the Prawn settings including graphics and text to their
372
- # defaults.
373
- #
374
- # Used after rendering examples so that each new example starts with a clean
375
- # slate.
376
- #
377
- def reset_settings
378
-
379
- # Text settings
380
- font("Helvetica", :size => 12)
381
- default_leading 0
382
- self.text_direction = :ltr
383
-
384
- # Graphics settings
385
- self.line_width = 1
386
- self.cap_style = :butt
387
- self.join_style = :miter
388
- undash
389
- fill_color BLACK
390
- stroke_color BLACK
391
- end
392
- end
393
- end
394
- end
@@ -1,128 +0,0 @@
1
- # encoding: utf-8
2
-
3
- module Prawn
4
- module ManualBuilder
5
- # The Prawn::ManualBuilder ExampleFile class is a utility class to ease
6
- # the manipulation and extraction of source code and comments from the
7
- # actual example files
8
- #
9
- class ExampleFile
10
- attr_reader :package, :filename
11
-
12
- # Stores the file data, filename and parent, which will be either an
13
- # ExampleSection or an ExamplePackage.
14
- #
15
- # Available boolean options are:
16
- #
17
- # <tt>:eval_source</tt>:: Evals the example source code (default: true)
18
- # <tt>:full_source</tt>:: Extract the full source code when true. Extract
19
- # only the code between the generate block when false (default: false)
20
- #
21
- def initialize(parent, filename, options={})
22
- @parent = parent.is_a?(String) ? ExamplePackage.new(parent) : parent
23
-
24
- @filename = filename
25
- @data = read_file(@parent.folder_name, filename)
26
-
27
- @options = {:eval_source => true, :full_source => false}.merge(options)
28
- end
29
-
30
- # Return the example source code excluding the initial comments and
31
- # require calls
32
- #
33
- def full_source
34
- @data.gsub(/# encoding.*?\n.*require.*?\n\n/m, "\n").strip
35
- end
36
-
37
- # Return the example source contained inside the first generate block or
38
- # the full source if no generate block is found
39
- #
40
- def generate_block_source
41
- block = @data.slice(/\w+\.generate.*? do\n(.*)end/m, 1)
42
-
43
- return full_source unless block
44
-
45
- block.gsub(/^( ){2}/, "")
46
- end
47
-
48
- def generated_block_line
49
- pre_block = @data.slice(/.*\w+\.generate.*? do\n/m)
50
-
51
- return 1 unless pre_block
52
-
53
- pre_block.lines.length + 1
54
- end
55
-
56
- # Return either the full_source or the generate_block_source according
57
- # to the options
58
- #
59
- def source
60
- @options[:full_source] ? full_source : generate_block_source
61
- end
62
-
63
- # Return true if the example source should be evaluated inline within
64
- # the manual according to the options
65
- #
66
- def eval?
67
- @options[:eval_source]
68
- end
69
-
70
- # Retrieve the comments between the encoding declaration and the require
71
- # call for example_helper.rb
72
- #
73
- # Then removes the '#' signs, reflows the line breaks and return the result
74
- #
75
- def introduction_text
76
- intro = @data.lines.grep(/^#/)
77
-
78
- intro.shift if intro.first =~ /^#!/
79
- intro.shift if intro.first =~ /coding:/
80
- intro.shift if intro.first =~ /frozen_string_literal:/
81
-
82
- intro = intro.join
83
-
84
- intro.gsub!(/\n# (?=\S)/m, ' ')
85
- intro.gsub!(/^#/, '')
86
- intro.gsub!("\n", "\n\n")
87
- intro.rstrip!
88
- intro
89
- end
90
-
91
- # Returns a human friendly version of the example file name
92
- #
93
- def name
94
- @name ||= @filename[/(.*)\.rb/, 1].gsub("_", " ").capitalize
95
- end
96
-
97
- # Returns this example's parent original folder name
98
- #
99
- def parent_folder_name
100
- @parent.folder_name
101
- end
102
-
103
- # Returns the human friendly version of this example parent name
104
- #
105
- def parent_name
106
- @parent.name
107
- end
108
-
109
- # Renders this example to a pdf
110
- #
111
- def render(pdf)
112
- pdf.render_example(self)
113
- end
114
-
115
- private
116
-
117
- # Read the data from a file in a given package
118
- #
119
- def read_file(folder_name, filename)
120
- data = File.read(File.expand_path(File.join(
121
- Prawn::ManualBuilder.manual_dir, folder_name, filename)))
122
-
123
- data.encode(::Encoding::UTF_8)
124
- end
125
-
126
- end
127
- end
128
- end
@@ -1,56 +0,0 @@
1
- # encoding: utf-8
2
-
3
- module Prawn
4
- module ManualBuilder
5
- # The Prawn::ManualBuilder::ExamplePackage class is a utility class to
6
- # handle the packaging of individual examples within a hierarchy
7
- # when building the manual.
8
- class ExamplePackage
9
- attr_reader :intro_block, :folder_name
10
-
11
- attr_writer :name
12
-
13
- def initialize(folder_name)
14
- @folder_name = folder_name
15
- @hierarchy = []
16
- end
17
-
18
- # Stores a new ExampleSection in the hierarchy and yields it to a block
19
- #
20
- def section(name)
21
- s = ExampleSection.new(self, name)
22
- yield s
23
- @hierarchy << s
24
- end
25
-
26
- # Stores a new ExampleFile in the hierarchy
27
- #
28
- def example(filename, options={})
29
- @hierarchy << ExampleFile.new(self, "#{filename}.rb", options)
30
- end
31
-
32
- # Stores a block with code to be evaluated when rendering the package cover
33
- #
34
- def intro(&block)
35
- @intro_block = block
36
- end
37
-
38
- # Returns a human friendly version of the package name
39
- #
40
- def name
41
- @name ||= @folder_name.gsub("_", " ").capitalize
42
- end
43
-
44
- # Renders a cover page for the package to a pdf and iterates the examples
45
- # hierarchy delegating the examples and sections to be rendered as well
46
- #
47
- def render(pdf)
48
- pdf.render_package_cover(self)
49
-
50
- @hierarchy.each do |node|
51
- node.render(pdf)
52
- end
53
- end
54
- end
55
- end
56
- end
@@ -1,47 +0,0 @@
1
- # encoding: utf-8
2
-
3
- module Prawn
4
- module ManualBuilder
5
- # The Prawn::ManualBuilder::ExampleSection class is a utility class to
6
- # handle sections of related examples within an ExamplePackage
7
- #
8
- class ExampleSection
9
- attr_reader :name
10
-
11
- def initialize(package, name)
12
- @package = package
13
- @name = name
14
- @examples = []
15
- end
16
-
17
- # Stores a new ExampleFile in the examples list
18
- #
19
- def example(filename, options={})
20
- @examples << ExampleFile.new(self, "#{filename}.rb", options)
21
- end
22
-
23
- # Returns this example's package original folder name
24
- #
25
- def folder_name
26
- @package.folder_name
27
- end
28
-
29
- # Returns the human friendly version of this section's package name
30
- #
31
- def package_name
32
- @package.name
33
- end
34
-
35
- # Renders the section to a pdf and iterates the examples list delegating the
36
- # examples to be rendered as well
37
- #
38
- def render(pdf)
39
- pdf.render_section(self)
40
-
41
- @examples.each do |example|
42
- example.render(pdf)
43
- end
44
- end
45
- end
46
- end
47
- end