How to calculate line width in Ruby?

String.length will only tell you how many characters are in the string. (In fact, before Ruby 1.9, it will only say how many bytes, which is even less useful.)

I would really like to know how many 'en' wide String. For instance:

 '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 

Even better would be if I could get the first n en of the String string:

 'foo'[0, 2.en] # => "fo" 'filial'[0, 3.en] # => "fili" 'foo bar baz'[0, 4.5en] # => "foo b" 

And it would be even better if I could develop all this. Some people think that space should be 0.25, some believe that it should be 0.33, etc.

+4
source share
5 answers

You must use the RMagick stone to render the Draw object using the desired font (you can upload .ttf files, etc.)

The code looks 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

+12
source

You can try to create a standard table of width proportions for calculating the approximation, basically you need to save the width of each character, and then cross the line that adds the width.

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 seriously, I would start by studying webkit, gecko and OO.org, but I think the algorithms for calculating kerning and size are not trivial.

+4
source

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 this from Ruby, use backlinks:

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

Use ttfunk gem to read metrics from the font file. Then you can get the line width of the text in em. Here is my stretch request so that this example is 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 over by maybe ... 0.015. 
+3
source

This is a good problem!

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

I am using 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 
+1
source

All Articles