prawn-html 0.4.2 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3eec6595a60748790725f32e92e7b749d92086e5331d8a572d287a3a90f9d83a
4
- data.tar.gz: 89bcbe740f84c4ecdea2877e96d0f677a25bf424325b41fa40caf3d1351fba96
3
+ metadata.gz: e82e0a464e39fd4c47c399cb043ceefe090524b29ead1c06795a484d4c648388
4
+ data.tar.gz: e124a7fb9800a434f42d403b478c6a6cb30b4e7d1e7e540d015028dec6daaa59
5
5
  SHA512:
6
- metadata.gz: 9b34f31b07aaac30910fd1bcb2118b9cbe92bbdaf4ac80850520887e7c3803a6bb1217764551baeeb1a88d695d7b26c4885a4d3065bbd1f20527d37a5723c7bf
7
- data.tar.gz: 6ed594bd070e4e4cf20519c28094f7d57eeb779f60c4867f380aab22946d20401bac307e17db47c0560b2cf1cdc591d80e07c3ff54999db92bd3040e19cc18d7
6
+ metadata.gz: c08e1252e2c9c8f1591840179549d3e14483d91361b1a9f56d96f23cfa30829b98d8d2ace33808a73a9467e1ff37054d7f462d1c7170db273a4ff83b203d807f
7
+ data.tar.gz: '0979bd9a66e463ee8e4ea3c141a2d4e2fcc0e3b1bc82397aeda2e357c10c4518b00b1e1e4fc3f2ec786070c6d229241423cd5933a89fbc9137bdccc211d22a44'
data/lib/prawn-html.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PrawnHtml
4
- PX = 0.66 # conversion constant for pixel sixes
4
+ PX = 0.6 # conversion constant for pixel sixes
5
5
 
