asciidoctor-pdf 1.5.0.alpha.17 → 1.5.0.beta.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.adoc +75 -2
- data/NOTICE.adoc +14 -11
- data/README.adoc +105 -27
- data/asciidoctor-pdf.gemspec +4 -1
- data/data/themes/base-theme.yml +4 -0
- data/data/themes/default-theme.yml +17 -34
- data/data/themes/default-with-fallback-font-theme.yml +22 -0
- data/docs/theming-guide.adoc +1057 -867
- data/lib/asciidoctor-pdf/asciidoctor_ext/abstract_block.rb +5 -0
- data/lib/asciidoctor-pdf/asciidoctor_ext/document.rb +3 -0
- data/lib/asciidoctor-pdf/asciidoctor_ext/image.rb +4 -4
- data/lib/asciidoctor-pdf/asciidoctor_ext/logging_shim.rb +8 -2
- data/lib/asciidoctor-pdf/asciidoctor_ext/section.rb +16 -8
- data/lib/asciidoctor-pdf/asciidoctor_ext.rb +3 -1
- data/lib/asciidoctor-pdf/converter.rb +758 -499
- data/lib/asciidoctor-pdf/core_ext/hash.rb +5 -0
- data/lib/asciidoctor-pdf/core_ext/regexp.rb +3 -0
- data/lib/asciidoctor-pdf/core_ext.rb +2 -0
- data/lib/asciidoctor-pdf/formatted_text/formatter.rb +8 -1
- data/lib/asciidoctor-pdf/formatted_text/inline_image_arranger.rb +3 -1
- data/lib/asciidoctor-pdf/formatted_text/parser.rb +24 -12
- data/lib/asciidoctor-pdf/formatted_text/parser.treetop +1 -1
- data/lib/asciidoctor-pdf/formatted_text/text_background_and_border_renderer.rb +45 -0
- data/lib/asciidoctor-pdf/formatted_text/transform.rb +44 -21
- data/lib/asciidoctor-pdf/formatted_text.rb +1 -0
- data/lib/asciidoctor-pdf/index_catalog.rb +9 -3
- data/lib/asciidoctor-pdf/measurements.rb +1 -1
- data/lib/asciidoctor-pdf/prawn_ext/extensions.rb +37 -21
- data/lib/asciidoctor-pdf/prawn_ext/images.rb +18 -7
- data/lib/asciidoctor-pdf/roman_numeral.rb +12 -0
- data/lib/asciidoctor-pdf/theme_loader.rb +99 -69
- data/lib/asciidoctor-pdf/version.rb +1 -1
- metadata +45 -5
@@ -19,7 +19,7 @@ class Formatter
|
|
19
19
|
def format string, *args
|
20
20
|
options = args[0] || {}
|
21
21
|
string = string.tr_s(WHITESPACE, ' ') if options[:normalize]
|
22
|
-
return [text: string] unless
|
22
|
+
return [text: string] unless FormattingSnifferPattern.match? string
|
23
23
|
if (parsed = @parser.parse(string))
|
24
24
|
@transform.apply(parsed.content)
|
25
25
|
else
|
@@ -27,6 +27,13 @@ class Formatter
|
|
27
27
|
[text: string]
|
28
28
|
end
|
29
29
|
end
|
30
|
+
|
31
|
+
# The original purpose of this method is to split paragraphs, but our formatter only works on paragraphs that have
|
32
|
+
# been presplit. Therefore, we just need to wrap the fragments in a single-element array (representing a single
|
33
|
+
# paragraph) and return them.
|
34
|
+
def array_paragraphs fragments
|
35
|
+
[fragments]
|
36
|
+
end
|
30
37
|
end
|
31
38
|
end
|
32
39
|
end
|
@@ -66,7 +66,9 @@ module InlineImageArranger
|
|
66
66
|
svg_obj = ::Prawn::SVG::Interface.new ::File.read(image_path), doc,
|
67
67
|
at: doc.bounds.top_left,
|
68
68
|
width: image_w,
|
69
|
-
fallback_font_name: doc.default_svg_font
|
69
|
+
fallback_font_name: doc.default_svg_font,
|
70
|
+
enable_web_requests: doc.allow_uri_read,
|
71
|
+
enable_file_requests_with_root: (::File.dirname image_path)
|
70
72
|
svg_size = image_w ? svg_obj.document.sizing :
|
71
73
|
# NOTE convert intrinsic dimensions to points; constrain to content width
|
72
74
|
(svg_obj.resize width: [(to_pt svg_obj.document.sizing.output_width, :px), available_w].min)
|
@@ -356,44 +356,44 @@ module Markup
|
|
356
356
|
r1 = SyntaxNode.new(input, (index-1)...index) if r1 == true
|
357
357
|
r0 = r1
|
358
358
|
else
|
359
|
-
if (match_len = has_terminal?('
|
359
|
+
if (match_len = has_terminal?('strong', false, index))
|
360
360
|
r2 = instantiate_node(SyntaxNode,input, index...(index + match_len))
|
361
361
|
@index += match_len
|
362
362
|
else
|
363
|
-
terminal_parse_failure('
|
363
|
+
terminal_parse_failure('strong')
|
364
364
|
r2 = nil
|
365
365
|
end
|
366
366
|
if r2
|
367
367
|
r2 = SyntaxNode.new(input, (index-1)...index) if r2 == true
|
368
368
|
r0 = r2
|
369
369
|
else
|
370
|
-
if (match_len = has_terminal?('
|
370
|
+
if (match_len = has_terminal?('em', false, index))
|
371
371
|
r3 = instantiate_node(SyntaxNode,input, index...(index + match_len))
|
372
372
|
@index += match_len
|
373
373
|
else
|
374
|
-
terminal_parse_failure('
|
374
|
+
terminal_parse_failure('em')
|
375
375
|
r3 = nil
|
376
376
|
end
|
377
377
|
if r3
|
378
378
|
r3 = SyntaxNode.new(input, (index-1)...index) if r3 == true
|
379
379
|
r0 = r3
|
380
380
|
else
|
381
|
-
if (match_len = has_terminal?('
|
381
|
+
if (match_len = has_terminal?('code', false, index))
|
382
382
|
r4 = instantiate_node(SyntaxNode,input, index...(index + match_len))
|
383
383
|
@index += match_len
|
384
384
|
else
|
385
|
-
terminal_parse_failure('
|
385
|
+
terminal_parse_failure('code')
|
386
386
|
r4 = nil
|
387
387
|
end
|
388
388
|
if r4
|
389
389
|
r4 = SyntaxNode.new(input, (index-1)...index) if r4 == true
|
390
390
|
r0 = r4
|
391
391
|
else
|
392
|
-
if (match_len = has_terminal?('
|
392
|
+
if (match_len = has_terminal?('color', false, index))
|
393
393
|
r5 = instantiate_node(SyntaxNode,input, index...(index + match_len))
|
394
394
|
@index += match_len
|
395
395
|
else
|
396
|
-
terminal_parse_failure('
|
396
|
+
terminal_parse_failure('color')
|
397
397
|
r5 = nil
|
398
398
|
end
|
399
399
|
if r5
|
@@ -422,11 +422,11 @@ module Markup
|
|
422
422
|
r7 = SyntaxNode.new(input, (index-1)...index) if r7 == true
|
423
423
|
r0 = r7
|
424
424
|
else
|
425
|
-
if (match_len = has_terminal?('
|
425
|
+
if (match_len = has_terminal?('button', false, index))
|
426
426
|
r8 = instantiate_node(SyntaxNode,input, index...(index + match_len))
|
427
427
|
@index += match_len
|
428
428
|
else
|
429
|
-
terminal_parse_failure('
|
429
|
+
terminal_parse_failure('button')
|
430
430
|
r8 = nil
|
431
431
|
end
|
432
432
|
if r8
|
@@ -455,8 +455,20 @@ module Markup
|
|
455
455
|
r10 = SyntaxNode.new(input, (index-1)...index) if r10 == true
|
456
456
|
r0 = r10
|
457
457
|
else
|
458
|
-
|
459
|
-
|
458
|
+
if (match_len = has_terminal?('del', false, index))
|
459
|
+
r11 = instantiate_node(SyntaxNode,input, index...(index + match_len))
|
460
|
+
@index += match_len
|
461
|
+
else
|
462
|
+
terminal_parse_failure('del')
|
463
|
+
r11 = nil
|
464
|
+
end
|
465
|
+
if r11
|
466
|
+
r11 = SyntaxNode.new(input, (index-1)...index) if r11 == true
|
467
|
+
r0 = r11
|
468
|
+
else
|
469
|
+
@index = i0
|
470
|
+
r0 = nil
|
471
|
+
end
|
460
472
|
end
|
461
473
|
end
|
462
474
|
end
|
@@ -51,7 +51,7 @@ grammar Markup
|
|
51
51
|
# QUESTION faster to do regex?
|
52
52
|
# QUESTION can we cut stuff we aren't using? what about supporting hr?
|
53
53
|
#'a' / 'b' / 'code' / 'color' / 'del' / 'em' / 'font' / 'i' / 'img' / 'link' / 'span' / 'strikethrough' / 'strong' / 'sub' / 'sup' / 'u'
|
54
|
-
'a' / '
|
54
|
+
'a' / 'strong' / 'em' / 'code' / 'color' / 'font' / 'span' / 'button' / 'sub' / 'sup' / 'del'
|
55
55
|
end
|
56
56
|
|
57
57
|
rule void_tag_name
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Asciidoctor::Pdf::FormattedText
|
2
|
+
module TextBackgroundAndBorderRenderer
|
3
|
+
module_function
|
4
|
+
|
5
|
+
# render_behind is called before the text is printed
|
6
|
+
def render_behind fragment
|
7
|
+
return if (pdf = fragment.document).scratch?
|
8
|
+
data = fragment.format_state
|
9
|
+
if (border_offset = data[:border_offset])
|
10
|
+
at = [fragment.left - border_offset, fragment.top + border_offset]
|
11
|
+
width = fragment.width + border_offset * 2
|
12
|
+
height = fragment.height + border_offset * 2
|
13
|
+
else
|
14
|
+
at = fragment.top_left
|
15
|
+
width = fragment.width
|
16
|
+
height = fragment.height
|
17
|
+
end
|
18
|
+
border_radius = data[:border_radius]
|
19
|
+
if (background_color = data[:background_color])
|
20
|
+
prev_fill_color = pdf.fill_color
|
21
|
+
pdf.fill_color background_color
|
22
|
+
if border_radius
|
23
|
+
pdf.fill_rounded_rectangle at, width, height, border_radius
|
24
|
+
else
|
25
|
+
pdf.fill_rectangle at, width, height
|
26
|
+
end
|
27
|
+
pdf.fill_color prev_fill_color
|
28
|
+
end
|
29
|
+
if (border_width = data[:border_width])
|
30
|
+
border_color = data[:border_color]
|
31
|
+
prev_stroke_color = pdf.stroke_color
|
32
|
+
prev_line_width = pdf.line_width
|
33
|
+
pdf.stroke_color border_color
|
34
|
+
pdf.line_width border_width
|
35
|
+
if border_radius
|
36
|
+
pdf.stroke_rounded_rectangle at, width, height, border_radius
|
37
|
+
else
|
38
|
+
pdf.stroke_rectangle at, width, height
|
39
|
+
end
|
40
|
+
pdf.stroke_color prev_stroke_color
|
41
|
+
pdf.line_width prev_line_width
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -20,21 +20,44 @@ class Transform
|
|
20
20
|
@merge_adjacent_text_nodes = options[:merge_adjacent_text_nodes]
|
21
21
|
# TODO add support for character spacing
|
22
22
|
if (theme = options[:theme])
|
23
|
-
@
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
23
|
+
@theme_settings = {
|
24
|
+
button: {
|
25
|
+
color: theme.button_font_color,
|
26
|
+
font: theme.button_font_family,
|
27
|
+
size: theme.button_font_size,
|
28
|
+
styles: to_styles(theme.button_font_style),
|
29
|
+
background_color: (button_bg_color = theme.button_background_color),
|
30
|
+
border_width: (button_border_width = theme.button_border_width),
|
31
|
+
border_color: button_border_width && (theme.button_border_color || theme.base_border_color),
|
32
|
+
border_offset: (button_bg_or_border = button_bg_color || button_border_width) && theme.button_border_offset,
|
33
|
+
border_radius: button_bg_or_border && theme.button_border_radius,
|
34
|
+
callback: button_bg_or_border && [TextBackgroundAndBorderRenderer],
|
35
|
+
}.compact,
|
36
|
+
code: {
|
37
|
+
color: theme.literal_font_color,
|
38
|
+
font: theme.literal_font_family,
|
39
|
+
size: theme.literal_font_size,
|
40
|
+
styles: to_styles(theme.literal_font_style),
|
41
|
+
background_color: (monospaced_bg_color = theme.literal_background_color),
|
42
|
+
border_width: (monospaced_border_width = theme.literal_border_width),
|
43
|
+
border_color: monospaced_border_width && (theme.literal_border_color || theme.base_border_color),
|
44
|
+
border_offset: (monospaced_bg_or_border = monospaced_bg_color || monospaced_border_width) && theme.literal_border_offset,
|
45
|
+
border_radius: monospaced_bg_or_border && theme.literal_border_radius,
|
46
|
+
callback: monospaced_bg_or_border && [TextBackgroundAndBorderRenderer],
|
47
|
+
}.compact,
|
48
|
+
link: {
|
49
|
+
color: theme.link_font_color,
|
50
|
+
font: theme.link_font_family,
|
51
|
+
size: theme.link_font_size,
|
52
|
+
styles: to_styles(theme.link_font_style, theme.link_text_decoration)
|
53
|
+
}.compact,
|
54
|
+
}
|
35
55
|
else
|
36
|
-
@
|
37
|
-
|
56
|
+
@theme_settings = {
|
57
|
+
button: { font: 'Courier', styles: [:bold].to_set },
|
58
|
+
code: { font: 'Courier', size: 0.9 },
|
59
|
+
link: { color: '0000FF' },
|
60
|
+
}
|
38
61
|
end
|
39
62
|
end
|
40
63
|
|
@@ -79,7 +102,7 @@ class Transform
|
|
79
102
|
image_format: attributes[:format],
|
80
103
|
# a zero-width space in the text will cause the image to be duplicated
|
81
104
|
text: (attributes[:alt].delete ZeroWidthSpace),
|
82
|
-
callback: InlineImageRenderer
|
105
|
+
callback: [InlineImageRenderer],
|
83
106
|
}
|
84
107
|
if (img_w = attributes[:width])
|
85
108
|
fragment[:image_width] = img_w
|
@@ -123,9 +146,9 @@ class Transform
|
|
123
146
|
styles << :bold
|
124
147
|
when :em
|
125
148
|
styles << :italic
|
126
|
-
when :code
|
127
|
-
# NOTE prefer old value, except for styles, which should be combined
|
128
|
-
fragment.update(@
|
149
|
+
when :code, :button
|
150
|
+
# NOTE prefer old value, except for styles and callback, which should be combined
|
151
|
+
fragment.update(@theme_settings[tag_name]) {|k, oval, nval| k == :styles ? oval.merge(nval) : (k == :callback ? oval.union(nval) : oval) }
|
129
152
|
when :color
|
130
153
|
if !fragment[:color]
|
131
154
|
if (rgb = attrs[:rgb])
|
@@ -168,7 +191,7 @@ class Transform
|
|
168
191
|
fragment[:width] = value
|
169
192
|
if (value = attrs[:align])
|
170
193
|
fragment[:align] = value.to_sym
|
171
|
-
fragment[:callback]
|
194
|
+
(fragment[:callback] ||= []) << InlineTextAligner
|
172
195
|
end
|
173
196
|
end
|
174
197
|
#if !fragment[:character_spacing] && (value = attrs[:character_spacing])
|
@@ -191,12 +214,12 @@ class Transform
|
|
191
214
|
if (type = attrs[:type])
|
192
215
|
fragment[:type] = type.to_sym
|
193
216
|
end
|
194
|
-
fragment[:callback]
|
217
|
+
(fragment[:callback] ||= []) << InlineDestinationMarker
|
195
218
|
visible = false
|
196
219
|
end
|
197
220
|
end
|
198
221
|
# NOTE prefer old value, except for styles, which should be combined
|
199
|
-
fragment.update(@
|
222
|
+
fragment.update(@theme_settings[:link]) {|k, oval, nval| k == :styles ? oval.merge(nval) : oval } if visible
|
200
223
|
when :sub
|
201
224
|
styles << :subscript
|
202
225
|
when :sup
|
@@ -7,3 +7,4 @@ require_relative 'formatted_text/inline_destination_marker'
|
|
7
7
|
require_relative 'formatted_text/inline_image_arranger'
|
8
8
|
require_relative 'formatted_text/inline_image_renderer'
|
9
9
|
require_relative 'formatted_text/inline_text_aligner'
|
10
|
+
require_relative 'formatted_text/text_background_and_border_renderer'
|
@@ -1,5 +1,6 @@
|
|
1
1
|
module Asciidoctor; module PDF
|
2
2
|
class IndexCatalog
|
3
|
+
include Sanitizer
|
3
4
|
LeadingAlphaRx = /^\p{Alpha}/
|
4
5
|
|
5
6
|
attr_accessor :start_page_number
|
@@ -8,6 +9,11 @@ module Asciidoctor; module PDF
|
|
8
9
|
@categories = {}
|
9
10
|
@start_page_number = 1
|
10
11
|
@dests = {}
|
12
|
+
@sequence = 0
|
13
|
+
end
|
14
|
+
|
15
|
+
def next_anchor_name
|
16
|
+
%(__indexterm-#{@sequence += 1})
|
11
17
|
end
|
12
18
|
|
13
19
|
def store_term names, dest = nil
|
@@ -22,7 +28,7 @@ module Asciidoctor; module PDF
|
|
22
28
|
|
23
29
|
def store_primary_term name, dest = nil
|
24
30
|
store_dest dest if dest
|
25
|
-
(init_category name.chr
|
31
|
+
(init_category uppercase_mb name.chr).store_term name, dest
|
26
32
|
end
|
27
33
|
|
28
34
|
def store_secondary_term primary_name, secondary_name, dest = nil
|
@@ -36,8 +42,8 @@ module Asciidoctor; module PDF
|
|
36
42
|
end
|
37
43
|
|
38
44
|
def init_category name
|
39
|
-
name = '@' unless LeadingAlphaRx
|
40
|
-
@categories[name] ||=
|
45
|
+
name = '@' unless LeadingAlphaRx.match? name
|
46
|
+
@categories[name] ||= IndexTermCategory.new name
|
41
47
|
end
|
42
48
|
|
43
49
|
def find_category name
|
@@ -48,7 +48,7 @@ module Asciidoctor; module PDF
|
|
48
48
|
|
49
49
|
# Resolve measurement values in the string to PDF points.
|
50
50
|
def resolve_measurement_values str
|
51
|
-
if MeasurementValueHintRx
|
51
|
+
if MeasurementValueHintRx.match? str
|
52
52
|
str.gsub(InsetMeasurementValueRx) { to_pt $1.to_f, $2 }
|
53
53
|
else
|
54
54
|
str
|
@@ -39,7 +39,7 @@ module Extensions
|
|
39
39
|
|
40
40
|
# Returns the effective (writable) width of the page
|
41
41
|
#
|
42
|
-
# If inside a
|
42
|
+
# If inside a bounding box, returns width of box.
|
43
43
|
#
|
44
44
|
def effective_page_width
|
45
45
|
reference_bounds.width
|
@@ -357,11 +357,19 @@ module Extensions
|
|
357
357
|
end
|
358
358
|
end
|
359
359
|
|
360
|
-
#
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
360
|
+
# NOTE override built-in draw_indented_formatted_line to insert leading before second line
|
361
|
+
def draw_indented_formatted_line string, opts
|
362
|
+
result = super
|
363
|
+
unless @no_text_printed || @all_text_printed
|
364
|
+
# as of Prawn 1.2.1, we have to handle the line gap after the first line manually
|
365
|
+
move_down opts[:leading]
|
366
|
+
end
|
367
|
+
result
|
368
|
+
end
|
369
|
+
|
370
|
+
# Performs the same work as Prawn::Text.text except that the first_line_opts are applied to the first line of text
|
371
|
+
# renderered. It's necessary to use low-level APIs in this method so we only style the first line and not the
|
372
|
+
# remaining lines (which is the default behavior in Prawn).
|
365
373
|
def text_with_formatted_first_line string, first_line_opts, opts
|
366
374
|
color = opts.delete :color
|
367
375
|
fragments = parse_text string, opts
|
@@ -378,17 +386,27 @@ module Extensions
|
|
378
386
|
first_line_opts = opts.merge(first_line_opts).merge single_line: true
|
379
387
|
box = ::Prawn::Text::Formatted::Box.new fragments, first_line_opts
|
380
388
|
# NOTE get remaining_fragments before we add color to fragments on first line
|
381
|
-
|
389
|
+
if (text_indent = opts.delete :indent_paragraphs)
|
390
|
+
remaining_fragments = indent text_indent do
|
391
|
+
box.render dry_run: true
|
392
|
+
end
|
393
|
+
else
|
394
|
+
remaining_fragments = box.render dry_run: true
|
395
|
+
end
|
382
396
|
# NOTE color must be applied per-fragment
|
383
397
|
if first_line_color
|
384
398
|
fragments.each {|fragment| fragment[:color] ||= first_line_color}
|
385
399
|
end
|
386
|
-
|
400
|
+
if text_indent
|
401
|
+
indent text_indent do
|
402
|
+
fill_formatted_text_box fragments, first_line_opts
|
403
|
+
end
|
404
|
+
else
|
405
|
+
fill_formatted_text_box fragments, first_line_opts
|
406
|
+
end
|
387
407
|
unless remaining_fragments.empty?
|
388
408
|
# NOTE color must be applied per-fragment
|
389
|
-
if color
|
390
|
-
remaining_fragments.each {|fragment| fragment[:color] ||= color }
|
391
|
-
end
|
409
|
+
remaining_fragments.each {|fragment| fragment[:color] ||= color } if color
|
392
410
|
# as of Prawn 1.2.1, we have to handle the line gap after the first line manually
|
393
411
|
move_down opts[:leading]
|
394
412
|
remaining_fragments = fill_formatted_text_box remaining_fragments, opts
|
@@ -730,20 +748,18 @@ module Extensions
|
|
730
748
|
nil
|
731
749
|
end
|
732
750
|
|
733
|
-
# Create a new page for the specified image. If the
|
734
|
-
#
|
735
|
-
# edges of the page (full coverage).
|
751
|
+
# Create a new page for the specified image. If the canvas option is true,
|
752
|
+
# the image is positioned relative to the boundaries of the page.
|
736
753
|
def image_page file, options = {}
|
737
754
|
start_new_page_discretely
|
738
|
-
|
739
|
-
|
740
|
-
|
741
|
-
end
|
755
|
+
image_page_number = page_number
|
756
|
+
if options.delete :canvas
|
757
|
+
canvas { image file, ({ position: :center, vposition: :center }.merge options) }
|
742
758
|
else
|
743
|
-
image file, fit: [bounds.width, bounds.height]
|
759
|
+
image file, (options.merge position: :center, vposition: :center, fit: [bounds.width, bounds.height])
|
744
760
|
end
|
745
|
-
#
|
746
|
-
go_to_page
|
761
|
+
# NOTE advance to new page just in case the image function threw off the cursor
|
762
|
+
go_to_page image_page_number
|
747
763
|
nil
|
748
764
|
end
|
749
765
|
|
@@ -9,10 +9,20 @@ module Images
|
|
9
9
|
|
10
10
|
# Dispatch to suitable image method in Prawn based on file extension.
|
11
11
|
def image file, opts = {}
|
12
|
-
# FIXME handle case when SVG is
|
13
|
-
if ::String === file && (file.downcase.end_with? '.svg')
|
14
|
-
opts[:
|
15
|
-
|
12
|
+
# FIXME handle case when SVG is an IO object
|
13
|
+
if ::String === file && (((opts = opts.dup).delete :format) == 'svg' || (file.downcase.end_with? '.svg'))
|
14
|
+
#opts[:enable_file_requests_with_root] = (::File.dirname file) unless opts.key? :enable_file_requests_with_root
|
15
|
+
#opts[:enable_web_requests] = allow_uri_read if !(opts.key? :enable_web_requests) && (respond_to? :allow_uri_read)
|
16
|
+
#opts[:fallback_font_name] = default_svg_font if !(opts.key? :fallback_font_name) && (respond_to? :default_svg_font)
|
17
|
+
if (opts.key? :fit) && (fit = opts.delete :fit) && !opts[:width] && !opts[:height]
|
18
|
+
svg (::File.read file), opts do |svg_doc|
|
19
|
+
max_width, max_height = fit
|
20
|
+
svg_doc.calculate_sizing requested_width: max_width if max_width && svg_doc.sizing.output_width != max_width
|
21
|
+
svg_doc.calculate_sizing requested_height: max_height if max_height && svg_doc.sizing.output_height > max_height
|
22
|
+
end
|
23
|
+
else
|
24
|
+
svg (::File.read file), opts
|
25
|
+
end
|
16
26
|
else
|
17
27
|
_initial_image file, opts
|
18
28
|
end
|
@@ -22,9 +32,9 @@ module Images
|
|
22
32
|
#
|
23
33
|
# Returns a Hash containing :width and :height keys that map to the image's
|
24
34
|
# intrinsic width and height values (in pixels)
|
25
|
-
def intrinsic_image_dimensions path
|
26
|
-
if
|
27
|
-
img_obj = ::Prawn::SVG::Interface.new ::File.read
|
35
|
+
def intrinsic_image_dimensions path, format
|
36
|
+
if format == 'svg'
|
37
|
+
img_obj = ::Prawn::SVG::Interface.new (::File.read path), self, {}
|
28
38
|
img_size = img_obj.document.sizing
|
29
39
|
{ width: img_size.output_width, height: img_size.output_height }
|
30
40
|
else
|
@@ -32,6 +42,7 @@ module Images
|
|
32
42
|
_, img_size = ::File.open(path, 'rb') {|fd| build_image_object fd }
|
33
43
|
{ width: img_size.width, height: img_size.height }
|
34
44
|
end
|
45
|
+
rescue
|
35
46
|
end
|
36
47
|
end
|
37
48
|
|
@@ -73,6 +73,14 @@ class RomanNumeral
|
|
73
73
|
@integer_value
|
74
74
|
end
|
75
75
|
|
76
|
+
def odd?
|
77
|
+
to_i.odd?
|
78
|
+
end
|
79
|
+
|
80
|
+
def even?
|
81
|
+
to_i.even?
|
82
|
+
end
|
83
|
+
|
76
84
|
def next
|
77
85
|
RomanNumeral.new @integer_value + 1, @letter_case
|
78
86
|
end
|
@@ -86,6 +94,10 @@ class RomanNumeral
|
|
86
94
|
RomanNumeral.new @integer_value - 1, @letter_case
|
87
95
|
end
|
88
96
|
|
97
|
+
def empty?
|
98
|
+
false
|
99
|
+
end
|
100
|
+
|
89
101
|
def self.int_to_roman value
|
90
102
|
result = []
|
91
103
|
BaseDigits.keys.reverse_each do |ival|
|