prawn-manual_builder 0.3.1 → 0.4.0

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