6
6
  COLORS = {
7
7
  'aliceblue' => 'f0f8ff',
@@ -7,16 +7,15 @@ module PrawnHtml
7
7
  attr_reader :styles
8
8
 
9
9
  STYLES_APPLY = {
10
- block: %i[align leading left margin_left padding_left position top],
10
+ block: %i[align bottom leading left margin_left padding_left position right top],
11
11
  tag_close: %i[margin_bottom padding_bottom break_after],
12
12
  tag_open: %i[margin_top padding_top break_before],
13
- text_node: %i[background callback character_spacing color font link list_style_type size styles white_space]
13
+ text_node: %i[callback character_spacing color font link list_style_type size styles white_space]
14
14
  }.freeze
15
15
 
16
16
  STYLES_LIST = {
17
17
  # text node styles
18
- 'background' => { key: :background, set: :convert_color },
19
- 'callback' => { key: :callback, set: :copy_value },
18
+ 'background' => { key: :callback, set: :callback_background },
20
19
  'color' => { key: :color, set: :convert_color },
21
20
  'font-family' => { key: :font, set: :unquote },
22
21
  'font-size' => { key: :size, set: :convert_size },
@@ -25,7 +24,7 @@ module PrawnHtml
25
24
  'href' => { key: :link, set: :copy_value },
26
25
  'letter-spacing' => { key: :character_spacing, set: :convert_float },
27
26
  'list-style-type' => { key: :list_style_type, set: :unquote },
28
- 'text-decoration' => { key: :styles, set: :append_styles },
27
+ 'text-decoration' => { key: :styles, set: :append_text_decoration },
29
28
  'vertical-align' => { key: :styles, set: :append_styles },
30
29
  'white-space' => { key: :white_space, set: :convert_symbol },
31
30
  # tag opening styles
@@ -37,13 +36,15 @@ module PrawnHtml
37
36
  'margin-bottom' => { key: :margin_bottom, set: :convert_size },
38
37
  'padding-bottom' => { key: :padding_bottom, set: :convert_size },
39
38
  # block styles
40
- 'left' => { key: :left, set: :convert_size },
39
+ 'bottom' => { key: :bottom, set: :convert_size, options: :height },
40
+ 'left' => { key: :left, set: :convert_size, options: :width },
41
41
  'line-height' => { key: :leading, set: :convert_size },
42
42
  'margin-left' => { key: :margin_left, set: :convert_size },
43
43
  'padding-left' => { key: :padding_left, set: :convert_size },
44
44
  'position' => { key: :position, set: :convert_symbol },
45
+ 'right' => { key: :right, set: :convert_size, options: :width },
45
46
  'text-align' => { key: :align, set: :convert_symbol },
46
- 'top' => { key: :top, set: :convert_size }
47
+ 'top' => { key: :top, set: :convert_size, options: :height }
47
48
  }.freeze
48
49
 
49
50
  STYLES_MERGE = %i[margin_left padding_left].freeze
@@ -67,9 +68,10 @@ module PrawnHtml
67
68
  # Merge text styles
68
69
  #
69
70
  # @param text_styles [String] styles to parse and process
70
- def merge_text_styles!(text_styles)
71
+ # @param options [Hash] options (container width/height/etc.)
72
+ def merge_text_styles!(text_styles, options: {})
71
73
  hash_styles = Attributes.parse_styles(text_styles)
72
- process_styles(hash_styles) unless hash_styles.empty?
74
+ process_styles(hash_styles, options: options) unless hash_styles.empty?
73
75
  end
74
76
 
75
77
  class << self
@@ -100,21 +102,33 @@ module PrawnHtml
100
102
 
101
103
  private
102
104
 
103
- def apply_rule!(result, rule, value)
104
- return unless rule
105
+ def process_styles(hash_styles, options:)
106
+ hash_styles.each do |key, value|
107
+ rule = evaluate_rule(key, value)
108
+ apply_rule!(merged_styles: @styles, rule: rule, value: value, options: options)
109
+ end
110
+ @styles
111
+ end
105
112
 
106
- if rule[:set] == :append_styles
107
- (result[rule[:key]] ||= []) << Utils.normalize_style(value)
108
- else
109
- result[rule[:key]] = Utils.send(rule[:set], value)
113
+ def evaluate_rule(rule_key, attr_value)
114
+ rule = STYLES_LIST[rule_key]
115
+ if rule && rule[:set] == :append_text_decoration
116
+ return { key: :callback, set: :callback_strike_through } if attr_value == 'line-through'
117
+
118
+ return { key: :styles, set: :append_styles }
110
119
  end
120
+ rule
111
121
  end
112
122
 
113
- def process_styles(hash_styles)
114
- hash_styles.each do |key, value|
115
- apply_rule!(@styles, STYLES_LIST[key], value)
123
+ def apply_rule!(merged_styles:, rule:, value:, options:)
124
+ return unless rule
125
+
126
+ if rule[:set] == :append_styles
127
+ (merged_styles[rule[:key]] ||= []) << Utils.normalize_style(value)
128
+ else
129
+ opts = rule[:options] ? options[rule[:options]] : nil
130
+ merged_styles[rule[:key]] = Utils.send(rule[:set], value, options: opts)
116
131
  end
117
- @styles
118
132
  end
119
133
  end
120
134
  end
@@ -2,12 +2,12 @@
2
2
 
3
3
  module PrawnHtml
4
4
  module Callbacks
5
- class Highlight
5
+ class Background
6
6
  DEF_HIGHLIGHT = 'ffff00'
7
7
 
8
- def initialize(pdf, item)
8
+ def initialize(pdf, color = nil)
9
9
  @pdf = pdf
10
- @color = item.delete(:background) || DEF_HIGHLIGHT
10
+ @color = color || DEF_HIGHLIGHT
11
11
  end
12
12
 
13
13
  def render_behind(fragment)
@@ -2,15 +2,17 @@
2
2
 
3
3
  module PrawnHtml
4
4
  class Context < Array
5
- DEF_FONT_SIZE = 10.3
5
+ DEF_FONT_SIZE = 16 * PX
6
6
 
7
- attr_accessor :last_margin, :last_text_node
7
+ attr_reader :previous_tag
8
+ attr_accessor :last_text_node
8
9
 
9
10
  # Init the Context
10
11
  def initialize(*_args)
11
12
  super
12
- @last_margin = 0
13
13
  @last_text_node = false
14
+ @merged_styles = nil
15
+ @previous_tag = nil
14
16
  end
15
17
 
16
18
  # Add an element to the context
@@ -25,6 +27,7 @@ module PrawnHtml
25
27
  element.parent = last
26
28
  push(element)
27
29
  element.on_context_add(self) if element.respond_to?(:on_context_add)
30
+ @merged_styles = nil
28
31
  self
29
32
  end
30
33
 
@@ -49,11 +52,21 @@ module PrawnHtml
49
52
  # Merge the context styles for text nodes
50
53
  #
51
54
  # @return [Hash] the hash of merged styles
52
- def text_node_styles
53
- each_with_object(base_styles) do |element, res|
54
- evaluate_element_styles(element, res)
55
- element.update_styles(res) if element.respond_to?(:update_styles)
56
- end
55
+ def merged_styles
56
+ @merged_styles ||=
57
+ each_with_object(base_styles) do |element, res|
58
+ evaluate_element_styles(element, res)
59
+ element.update_styles(res) if element.respond_to?(:update_styles)
60
+ end
61
+ end
62
+
63
+ # Remove the last element from the context
64
+ def remove_last
65
+ last.on_context_remove(self) if last.respond_to?(:on_context_remove)
66
+ @merged_styles = nil
67
+ @last_text_node = false
68
+ @previous_tag = last.tag
69
+ pop
57
70
  end
58
71
 
59
72
  private
@@ -11,6 +11,7 @@ module PrawnHtml
11
11
  def initialize(pdf)
12
12
  @buffer = []
13
13
  @context = Context.new
14
+ @last_margin = 0
14
15
  @pdf = pdf
15
16
  end
16
17
 
@@ -20,8 +21,7 @@ module PrawnHtml
20
21
  def on_tag_close(element)
21
22
  render_if_needed(element)
22
23
  apply_tag_close_styles(element)
23
- context.last_text_node = false
24
- context.pop
24
+ context.remove_last
25
25
  end
26
26
 
27
27
  # On tag open callback
@@ -35,8 +35,9 @@ module PrawnHtml
35
35
  tag_class = Tag.class_for(tag_name)
36
36
  return unless tag_class
37
37
 
38
- tag_class.new(tag_name, attributes: attributes, element_styles: element_styles).tap do |element|
39
- setup_element(element)
38
+ options = { width: pdf.page_width, height: pdf.page_height }
39
+ tag_class.new(tag_name, attributes: attributes, options: options).tap do |element|
40
+ setup_element(element, element_styles: element_styles)
40
41
  end
41
42
  end
42
43
 
@@ -48,7 +49,7 @@ module PrawnHtml
48
49
  def on_text_node(content)
49
50
  return if content.match?(/\A\s*\Z/)
50
51
 
51
- buffer << context.text_node_styles.merge(text: prepare_text(content))
52
+ buffer << context.merged_styles.merge(text: prepare_text(content))
52
53
  context.last_text_node = true
53
54
  nil
54
55
  end
@@ -59,19 +60,20 @@ module PrawnHtml
59
60
 
60
61
  output_content(buffer.dup, context.block_styles)
61
62
  buffer.clear
62
- context.last_margin = 0
63
+ @last_margin = 0
63
64
  end
64
65
 
65
66
  alias_method :flush, :render
66
67
 
67
68
  private
68
69
 
69
- attr_reader :buffer, :context, :pdf
70
+ attr_reader :buffer, :context, :last_margin, :pdf
70
71
 
71
- def setup_element(element)
72
+ def setup_element(element, element_styles:)
72
73
  add_space_if_needed unless render_if_needed(element)
73
- apply_tag_open_styles(element)
74
74
  context.add(element)
75
+ element.process_styles(element_styles: element_styles)
76
+ apply_tag_open_styles(element)
75
77
  element.custom_render(pdf, context) if element.respond_to?(:custom_render)
76
78
  end
77
79
 
@@ -89,14 +91,14 @@ module PrawnHtml
89
91
 
90
92
  def apply_tag_close_styles(element)
91
93
  tag_styles = element.tag_close_styles
92
- context.last_margin = tag_styles[:margin_bottom].to_f
93
- pdf.advance_cursor(context.last_margin + tag_styles[:padding_bottom].to_f)
94
+ @last_margin = tag_styles[:margin_bottom].to_f
95
+ pdf.advance_cursor(last_margin + tag_styles[:padding_bottom].to_f)
94
96
  pdf.start_new_page if tag_styles[:break_after]
95
97
  end
96
98
 
97
99
  def apply_tag_open_styles(element)
98
100
  tag_styles = element.tag_open_styles
99
- move_down = (tag_styles[:margin_top].to_f - context.last_margin) + tag_styles[:padding_top].to_f
101
+ move_down = (tag_styles[:margin_top].to_f - last_margin) + tag_styles[:padding_top].to_f
100
102
  pdf.advance_cursor(move_down) if move_down > 0
101
103
  pdf.start_new_page if tag_styles[:break_before]
102
104
  end
@@ -111,24 +113,41 @@ module PrawnHtml
111
113
  def output_content(buffer, block_styles)
112
114
  apply_callbacks(buffer)
113
115
  left_indent = block_styles[:margin_left].to_f + block_styles[:padding_left].to_f
114
- options = block_styles.slice(:align, :leading, :mode, :padding_left)
115
- options[:indent_paragraphs] = left_indent if left_indent > 0
116
- pdf.puts(buffer, options, bounding_box: bounds(block_styles))
116
+ options = block_styles.slice(:align, :indent_paragraphs, :leading, :mode, :padding_left)
117
+ options[:leading] = adjust_leading(buffer, options[:leading])
118
+ pdf.puts(buffer, options, bounding_box: bounds(buffer, options, block_styles), left_indent: left_indent)
117
119
  end
118
120
 
119
121
  def apply_callbacks(buffer)
120
122
  buffer.select { |item| item[:callback] }.each do |item|
121
- callback = Tag::CALLBACKS[item[:callback]]
122
- item[:callback] = callback.new(pdf, item)
123
+ callback, arg = item[:callback]
124
+ callback_class = Tag::CALLBACKS[callback]
125
+ item[:callback] = callback_class.new(pdf, arg)
123
126
  end
124
127
  end
125
128
 
126
- def bounds(block_styles)
129
+ def adjust_leading(buffer, leading)
130
+ return leading if leading
131
+
132
+ (buffer.map { |item| item[:size] || Context::DEF_FONT_SIZE }.max * 0.055).round(4)
133
+ end
134
+
135
+ def bounds(buffer, options, block_styles)
127
136
  return unless block_styles[:position] == :absolute
128
137
 
129
- y = pdf.bounds.height - (block_styles[:top] || 0)
130
- w = pdf.bounds.width - (block_styles[:left] || 0)
131
- [[block_styles[:left] || 0, y], { width: w }]
138
+ x = if block_styles.include?(:right)
139
+ x1 = pdf.calc_buffer_width(buffer) + block_styles[:right]
140
+ x1 < pdf.page_width ? (pdf.page_width - x1) : 0
141
+ else
142
+ block_styles[:left] || 0
143
+ end
144
+ y = if block_styles.include?(:bottom)
145
+ pdf.calc_buffer_height(buffer, options) + block_styles[:bottom]
146
+ else
147
+ pdf.page_height - (block_styles[:top] || 0)
148
+ end
149
+
150
+ [[x, y], { width: pdf.page_width - x }]
132
151
  end
133
152
  end
134
153
  end
@@ -6,7 +6,7 @@ module PrawnHtml
6
6
  class PdfWrapper
7
7
  extend Forwardable
8
8
 
9
- def_delegators :@pdf, :bounds, :start_new_page
9
+ def_delegators :@pdf, :start_new_page
10
10
 
11
11
  # Wrapper for Prawn PDF Document
12
12
  #
@@ -24,6 +24,46 @@ module PrawnHtml
24
24
  pdf.move_down(move_down)
25
25
  end
26
26
 
27
+ # Calculate the height of a buffer of items
28
+ #
29
+ # @param buffer [Array] Buffer of items
30
+ # @param options [Hash] Output options
31
+ #
32
+ # @return [Float] calculated height
33
+ def calc_buffer_height(buffer, options)
34
+ pdf.height_of_formatted(buffer, options)
35
+ end
36
+
37
+ # Calculate the width of a buffer of items
38
+ #
39
+ # @param buffer [Array] Buffer of items
40
+ #
41
+ # @return [Float] calculated width
42
+ def calc_buffer_width(buffer)
43
+ width = 0
44
+ buffer.each do |item|
45
+ font_family = item[:font] || pdf.font.name
46
+ pdf.font(font_family, size: item[:size] || pdf.font_size) do
47
+ width += pdf.width_of(item[:text], inline_format: true)
48
+ end
49
+ end
50
+ width
51
+ end
52
+
53
+ # Height of the page
54
+ #
55
+ # @return [Float] height
56
+ def page_height
57
+ pdf.bounds.height
58
+ end
59
+
60
+ # Width of the page
61
+ #
62
+ # @return [Float] width
63
+ def page_width
64
+ pdf.bounds.width
65
+ end
66
+
27
67
  # Draw a rectangle
28
68
  #
29
69
  # @param x [Float] left position of the rectangle
@@ -66,12 +106,12 @@ module PrawnHtml
66
106
  # @param buffer [Array] array of text items
67
107
  # @param options [Hash] hash of options
68
108
  # @param bounding_box [Array] bounding box arguments, if bounded
69
- def puts(buffer, options, bounding_box: nil)
70
- return pdf.formatted_text(buffer, options) unless bounding_box
109
+ def puts(buffer, options, bounding_box: nil, left_indent: 0)
110
+ return output_buffer(buffer, options, left_indent: left_indent) unless bounding_box
71
111
 
72
112
  current_y = pdf.cursor
73
113
  pdf.bounding_box(*bounding_box) do
74
- pdf.formatted_text(buffer, options)
114
+ output_buffer(buffer, options, left_indent: left_indent)
75
115
  end
76
116
  pdf.move_cursor_to(current_y)
77
117
  end
@@ -90,5 +130,12 @@ module PrawnHtml
90
130
  private
91
131
 
92
132
  attr_reader :pdf
133
+
134
+ def output_buffer(buffer, options, left_indent:)
135
+ formatted_text = proc { pdf.formatted_text(buffer, options) }
136
+ return formatted_text.call if left_indent == 0
137
+
138
+ pdf.indent(left_indent, 0, &formatted_text)
139
+ end
93
140
  end
94
141
  end
@@ -3,7 +3,7 @@
3
3
  module PrawnHtml
4
4
  class Tag
5
5
  CALLBACKS = {
6
- 'Highlight' => Callbacks::Highlight,
6
+ 'Background' => Callbacks::Background,
7
7
  'StrikeThrough' => Callbacks::StrikeThrough
8
8
  }.freeze
9
9
  TAG_CLASSES = %w[A B Blockquote Body Br Code Del Div H Hr I Img Li Mark Ol P Pre Small Span Sub Sup U Ul].freeze
@@ -15,11 +15,11 @@ module PrawnHtml
15
15
  #
16
16
  # @param tag [Symbol] tag name
17
17
  # @param attributes [Hash] hash of element attributes
18
- # @param element_styles [String] document styles tp apply to the element
19
- def initialize(tag, attributes: {}, element_styles: '')
18
+ # @param options [Hash] options (container width/height/etc.)
19
+ def initialize(tag, attributes: {}, options: {})
20
20
  @tag = tag
21
+ @options = options
21
22
  @attrs = Attributes.new(attributes)
22
- process_styles(element_styles, attributes['style'])
23
23
  end
24
24
 
25
25
  # Is a block tag?
@@ -38,6 +38,15 @@ module PrawnHtml
38
38
  block_styles
39
39
  end
40
40
 
41
+ # Process tag styles
42
+ #
43
+ # @param element_styles [String] extra styles to apply to the element
44
+ def process_styles(element_styles: nil)
45
+ attrs.merge_text_styles!(tag_styles, options: options) if respond_to?(:tag_styles)
46
+ attrs.merge_text_styles!(element_styles, options: options) if element_styles
47
+ attrs.merge_text_styles!(attrs.style, options: options)
48
+ end
49
+
41
50
  # Styles to apply on tag closing
42
51
  #
43
52
  # @return [Hash] hash of styles to apply
@@ -77,10 +86,6 @@ module PrawnHtml
77
86
 
78
87
  private
79
88
 
80
- def process_styles(element_styles, inline_styles)
81
- attrs.merge_text_styles!(tag_styles) if respond_to?(:tag_styles)
82
- attrs.merge_text_styles!(element_styles)
83
- attrs.merge_text_styles!(inline_styles)
84
- end
89
+ attr_reader :options
85
90
  end
86
91
  end
@@ -6,7 +6,13 @@ module PrawnHtml
6
6
  ELEMENTS = [:a].freeze
7
7
 
8
8
  def tag_styles
9
- "href: #{attrs.href}" if attrs.href
9
+ return unless attrs.href
10
+
11
+ <<~STYLES
12
+ color: #00E;
13
+ href: #{attrs.href};
14
+ text-decoration: underline;
15
+ STYLES
10
16
  end
11
17
  end
12
18
  end
@@ -5,9 +5,9 @@ module PrawnHtml
5
5
  class Blockquote < Tag
6
6
  ELEMENTS = [:blockquote].freeze
7
7
 
8
- MARGIN_BOTTOM = 10
9
- MARGIN_LEFT = 25
10
- MARGIN_TOP = 10
8
+ MARGIN_BOTTOM = 12.7
9
+ MARGIN_LEFT = 40.4
10
+ MARGIN_TOP = 12.7
11
11
 
12
12
  def block?
13
13
  true
@@ -4,6 +4,10 @@ module PrawnHtml
4
4
  module Tags
5
5
  class Body < Tag
6
6
  ELEMENTS = [:body].freeze
7
+
8
+ def block?
9
+ true
10
+ end
7
11
  end
8
12
  end
9
13
  end
@@ -5,14 +5,14 @@ module PrawnHtml
5
5
  class Br < Tag
6
6
  ELEMENTS = [:br].freeze
7
7
 
8
- BR_SPACING = Utils.convert_size('12')
8
+ BR_SPACING = Utils.convert_size('17')
9
9
 
10
10
  def block?
11
11
  true
12
12
  end
13
13
 
14
14
  def custom_render(pdf, context)
15
- return if context.last_text_node
15
+ return if context.last_text_node || context.previous_tag != :br
16
16
 
17
17
  pdf.advance_cursor(BR_SPACING)
18
18
  end
@@ -6,7 +6,7 @@ module PrawnHtml
6
6
  ELEMENTS = [:del, :s].freeze
7
7
 
8
8
  def tag_styles
9
- 'callback: StrikeThrough'
9
+ 'text-decoration: line-through'
10
10
  end
11
11
  end
12
12
  end
@@ -6,30 +6,30 @@ module PrawnHtml
6
6
  ELEMENTS = [:h1, :h2, :h3, :h4, :h5, :h6].freeze
7
7
 
8
8
  MARGINS_TOP = {
9
- h1: 25.5,
9
+ h1: 25,
10
10
  h2: 20.5,
11
- h3: 19,
12
- h4: 20,
11
+ h3: 18,
12
+ h4: 21.2,
13
13
  h5: 21.2,
14
- h6: 23.5
14
+ h6: 22.8
15
15
  }.freeze
16
16
 
17
17
  MARGINS_BOTTOM = {
18
- h1: 18.2,
19
- h2: 17.5,
20
- h3: 17.5,
21
- h4: 22,
22
- h5: 22,
23
- h6: 26.5
18
+ h1: 15.8,
19
+ h2: 15.8,
20
+ h3: 15.8,
21
+ h4: 20,
22
+ h5: 21.4,
23
+ h6: 24.8
24
24
  }.freeze
25
25
 
26
26
  SIZES = {
27
- h1: 31,
28
- h2: 23.5,
29
- h3: 18.2,
30
- h4: 16,
27
+ h1: 31.5,
28
+ h2: 24,
29
+ h3: 18.7,
30
+ h4: 15.7,
31
31
  h5: 13,
32
- h6: 10.5
32
+ h6: 10.8
33
33
  }.freeze
34
34
 
35
35
  def block?
@@ -12,16 +12,17 @@ module PrawnHtml
12
12
  def custom_render(pdf, context)
13
13
  parsed_styles = Attributes.parse_styles(attrs.style)
14
14
  block_styles = context.block_styles
15
- evaluated_styles = evaluate_styles(pdf, block_styles.merge(parsed_styles))
15
+ evaluated_styles = adjust_styles(pdf, block_styles.merge(parsed_styles))
16
16
  pdf.image(@attrs.src, evaluated_styles)
17
17
  end
18
18
 
19
19
  private
20
20
 
21
- def evaluate_styles(pdf, img_styles)
21
+ def adjust_styles(pdf, img_styles)
22
22
  {}.tap do |result|
23
- result[:width] = Utils.convert_size(img_styles['width'], pdf.bounds.width) if img_styles.include?('width')
24
- result[:height] = Utils.convert_size(img_styles['height'], pdf.bounds.height) if img_styles.include?('height')
23
+ w, h = img_styles['width'], img_styles['height']
24
+ result[:width] = Utils.convert_size(w, options: pdf.page_width) if w
25
+ result[:height] = Utils.convert_size(h, options: pdf.page_height) if h
25
26
  result[:position] = img_styles[:align] if %i[left center right].include?(img_styles[:align])
26
27
  end
27
28
  end
@@ -5,6 +5,9 @@ module PrawnHtml
5
5
  class Li < Tag
6
6
  ELEMENTS = [:li].freeze
7
7
 
8
+ INDENT_OL = -12
9
+ INDENT_UL = -6
10
+
8
11
  def block?
9
12
  true
10
13
  end
@@ -13,9 +16,21 @@ module PrawnHtml
13
16
  @counter ? "#{@counter}. " : "#{@symbol} "
14
17
  end
15
18
 
19
+ def block_styles
20
+ super.tap do |bs|
21
+ bs[:indent_paragraphs] = @indent
22
+ end
23
+ end
24
+
16
25
  def on_context_add(_context)
17
- @counter = (parent.counter += 1) if parent.is_a? Ol
18
- @symbol = parent.styles[:list_style_type] || '&bullet;' if parent.is_a? Ul
26
+ case parent.class.to_s
27
+ when 'PrawnHtml::Tags::Ol'
28
+ @indent = INDENT_OL
29
+ @counter = (parent.counter += 1)
30
+ when 'PrawnHtml::Tags::Ul'
31
+ @indent = INDENT_UL
32
+ @symbol = parent.styles[:list_style_type] || '&bullet;'
33
+ end
19
34
  end
20
35
  end
21
36
  end
@@ -6,7 +6,7 @@ module PrawnHtml
6
6
  ELEMENTS = [:mark].freeze
7
7
 
8
8
  def tag_styles
9
- 'callback: Highlight'
9
+ 'background: #ff0'
10
10
  end
11
11
  end
12
12
  end
@@ -5,21 +5,38 @@ module PrawnHtml
5
5
  class Ol < Tag
6
6
  ELEMENTS = [:ol].freeze
7
7
 
8
- MARGIN_LEFT = 25
8
+ MARGIN_TOP = 15
9
+ MARGIN_LEFT = 40
10
+ MARGIN_BOTTOM = 15
9
11
 
10
12
  attr_accessor :counter
11
13
 
12
- def initialize(tag, attributes: {}, element_styles: '')
14
+ def initialize(tag, attributes: {}, options: {})
13
15
  super
14
16
  @counter = 0
17
+ @first_level = false
15
18
  end
16
19
 
17
20
  def block?
18
21
  true
19
22
  end
20
23
 
24
+ def on_context_add(context)
25
+ return if context.map(&:tag).count { |el| el == :ol } > 1
26
+
27
+ @first_level = true
28
+ end
29
+
21
30
  def tag_styles
22
- "margin-left: #{MARGIN_LEFT}px"
31
+ if @first_level
32
+ <<~STYLES
33
+ margin-top: #{MARGIN_TOP}px;
34
+ margin-left: #{MARGIN_LEFT}px;
35
+ margin-bottom: #{MARGIN_BOTTOM}px;
36
+ STYLES
37
+ else
38
+ "margin-left: #{MARGIN_LEFT}px"
39
+ end
23
40
  end
24
41
  end
25
42
  end
@@ -5,8 +5,8 @@ module PrawnHtml
5
5
  class P < Tag
6
6
  ELEMENTS = [:p].freeze
7
7
 
8
- MARGIN_BOTTOM = 6
9
- MARGIN_TOP = 6
8
+ MARGIN_BOTTOM = 12.5
9
+ MARGIN_TOP = 12.5
10
10
 
11
11
  def block?
12
12
  true
@@ -5,14 +5,35 @@ module PrawnHtml
5
5
  class Ul < Tag
6
6
  ELEMENTS = [:ul].freeze
7
7
 
8
- MARGIN_LEFT = 25
8
+ MARGIN_TOP = 15
9
+ MARGIN_LEFT = 40
10
+ MARGIN_BOTTOM = 15
11
+
12
+ def initialize(tag, attributes: {}, options: {})
13
+ super
14
+ @first_level = false
15
+ end
9
16
 
10
17
  def block?
11
18
  true
12
19
  end
13
20
 
21
+ def on_context_add(context)
22
+ return if context.map(&:tag).count { |el| el == :ul } > 1
23
+
24
+ @first_level = true
25
+ end
26
+
14
27
  def tag_styles
15
- "margin-left: #{MARGIN_LEFT}px"
28
+ if @first_level
29
+ <<~STYLES
30
+ margin-top: #{MARGIN_TOP}px;
31
+ margin-left: #{MARGIN_LEFT}px;
32
+ margin-bottom: #{MARGIN_BOTTOM}px;
33
+ STYLES
34
+ else
35
+ "margin-left: #{MARGIN_LEFT}px"
36
+ end
16
37
  end
17
38
  end
18
39
  end
@@ -10,6 +10,24 @@ module PrawnHtml
10
10
  'underline' => :underline
11
11
  }.freeze
