Skip to content

Commit

Permalink
Merge pull request #32 from blocknotes/v0.6.0
Browse files Browse the repository at this point in the history
v0.6.0
  • Loading branch information
blocknotes committed Sep 14, 2021
2 parents 56baa11 + 5f87d29 commit 9230efc
Show file tree
Hide file tree
Showing 15 changed files with 176 additions and 50 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ CSS attributes (dimensional units are ignored and considered in pixel):
- **top**: see *position (absolute)*
- **width**: for *img* tag, support also percentage, ex. `<img src="image.jpg" style="width: 50%; height: 200px"/>`

The above attributes supports the `initial` value to reset them to their original value.

For colors, the supported formats are:
- 3 hex digits, ex. `color: #FB1`;
- 6 hex digits, ex. `color: #abcdef`;
Expand Down
64 changes: 48 additions & 16 deletions lib/prawn_html/attributes.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
# frozen_string_literal: true

require 'ostruct'
require 'set'

module PrawnHtml
class Attributes < OpenStruct
attr_reader :styles
attr_reader :initial, :styles

STYLES_APPLY = {
block: %i[align bottom leading left margin_left padding_left position right top],
Expand All @@ -19,13 +20,13 @@ class Attributes < OpenStruct
'color' => { key: :color, set: :convert_color },
'font-family' => { key: :font, set: :unquote },
'font-size' => { key: :size, set: :convert_size },
'font-style' => { key: :styles, set: :append_styles },
'font-weight' => { key: :styles, set: :append_styles },
'font-style' => { key: :styles, set: :append_styles, values: %i[italic] },
'font-weight' => { key: :styles, set: :append_styles, values: %i[bold] },
'href' => { key: :link, set: :copy_value },
'letter-spacing' => { key: :character_spacing, set: :convert_float },
'list-style-type' => { key: :list_style_type, set: :unquote },
'text-decoration' => { key: :styles, set: :append_text_decoration },
'vertical-align' => { key: :styles, set: :append_styles },
'text-decoration' => { key: :styles, set: :append_styles, values: %i[underline] },
'vertical-align' => { key: :styles, set: :append_styles, values: %i[subscript superscript] },
'white-space' => { key: :white_space, set: :convert_symbol },
# tag opening styles
'break-before' => { key: :break_before, set: :convert_symbol },
Expand All @@ -44,7 +45,9 @@ class Attributes < OpenStruct
'position' => { key: :position, set: :convert_symbol },
'right' => { key: :right, set: :convert_size, options: :width },
'text-align' => { key: :align, set: :convert_symbol },
'top' => { key: :top, set: :convert_size, options: :height }
'top' => { key: :top, set: :convert_size, options: :height },
# special styles
'text-decoration-line-through' => { key: :callback, set: :callback_strike_through }
}.freeze

STYLES_MERGE = %i[margin_left padding_left].freeze
Expand All @@ -53,6 +56,7 @@ class Attributes < OpenStruct
def initialize(attributes = {})
super
@styles = {} # result styles
@initial = Set.new
end

# Processes the data attributes
Expand All @@ -74,6 +78,33 @@ def merge_text_styles!(text_styles, options: {})
process_styles(hash_styles, options: options) unless hash_styles.empty?
end

# Remove an attribute value from the context styles
#
# @param context_styles [Hash] hash of the context styles that will be updated
# @param rule [Hash] rule from the STYLES_LIST to lookup in the context style for value removal
def remove_value(context_styles, rule)
if rule[:set] == :append_styles
context_styles[rule[:key]] -= rule[:values] if context_styles[:styles]
else
default = Context::DEFAULT_STYLES[rule[:key]]
default ? (context_styles[rule[:key]] = default) : context_styles.delete(rule[:key])
end
end

# Update context styles applying the initial rules (if set)
#
# @param context_styles [Hash] hash of the context styles that will be updated
#
# @return [Hash] the update context styles
def update_styles(context_styles)
initial.each do |rule|
next unless rule

remove_value(context_styles, rule)
end
context_styles
end

class << self
# Merges attributes
#
Expand Down Expand Up @@ -105,29 +136,30 @@ def parse_styles(styles)
def process_styles(hash_styles, options:)
hash_styles.each do |key, value|
rule = evaluate_rule(key, value)
next unless rule

apply_rule!(merged_styles: @styles, rule: rule, value: value, options: options)
end
@styles
end

