Feat/font autoscale (#288)

* PoC font autosize feature

* Take enumerables or 'auto' keyword for autoscaling

* Allo enumerables of font_size per card, where the largest font size that does not ellipsize is chosen

* Use ellipsize: :autoscale keyword to determine font autosizing

* Use binary search to find the correct font size

* Consider new ellipsize option in testcase

* Document ellipsize: :autoscale option

* Add ellipsize: :autoscale sample to text sample

* Fix double multiplication with Pango::SCALE

* Scale embedded images with text when autoscaling

* Remove stray semicolon

* Return dummy value for FontDescription.size in mock_cairo

* Update regression test data for autoscale feature.

* Add ellipsize: :autoscale example to existing font_autoscale sample

* Add missing :autoscale to list of options in documentation
This commit is contained in:
Mirko K
2019-10-17 11:29:14 -04:00
committed by Andy Meneely
parent 43cca582a6
commit 6d57b3e8c1
28 changed files with 1161 additions and 187 deletions
+6 -2
View File
@@ -35,10 +35,12 @@ module Squib
svg_args = Args::SvgSpecial.new.load!(opts, expand_by: @deck_size, layout: @layout, dpi: @dpi)
rule = { type: :svg, file: ifile, box: box, paint: paint, trans: trans,
adjust: adjust, svg_args: svg_args }
rule[:draw] = Proc.new do |card, x, y|
rule[:draw] = Proc.new do |card, x, y, scale|
i = card.index
b = box[i]
b.x, b.y = x, y
b.width = b.width * scale if b.width.is_a? Numeric
b.height = b.height * scale if b.height.is_a? Numeric
Dir.chdir(@img_dir) do
card.svg(ifile[i].file, svg_args[i], b, paint[i], trans[i])
end
@@ -56,10 +58,12 @@ module Squib
trans = Args::Transform.new.load!(opts, expand_by: @deck_size, layout: @layout, dpi: @dpi)
ifile = Args::InputFile.new.load!(opts, expand_by: @deck_size, layout: @layout, dpi: @dpi)
rule = { type: :png, file: ifile, box: box, paint: paint, trans: trans, adjust: adjust }
rule[:draw] = Proc.new do |card, x, y|
rule[:draw] = Proc.new do |card, x, y, scale|
i = card.index
b = box[i]
b.x, b.y = x, y
b.width = b.width * scale if b.width.is_a? Numeric
b.height = b.height * scale if b.height.is_a? Numeric
Dir.chdir(@img_dir) do
card.png(ifile[i].file, b, paint[i], trans[i])
end
+3 -1
View File
@@ -82,8 +82,10 @@ module Squib
Pango::EllipsizeMode::MIDDLE
when 'end', 'true'
Pango::EllipsizeMode::END
when 'autoscale'
:autoscale
else
raise ArgumentError, 'ellipsize must be one of: none, start, middle, end, true, or false'
raise ArgumentError, 'ellipsize must be one of: none, start, middle, end, true, false or autoscale'
end
end
+37 -9
View File
@@ -76,7 +76,7 @@ module Squib
# # :nodoc:
# # @api private
def embed_images!(embed, str, layout, valign)
def embed_images!(embed, str, layout, valign, scale)
return [] unless embed.rules.any?
layout.markup = str
clean_str = layout.text
@@ -84,7 +84,7 @@ module Squib
EmbeddingUtils.indices(clean_str, embed.rules.keys).each do |key, ranges|
rule = embed.rules[key]
ranges.each do |range|
carve = Pango::Rectangle.new(0, 0, compute_carve(rule, range), 0)
carve = Pango::Rectangle.new(0, 0, compute_carve(rule, range) * scale, 0)
att = Pango::AttrShape.new(carve, carve, rule)
att.start_index = range.first
att.end_index = range.last
@@ -100,7 +100,7 @@ module Squib
y = Pango.pixels(layout.index_to_pos(att.start_index).y) +
rule[:adjust].dy[@index] +
compute_valign(layout, valign, rule[:box].height[@index])
rule[:draw].call(self, x, y)
rule[:draw].call(self, x, y, scale)
cxt.reset_clip
[cxt, att, do_path]
end
@@ -121,19 +121,46 @@ module Squib
end
end
# :nodoc:
# @api private
def text(embed, para, box, trans, draw, dpi)
font_desc = Pango::FontDescription.new(para.font)
font_desc.size = para.font_size * Pango::SCALE if para.font_size.is_a? Numeric
orig_font_size = font_desc.size
# If text autoscaling is enabled, find the largest text size (smaller or equal to the set text size) that fits
if para.ellipsize == :autoscale
para.ellipsize = Pango::EllipsizeMode::END
sizes = sizes = (1 .. font_desc.size).to_a.reverse
# Dummy render to an area outside the card with decreasing font sizes until text no longer ellipsizes
max_fitting_size = sizes.bsearch{ |sz|
font_desc.size = sz
extents = render_text(embed, para, box, trans, draw, dpi, font_desc, orig_font_size, true)
!extents[:ellipsized]
}
if max_fitting_size.nil?
max_fitting_size = sizes.last
Squib.logger.warn{"Could not autosize for Card \##{@index} as minimum specified size #{max_fitting_size} still ellipsizes."}
end
font_desc.size = max_fitting_size
end
render_text(embed, para, box, trans, draw, dpi, font_desc, orig_font_size, false)
end
# :nodoc:
# @api private
def render_text(embed, para, box, trans, draw, dpi, font_desc, orig_font_size, dummy_draw)
Squib.logger.debug {"Rendering text with: \n#{para} \nat:\n #{box} \ndraw:\n #{draw} \ntransform: #{trans}"}
extents = nil
use_cairo do |cc|
cc.set_source_squibcolor(draw.color)
cc.translate(box.x, box.y)
cc.translate(-10000, -10000) if dummy_draw
cc.rotate(trans.angle)
cc.move_to(0, 0)
font_desc = Pango::FontDescription.new(para.font)
font_desc.size = para.font_size * Pango::SCALE unless para.font_size.nil?
layout = cc.create_pango_layout
layout.font_description = font_desc
layout.text = para.str.to_s
@@ -152,7 +179,7 @@ module Squib
layout.justify = para.justify unless para.justify.nil?
layout.spacing = para.spacing unless para.spacing.nil?
embed_images!(embed, para.str, layout, para.valign)
embed_images!(embed, para.str, layout, para.valign, font_desc.size / orig_font_size.to_f)
vertical_start = compute_valign(layout, para.valign, 0)
cc.move_to(0, vertical_start)
@@ -164,8 +191,9 @@ module Squib
stroke_outline!(cc, layout, draw) if draw.stroke_strategy == :fill_first
draw_text_hint(cc, box.x, box.y, layout, para.hint)
extents = { width: layout.extents[1].width / Pango::SCALE,
height: layout.extents[1].height / Pango::SCALE }
warn_if_ellipsized layout
height: layout.extents[1].height / Pango::SCALE,
ellipsized: layout.ellipsized?}
warn_if_ellipsized layout unless dummy_draw
end
return extents
end