12
12
 
13
+ # Setup a background callback
14
+ #
15
+ # @param value [String] HTML string color
16
+ #
17
+ # @return [Array] callback name and argument value
18
+ def callback_background(value, options: nil)
19
+ ['Background', convert_color(value, options: options)]
20
+ end
21
+
22
+ # Setup a strike through callback
23
+ #
24
+ # @param value [String] unused
25
+ #
26
+ # @return [Array] callback name and argument value
27
+ def callback_strike_through(value, options: nil)
28
+ ['StrikeThrough', nil]
29
+ end
30
+
13
31
  # Converts a color string
14
32
  #
15
33
  # Supported formats:
@@ -21,7 +39,7 @@ module PrawnHtml
21
39
  # @param value [String] HTML string color
22
40
  #
23
41
  # @return [String] adjusted string color or nil if value is invalid
24
- def convert_color(value)
42
+ def convert_color(value, options: nil)
25
43
  val = value.to_s.strip.downcase
26
44
  return Regexp.last_match[1] if val.match /\A#([a-f0-9]{6})\Z/ # rubocop:disable Performance/RedundantMatch
27
45
 
@@ -42,7 +60,7 @@ module PrawnHtml
42
60
  # @param value [String] string decimal
43
61
  #
44
62
  # @return [Float] converted and rounded float number