def evaluate_rule(rule_key, attr_value)
rule = STYLES_LIST[rule_key]
if rule && rule[:set] == :append_text_decoration
return { key: :callback, set: :callback_strike_through } if attr_value == 'line-through'

return { key: :styles, set: :append_styles }
end
rule
key = nil
key = 'text-decoration-line-through' if rule_key == 'text-decoration' && attr_value == 'line-through'
key ||= rule_key
STYLES_LIST[key]
end

def apply_rule!(merged_styles:, rule:, value:, options:)
return unless rule
return (@initial << rule) if value == 'initial'

if rule[:set] == :append_styles
(merged_styles[rule[:key]] ||= []) << Utils.normalize_style(value)
val = Utils.normalize_style(value)
(merged_styles[rule[:key]] ||= []) << val if val
else
opts = rule[:options] ? options[rule[:options]] : nil
merged_styles[rule[:key]] = Utils.send(rule[:set], value, options: opts)
val = Utils.send(rule[:set], value, options: opts)
merged_styles[rule[:key]] = val if val
end
end
end
Expand Down
14 changes: 5 additions & 9 deletions lib/prawn_html/context.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

module PrawnHtml
class Context < Array
DEF_FONT_SIZE = 16 * PX
DEFAULT_STYLES = {
size: 16 * PX
}.freeze

attr_reader :previous_tag
attr_accessor :last_text_node
Expand Down Expand Up @@ -54,9 +56,9 @@ def block_styles
# @return [Hash] the hash of merged styles
def merged_styles
@merged_styles ||=
each_with_object(base_styles) do |element, res|
each_with_object(DEFAULT_STYLES.dup) do |element, res|
evaluate_element_styles(element, res)
element.update_styles(res) if element.respond_to?(:update_styles)
element.update_styles(res)
end
end

Expand All @@ -71,12 +73,6 @@ def remove_last

private

def base_styles
{
size: DEF_FONT_SIZE
}
end

def evaluate_element_styles(element, res)
styles = element.styles.slice(*Attributes::STYLES_APPLY[:text_node])
styles.each do |key, val|
Expand Down
2 changes: 1 addition & 1 deletion lib/prawn_html/document_renderer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ def apply_callbacks(buffer)
def adjust_leading(buffer, leading)
return leading if leading

(buffer.map { |item| item[:size] || Context::DEF_FONT_SIZE }.max * 0.055).round(4)
(buffer.map { |item| item[:size] || Context::DEFAULT_STYLES[:size] }.max * 0.055).round(4)
end

def bounds(buffer, options, block_styles)
Expand Down
13 changes: 6 additions & 7 deletions lib/prawn_html/tag.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,17 @@

module PrawnHtml
class Tag
extend Forwardable

CALLBACKS = {
'Background' => Callbacks::Background,
'StrikeThrough' => Callbacks::StrikeThrough
}.freeze

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

def_delegators :@attrs, :styles, :update_styles

attr_accessor :parent
attr_reader :attrs, :tag

Expand Down Expand Up @@ -45,6 +50,7 @@ def process_styles(element_styles: nil)
attrs.merge_text_styles!(tag_styles, options: options) if respond_to?(:tag_styles)
attrs.merge_text_styles!(element_styles, options: options) if element_styles
attrs.merge_text_styles!(attrs.style, options: options)
attrs.merge_text_styles!(extra_styles, options: options) if respond_to?(:extra_styles)
end

# Styles to apply on tag closing
Expand All @@ -54,13 +60,6 @@ def tag_close_styles
styles.slice(*Attributes::STYLES_APPLY[:tag_close])
end

# Styles hash
#
# @return [Hash] hash of styles
def styles
attrs.styles
end

# Styles to apply on tag opening
#
# @return [Hash] hash of styles to apply
Expand Down
7 changes: 4 additions & 3 deletions lib/prawn_html/tags/a.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@ module Tags
class A < Tag
ELEMENTS = [:a].freeze

def tag_styles
return unless attrs.href
def extra_styles
attrs.href ? "href: #{attrs.href}" : nil
end

def tag_styles
<<~STYLES
color: #00E;
href: #{attrs.href};
text-decoration: underline;
STYLES
end
Expand Down
8 changes: 4 additions & 4 deletions lib/prawn_html/tags/small.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ module Tags
class Small < Tag
ELEMENTS = [:small].freeze

