String.length только скажет мне, сколько символов в строке. (Фактически, до Ruby 1.9 он сообщал мне только количество байтов, что было еще менее полезно.)
Я действительно хотел бы узнать, сколько строк в ширину. Например:
'foo'.width
# => 3
'moo'.width
# => 3.5 # m's, w's, etc. are wide
'foi'.width
# => 2.5 # i's, j's, etc. are narrow
'foo bar'.width
# => 6.25 # spaces are very narrow
Еще лучше было бы, если бы я мог получить первый n en строки:
'foo'[0, 2.en]
# => "fo"
'filial'[0, 3.en]
# => "fili"
'foo bar baz'[0, 4.5en]
# => "foo b"
И все же было бы лучше, если бы я мог все продумать. Кто-то думает, что интервал должен быть 0,25 пенни, кто-то думает, что он должен быть 0,33 и т. д.
И вы хотите это независимо от шрифта? Я не думаю, что это возможно
Я согласен с другими комментаторами, если вы не знаете шрифт, это невозможно.
Вариант 1 предполагает, что если вы используете шрифт фиксированной ширины, вы должны использовать .legnth вместо .width. Вариант 2 - иметь объект Font. AFAIK, в Ruby нет класса Font.

Вы можете попытаться создать стандартизированную «таблицу пропорций ширины» для вычисления приближения, в основном вам нужно сохранить ширину каждого символа, а затем пересечь строку, складывая ширину.
Я нашел эту таблицу здесь:
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
Если вы хотите стать серьезным, я бы начал с просмотра webkit, gecko и OO.org, но я думаю, что алгоритмы кернинга и расчета размера нетривиальны.
Вы должны использовать гем RMagick для рендеринга объекта "Draw" с использованием нужного шрифта (вы можете загружать файлы .ttf и т. д.)
Код будет выглядеть примерно так:
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
Вы можете увидеть это в действии в моем конструкторе кнопок здесь: http://risingcode.com/button/everybodywangchungtonite
просто не забудьте установить размер шрифта с помощью «label.pointsize = THE_SIZE», иначе RMagick по умолчанию будет использовать размер шрифта 12.
ссылка производителя кнопки кажется мертва
Если у вас установлен ImageMagick, вы можете получить доступ к этой информации из командной строки.
$ 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
Чтобы сделать это из Ruby, используйте обратные кавычки:
result = `convert xc: -font #{path_to_font} -pointsize #{size} -debug annotate -annotate 0 '#{string}' null: 2>&1`
if result =~ /width: (\d+);/
$1
end
Поскольку драгоценный камень rmagick в настоящее время не обслуживается, это привлекательный вариант.
Это хорошая проблема!
Я пытаюсь решить эту проблему, используя pango / cairo в ruby для вывода SVG. Я, вероятно, собираюсь использовать pango для вычисления ширины, а затем использовать простой элемент svg.
Я использую следующий код:
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
что вы в конечном итоге использовали для этого? Пытаюсь сделать что-то подобное, но без RMagick (и легковесного).
Используйте гем ttfunk для чтения показателей из файла шрифта. Затем вы можете получить ширину строки текста в em. Вот мой запрос на добавление этого примера в драгоценный камень.
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
Вот он в действии:
>> 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.
Как только мне пришлось отобразить массив строк (содержащий ближайшие мировые дни, текущие именные дни и т. д.) В двух строках, поместив разрыв строки после соответствующей строки, мне пришлось определить совокупную ширину строк, напечатанных в Arial. Я открыл свой текстовый редактор, набрал алфавит и разделил символы на два класса в зависимости от их ширины в данном шрифте:
w = "023456789AÁBCDEFGHJKLMNOÓÖŐPQRSTUÚÜŰWZYaábcdeghksoóöőpqwuúüűzymn".chars.yield_self{|z| z.zip(Array.new(z.size){1.5})}.to_h.merge("1rfiíjltIÍ ".chars.yield_self{|z| z.zip(Array.new(z.size){1})}.to_h)
w.default=1
nntd=["01-21:A vallások világnapja", "01-19:Kanut", "Kenéz", "Margaréta", "Márió", "Máriusz", "Megyer", "Sára", "Szultána", "Vázsony"]
nntd.sort_by!{|z| z.chars.map{|q| w[q]}.sum}.reverse
Затем я смог определить положение переноса строки:
ind=nntd.collect.with_index.find_index{|z,i| nntd[0..i].join.chars.map{|q| w[q]}.sum >=nntd.join.chars.map{|q| w[q]}.sum/2}
t=[nntd[0..ind],nntd[ind+1..-1]].map{|z| z.join(",")}.join("\n")
В конце концов, у меня получился красивый сбалансированный выход, разделенный на две строки:
01–21: Валласок вилагнапья, 01–19: Маргарета, Султана Вазони, Мариуш, Мегьер, Кенес, Канут, Марио, Сара
Таким образом, я могу мгновенно проверить наступающие мировые дни и текущие именные дни.
Это должно сильно зависеть от шрифта, используемого для рендеринга строки, не так ли?