45
- def convert_float(value)
63
+ def convert_float(value, options: nil)
46
64
  val = value&.gsub(/[^0-9.]/, '') || ''
47
65
  val.to_f.round(4)
48
66
  end
@@ -50,14 +68,14 @@ module PrawnHtml
50
68
  # Converts a size string
51
69
  #
52
70
  # @param value [String] size string
53
- # @param container_size [Numeric] container size
71
+ # @param options [Numeric] container size
54
72
  #
55
73
  # @return [Float] converted and rounded size
56
- def convert_size(value, container_size = nil)
74
+ def convert_size(value, options: nil)
57
75
  val = value&.gsub(/[^0-9.]/, '') || ''
58
76
  val =
59
- if container_size && value.include?('%')
60
- val.to_f * container_size * 0.01
77
+ if options && value&.include?('%')
78
+ val.to_f * options * 0.01
61
79
  else
62
80
  val.to_f * PrawnHtml::PX
63
81
  end
@@ -69,7 +87,7 @@ module PrawnHtml
69
87
  # @param value [String] string
70
88
  #
71
89
  # @return [Symbol] symbol
72
- def convert_symbol(value)
90
+ def convert_symbol(value, options: nil)
73
91
  value.to_sym if value && !value.match?(/\A\s*\Z/)
