How do I calculate a String's width in Ruby?

牧云@^-^@ 提交于 2019-12-03 14:16:10

You should use the RMagick gem to render a "Draw" object using the font you want (you can load .ttf files and such)

The code would look something like this:

   the_text = "TheTextYouWantTheWidthOf"
   label = Draw.new
   label.font = "Vera" #you can also specify a file name... check the rmagick docs to be sure
   label.text_antialias(true)
   label.font_style=Magick::NormalStyle
   label.font_weight=Magick::BoldWeight
   label.gravity=Magick::CenterGravity
   label.text(0,0,the_text)
   metrics = label.get_type_metrics(the_text)
   width = metrics.width
   height = metrics.height

You can see it in action in my button maker here: http://risingcode.com/button/everybodywangchungtonite

You could attempt to create a standarized "width proportion table" to calculate an aproximation, basically you need to store the width of each character and then traverse the string adding up the widths.

I found this table here:

Left, Width, Advance values for ArialBD16 'c' through 'm'
Letter  Left    Width   Advance
c        1       7       9
d        1       8       10
e        1       8       9
f        0       6       5
g        0       9       10
h        1       8       10
i        1       2       4
j       -1       4       4
k        1       8       9
l        1       2       4
m        1       12      14

If you want to get serious, I'd start by looking at webkit, gecko, and OO.org, but I guess the algorithms for kerning and size calculation are not trivial.

If you have ImageMagick installed you can access this information from the command line.

$ convert xc: -font ./.fonts/HelveticaRoundedLTStd-Bd.otf  -pointsize 24 -debug annotate -annotate 0 'MyTestString' null: 2>&1
2010-11-02T19:17:48+00:00 0:00.010 0.010u 6.6.5 Annotate convert[22496]: annotate.c/RenderFreetype/1155/Annotate
  Font ./.fonts/HelveticaRoundedLTStd-Bd.otf; font-encoding none; text-encoding none; pointsize 24
2010-11-02T19:17:48+00:00 0:00.010 0.010u 6.6.5 Annotate convert[22496]: annotate.c/GetTypeMetrics/736/Annotate
  Metrics: text: MyTestString; width: 157; height: 29; ascent: 18; descent: -7; max advance: 24; bounds: 0,-5  20,17; origin: 158,0; pixels per em: 24,24; underline position: -1.5625; underline thickness: 0.78125
2010-11-02T19:17:48+00:00 0:00.010 0.010u 6.6.5 Annotate convert[22496]: annotate.c/RenderFreetype/1155/Annotate
  Font ./.fonts/HelveticaRoundedLTStd-Bd.otf; font-encoding none; text-encoding none; pointsize 24

To do it from Ruby, use backticks:

result = `convert xc: -font #{path_to_font} -pointsize #{size} -debug annotate -annotate 0 '#{string}' null: 2>&1`
if result =~ /width: (\d+);/
  $1
end

Use the ttfunk gem to read the metrics from the font file. You can then get the width of a string of text in em. Here's my pull request to get this example added to the gem.

require 'rubygems'
require 'ttfunk'
require 'valuable'
# Everything you never wanted to know about glyphs:
# http://chanae.walon.org/pub/ttf/ttf_glyphs.htm

# this code is a substantial reworking of:
# https://github.com/prawnpdf/ttfunk/blob/master/examples/metrics.rb

class Font
  attr_reader :file

  def initialize(path_to_file)
    @file = TTFunk::File.open(path_to_file)
  end

  def width_of( string )
    string.split('').map{|char| character_width( char )}.inject{|sum, x| sum + x}
  end

  def character_width( character )
    width_in_units = ( horizontal_metrics.for( glyph_id( character )).advance_width )
    width_in_units.to_f / units_per_em
  end

  def units_per_em
    @u_per_em ||= file.header.units_per_em
  end

  def horizontal_metrics
    @hm = file.horizontal_metrics
  end

  def glyph_id(character)
    character_code = character.unpack("U*").first
    file.cmap.unicode.first[character_code]
  end
end

Here it is in action:

>> din = Font.new("#{File.dirname(__FILE__)}/../../fonts/DIN/DINPro-Light.ttf")
>> din.width_of("Hypertension")
=> 5.832
# which is correct! Hypertension in that font takes up about 5.832 em! It's over by maybe ... 0.015.
Daniel

This is a good problem!

I'm trying to solve it using pango/cairo in ruby for SVG output. I am probably going to use pango to calculate the width and then use a simple svg element.

I use the following code:

require "cairo"
require "pango"

paper = Cairo::Paper::A4_LANDSCAPE
TEXT = "Don't you love me anymore?"
def pac(surface)
        cr = Cairo::Context.new(surface)
        cr.select_font_face("Calibri",
                              Cairo::FONT_SLANT_NORMAL,
                              Cairo::FONT_WEIGHT_NORMAL)
    cr.set_font_size(12)
    extents = cr.text_extents(TEXT)
    puts extents
end

Cairo::ImageSurface.new(*paper.size("pt")) do |surface|
  cr = pac(surface)
end
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!