You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

209 lines
6.9 KiB

require 'pango'
require 'squib/args/typographer'
module Squib
class Card
# :nodoc:
# @api private
def draw_text_hint(cc, x, y, layout, color)
color = @deck.text_hint if color.to_s.eql? 'off' and not @deck.text_hint.to_s.eql? 'off'
return if color.to_s.eql? 'off' or color.nil?
# when w,h < 0, it was never set. extents[1] are ink extents
w = layout.width / Pango::SCALE
w = layout.extents[1].width / Pango::SCALE if w < 0
h = layout.height / Pango::SCALE
h = layout.extents[1].height / Pango::SCALE if h < 0
cc.rounded_rectangle(0, 0, w, h, 0, 0)
cc.set_source_color(color)
cc.set_line_width(2.0)
cc.stroke
end
# :nodoc:
# @api private
def set_ellipsize!(layout, ellipsize)
case ellipsize.to_s.downcase
when 'none', 'false'
layout.ellipsize = Pango::Layout::ELLIPSIZE_NONE
when 'start'
layout.ellipsize = Pango::Layout::ELLIPSIZE_START
when 'middle'
layout.ellipsize = Pango::Layout::ELLIPSIZE_MIDDLE
when 'end', 'true'
layout.ellipsize = Pango::Layout::ELLIPSIZE_END
end
end
# :nodoc:
# @api private
def set_wrap!(layout, wrap)
case wrap.to_s.downcase
when 'word'
layout.wrap = Pango::Layout::WRAP_WORD
when 'char'
layout.wrap = Pango::Layout::WRAP_CHAR
when 'word_char', 'true'
layout.wrap = Pango::Layout::WRAP_WORD_CHAR
end
end
# :nodoc:
# @api private
def set_align!(layout, align)
case align.to_s.downcase
when 'left'
layout.alignment = Pango::ALIGN_LEFT
when 'right'
layout.alignment = Pango::ALIGN_RIGHT
when 'center'
layout.alignment = Pango::ALIGN_CENTER
end
end
# :nodoc:
# @api private
def compute_valign(layout, valign)
return 0 unless layout.height > 0
ink_extents = layout.extents[1]
case valign.to_s.downcase
when 'middle'
Pango.pixels( (layout.height - ink_extents.height) / 2)
when 'bottom'
Pango.pixels(layout.height - ink_extents.height)
else
0
end
end
def set_font_rendering_opts!(layout)
font_options = Cairo::FontOptions.new
font_options.antialias = Conf::ANTIALIAS_OPTS[(@deck.antialias || 'gray').downcase]
font_options.hint_metrics = 'on' # TODO make this configurable
font_options.hint_style = 'full' # TODO make this configurable
layout.context.font_options = font_options
end
# :nodoc:
# @api private
def set_wh!(layout, width, height)
layout.width = width * Pango::SCALE unless width.nil? || width == :auto
layout.height = height * Pango::SCALE unless height.nil? || height == :auto
end
# :nodoc:
# @api private
def next_embed(keys, str)
ret = nil
ret_key = nil
keys.each do |key|
i = str.index(key)
ret ||= i
unless i.nil? || i > ret
ret = i
ret_key = key
end
end
ret_key
end
# When we do embedded icons, we create a 3-character string that has a font
# size of zero and a letter-spacing that fits the icon we need. That gives
# us the wrapping behavior we need but no clutter beneath. On most
# platforms, this works fine. On Linux, this creates
# a Cairo transformation matrix that
ZERO_WIDTH_CHAR_SIZE = 0 # this works on most platforms
# some platforms make this Pango pixels (1/1024), others a 1 pt font
ZERO_WIDTH_CHAR_SIZE = 1 if RbConfig::CONFIG['host_os'] === 'linux-gnu'
# :nodoc:
# @api private
def process_embeds(embed, str, layout)
return [] unless embed.rules.any?
layout.markup = str
clean_str = layout.text
draw_calls = []
searches = []
while (key = next_embed(embed.rules.keys, clean_str)) != nil
rule = embed.rules[key]
spacing = rule[:width] * Pango::SCALE
index = clean_str.index(key)
index = clean_str[0..index].bytesize #convert to byte index (bug #57)
str = str.sub(key, "<span size=\"#{ZERO_WIDTH_CHAR_SIZE}\">a<span letter_spacing=\"#{spacing.to_i}\">a</span>a</span>")
layout.markup = str
clean_str = layout.text
searches << { index: index, rule: rule }
end
searches.each do |search|
rect = layout.index_to_pos(search[:index])
x = Pango.pixels(rect.x) + search[:rule][:dx]
y = Pango.pixels(rect.y) + search[:rule][:dy]
draw_calls << {x: x, y: y, draw: search[:rule][:draw]} # defer drawing until we've valigned
end
return draw_calls
end
def stroke_outline!(cc, layout, draw)
if draw.stroke_width > 0
cc.pango_layout_path(layout)
cc.fancy_stroke draw
end
end
# :nodoc:
# @api private
def text(embed, para, box, trans, draw)
Squib.logger.debug {"Placing '#{str}'' with font '#{font}' @ #{box.x}, #{box.y}, color: #{draw.color}, angle: #{angle} etc."}
extents = nil
use_cairo do |cc|
cc.set_source_squibcolor(draw.color)
cc.translate(box.x, box.y)
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
if para.markup
para.str = @deck.typographer.process(layout.text)
layout.markup = para.str
end
set_font_rendering_opts!(layout)
set_wh!(layout, box.width, box.height)
set_wrap!(layout, para.wrap)
set_ellipsize!(layout, para.ellipsize)
set_align!(layout, para.align)
layout.justify = para.justify unless para.justify.nil?
layout.spacing = para.spacing * Pango::SCALE unless para.spacing.nil?
cc.update_pango_layout(layout)
embed_draws = process_embeds(embed, para.str, layout)
vertical_start = compute_valign(layout, para.valign)
cc.move_to(0, vertical_start)
cc.update_pango_layout(layout)
cc.show_pango_layout(layout)
stroke_outline!(cc, layout, draw)
begin
embed_draws.each { |ed| ed[:draw].call(self, ed[:x], ed[:y] + vertical_start) }
rescue Exception => e
puts "====EXCEPTION!===="
puts e
puts "If this was a non-invertible matrix error, this is a known issue with a potential workaround. Please report it at: https://github.com/andymeneely/squib/issues/55"
puts "=================="
raise e
end
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 }
end
return extents
end
end
end