74
92
  end
75
93
 
@@ -78,7 +96,7 @@ module PrawnHtml
78
96
  # @param value
79
97
  #
80
98
  # @return value
81
- def copy_value(value)
99
+ def copy_value(value, options: nil)
82
100
  value
83
101
  end
84
102
 
@@ -97,13 +115,13 @@ module PrawnHtml
97
115
  # @param value [String] string
98
116
  #
99
117
  # @return [String] string without quotes at the beginning/ending
100
- def unquote(value)
118
+ def unquote(value, options: nil)
101
119
  (value&.strip || +'').tap do |val|
102
120
  val.gsub!(/\A['"]|["']\Z/, '')
103
121
  end
104
122
  end
105
123
 
106
- module_function :convert_color, :convert_float, :convert_size, :convert_symbol, :copy_value, :normalize_style,
107
- :unquote
124
+ module_function :callback_background, :callback_strike_through, :convert_color, :convert_float, :convert_size,
125
+ :convert_symbol, :copy_value, :normalize_style, :unquote
108
126
  end
109
127
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PrawnHtml # :nodoc:
4
- VERSION = '0.4.2'
4
+ VERSION = '0.5.0'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: prawn-html
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.2
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mattia Roccoberton
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-09-03 00:00:00.000000000 Z
11
+ date: 2021-09-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: oga
@@ -48,7 +48,7 @@ files:
48
48
  - README.md
49
49
  - lib/prawn-html.rb
50
50
  - lib/prawn_html/attributes.rb
51
- - lib/prawn_html/callbacks/highlight.rb
51
+ - lib/prawn_html/callbacks/background.rb
52
52
  - lib/prawn_html/callbacks/strike_through.rb
53
53
  - lib/prawn_html/context.rb
54
54
  - lib/prawn_html/document_renderer.rb