def update_styles(styles)
size = (styles[:size] || Context::DEF_FONT_SIZE) * 0.85
styles[:size] = size
styles
def update_styles(context_styles)
size = (context_styles[:size] || Context::DEFAULT_STYLES[:size]) * 0.85
context_styles[:size] = size
super(context_styles)
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/prawn_html/version.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# frozen_string_literal: true

module PrawnHtml # :nodoc:
VERSION = '0.5.0'
VERSION = '0.6.0'
end
2 changes: 1 addition & 1 deletion spec/integrations/blocks_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

RSpec.describe 'Blocks' do
let(:pdf) { Prawn::Document.new(page_size: 'A4', page_layout: :portrait) }
let(:size) { PrawnHtml::Context::DEF_FONT_SIZE }
let(:size) { PrawnHtml::Context::DEFAULT_STYLES[:size] }

before do
PrawnHtml.append_html(pdf, html)
Expand Down
4 changes: 2 additions & 2 deletions spec/support/test_utils.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
module TestUtils
extend self

def adjust_leading(size = PrawnHtml::Context::DEF_FONT_SIZE)
def adjust_leading(size = PrawnHtml::Context::DEFAULT_STYLES[:size])
(size * 0.055).round(4)
end

Expand All @@ -16,7 +16,7 @@ def default_font_family
end

def default_font_size
PrawnHtml::Context::DEF_FONT_SIZE
PrawnHtml::Context::DEFAULT_STYLES[:size]
end

def font_ascender(font_family: 'Helvetica', font_size: default_font_size)
Expand Down
38 changes: 38 additions & 0 deletions spec/units/prawn_html/attributes_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,44 @@
end
end

describe '#remove_value' do
subject(:remove_value) { attributes.remove_value(context_styles, rule) }

let(:context_styles) { { size: 9.6, styles: %i[bold italic] } }

context 'with a missing rule' do
let(:rule) { { key: :color, set: :convert_color } }

it "doesn't change the context styles" do
expect { remove_value }.not_to change(context_styles, :values)
end
end

context 'with an applied rule' do
let(:rule) { { key: :styles, set: :append_styles, values: %i[bold] } }

it "changes the context styles" do
expect { remove_value }.to change(context_styles, :values).from([9.6, %i[bold italic]]).to([9.6, %i[italic]])
end
end
end

describe '#update_styles' do
subject(:update_styles) { attributes.update_styles(context_styles) }

let(:context_styles) { { size: 9.6 } }
let(:rule) { { key: :styles, set: :append_styles, values: %i[bold] } }

before do
allow(attributes).to receive_messages(initial: Set.new([rule]), remove_value: nil)
end

it 'asks to the attributes to remove the value from the context styles that match the specified rule' do
update_styles
expect(attributes).to have_received(:remove_value).with(context_styles, rule)
end
end

describe '.merge_attr!' do
context 'with an empty key' do
let(:key) { nil }
Expand Down
8 changes: 4 additions & 4 deletions spec/units/prawn_html/context_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -121,12 +121,12 @@ def on_context_remove(context)
subject(:merged_styles) { context.merged_styles }

context 'with no elements' do
it { is_expected.to eq(size: PrawnHtml::Context::DEF_FONT_SIZE) }
it { is_expected.to eq(size: PrawnHtml::Context::DEFAULT_STYLES[:size]) }
end

context 'with some elements' do
let(:tag1) { instance_double(PrawnHtml::Tag, styles: { color: 'fb1', size: 12.34 }) }
let(:tag2) { instance_double(PrawnHtml::Tag, styles: { color: 'abc' }) }
let(:tag1) { instance_double(PrawnHtml::Tag, styles: { color: 'fb1', size: 12.34 }, update_styles: nil) }
let(:tag2) { instance_double(PrawnHtml::Tag, styles: { color: 'abc' }, update_styles: nil) }

before do
context << tag1 << tag2
Expand All @@ -145,7 +145,7 @@ def update_styles(res)
end
end
end
let(:tag1) { instance_double(PrawnHtml::Tag, styles: { color: 'fb1', size: 12.34 }) }
let(:tag1) { instance_double(PrawnHtml::Tag, styles: { color: 'fb1', size: 12.34 }, update_styles: nil) }
let(:tag2) { some_tag_class.new(:some_tag) }

before do
Expand Down
Loading

0 comments on commit 9230efc

Please sign in to comment.