Class: GD2::Font::TrueType

Inherits:
Object
  • Object
show all
Defined in:
lib/gd2/font.rb

Defined Under Namespace

Classes: FontconfigError, FreeTypeError

Constant Summary collapse

CHARMAP_UNICODE =
0
CHARMAP_SHIFT_JIS =
1
CHARMAP_BIG5 =
2
FTEX_LINESPACE =
1
FTEX_CHARMAP =
2
FTEX_RESOLUTION =
4
FTEX_DISABLE_KERNING =
8
FTEX_XSHOW =
16
FTEX_FONTPATHNAME =
32
FTEX_FONTCONFIG =
64
FTEX_RETURNFONTPATHNAME =
128

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(fontname, ptsize, options = {}) ⇒ TrueType

Instantiate a TrueType font given by fontname (either a pathname or a fontconfig pattern if fontconfig is enabled) and ptsize (a point size given as a floating point number).

The possible options are:

  • :linespacing => The desired line spacing for multiline text, expressed as a multiple of the font height. A line spacing of 1.0 is the minimum to guarantee that lines of text do not collide. The default according to GD is 1.05.

  • :charmap => Specify a preference for Unicode, Shift_JIS, or Big5 character encoding. Use one of the constants Font::TrueType::CHARMAP_UNICODE, Font::TrueType::CHARMAP_SHIFT_JIS, or Font::TrueType::CHARMAP_BIG5.

  • :hdpi => The horizontal resolution hint for the rendering engine. The default according to GD is 96 dpi.

  • :vdpi => The vertical resolution hint for the rendering engine. The default according to GD is 96 dpi.

  • :dpi => A shortcut to specify both :hdpi and :vdpi.

  • :kerning => A boolean to specify whether kerning tables should be used, if fontconfig is available. The default is true.


195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
# File 'lib/gd2/font.rb', line 195

def initialize(fontname, ptsize, options = {})
  @fontname, @ptsize = fontname, ptsize.to_f
  @linespacing = options.delete(:linespacing)
  @linespacing = @linespacing.to_f if @linespacing
  @charmap = options.delete(:charmap)
  @hdpi = options.delete(:hdpi)
  @vdpi = options.delete(:vdpi)
  if dpi = options.delete(:dpi)
    @hdpi ||= dpi
    @vdpi ||= dpi
  end
  @kerning = options.delete(:kerning)
  @kerning = true if @kerning.nil?

  self.class.register(self)

  # Get the font path (and verify existence of file)

  strex = strex(false, true)
  args = [ nil, nil, 0, @fontname, @ptsize, 0.0, 0, 0, '', strex ]
  r = ::GD2::GD2FFI.send(:gdImageStringFTEx, *args)

  raise FreeTypeError.new(r.read_string) unless r.null?
  @fontpath = strex[:fontpath].read_string
ensure
  ::GD2::GD2FFI.send(:gdFree, strex[:fontpath])
end

Instance Attribute Details

#charmapObject (readonly)

The chosen charmap


157
158
159
# File 'lib/gd2/font.rb', line 157

def charmap
  @charmap
end

#fontpathObject (readonly)

The effective path to this TrueType font


151
152
153
# File 'lib/gd2/font.rb', line 151

def fontpath
  @fontpath
end

#hdpiObject (readonly)

The chosen horizontal resolution hint


160
161
162
# File 'lib/gd2/font.rb', line 160

def hdpi
  @hdpi
end

#kerningObject (readonly)

Whether kerning is desired


166
167
168
# File 'lib/gd2/font.rb', line 166

def kerning
  @kerning
end

#linespacingObject (readonly)

The chosen linespacing


154
155
156
# File 'lib/gd2/font.rb', line 154

def linespacing
  @linespacing
end

#vdpiObject (readonly)

The chosen vertical resolution hint


163
164
165
# File 'lib/gd2/font.rb', line 163

def vdpi
  @vdpi
end

Class Method Details

.fontconfigObject

:nodoc:


123
124
125
126
127
128
129
# File 'lib/gd2/font.rb', line 123

def self.fontconfig #:nodoc:
  if Thread.current[:fontconfig].nil?
    Thread.current[:fontconfig] = false
  end

  Thread.current[:fontconfig]
end

.fontconfig=(want) ⇒ Object

Set whether fontconfig support should be enabled. To use this, the GD library must have been built with fontconfig support. Raises an error if fontconfig support is unavailable.

Raises:


140
141
142
143
144
# File 'lib/gd2/font.rb', line 140

def self.fontconfig=(want)
  avail = !::GD2::GD2FFI.send(:gdFTUseFontConfig, want ? 1 : 0).zero?
  raise FontconfigError, 'Fontconfig not available' if want && !avail
  Thread.current[:fontconfig] = !!want
end

.fontconfig?Boolean

Return a boolean indicating whether fontconfig support has been enabled. The default is false.

Returns:

  • (Boolean)

133
134
135
# File 'lib/gd2/font.rb', line 133

def self.fontconfig?
  fontconfig
end

.register(font) ⇒ Object

:nodoc:


95
96
97
98
99
100
101
102
103
104
105
# File 'lib/gd2/font.rb', line 95

def self.register(font) #:nodoc:
  Thread.current[:fontcount] ||= 0
  Thread.current[:fontcount] += 1

  if Thread.current[:fontcount].zero?
    raise FreeTypeError, 'FreeType library failed to initialize' unless
      ::GD2::GD2FFI.send(:gdFontCacheSetup).zero?
  end

  ObjectSpace.define_finalizer(font, font_finalizer)
end

Instance Method Details

#bounding_rectangle(string, angle = 0.0) ⇒ Object

Return a hash describing the rectangle that would enclose the given string rendered in this font at the given angle. The returned hash contains the following keys:

  • :lower_left => The [x, y] coordinates of the lower left corner.

  • :lower_right => The [x, y] coordinates of the lower right corner.

  • :upper_right => The [x, y] coordinates of the upper right corner.

  • :upper_left => The [x, y] coordinates of the upper left corner.

  • :position => An array of floating point character position offsets for each character of the string, beginning with 0.0. The array also includes a final position indicating where the last character ends.

The upper, lower, left, and right references are relative to the text of the string, regardless of the angle.


295
296
297
298
299
300
301
302
303
304
305
# File 'lib/gd2/font.rb', line 295

def bounding_rectangle(string, angle = 0.0)
  data = draw(nil, 0, 0, angle, string, 0)

  if string.length == 1
    # gd annoyingly fails to provide xshow data for strings of length 1
    position = draw(nil, 0, 0, angle, string + ' ', 0)[:position]
    data[:position] = position[0...-1]
  end

  data
end

#draw(image_ptr, x, y, angle, string, fg) ⇒ Object

:nodoc:

Raises:


233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
# File 'lib/gd2/font.rb', line 233

def draw(image_ptr, x, y, angle, string, fg)  #:nodoc:
  brect = FFI::MemoryPointer.new(:int, 8)
  strex = strex(true)
  args = [ image_ptr, brect, fg, @fontname, @ptsize, angle.to_f, x.to_i, y.to_i, string.gsub('&', '&'), strex ]

  r = ::GD2::GD2FFI.send(:gdImageStringFTEx, *args)
  raise FreeTypeError.new(r.read_string) unless r.null?
  brect = brect.read_array_of_int(8)

  if !strex[:xshow].null? && (xshow = strex[:xshow])
    begin
      xshow = xshow.read_string.split(' ').map { |e| e.to_f }
    ensure
      ::GD2::GD2FFI.send(:gdFree, strex[:xshow])
    end
  else
    xshow = []
  end

  sum = 0.0
  position = Array.new(xshow.length + 1)
  xshow.each_with_index do |advance, i|
    position[i] = sum
    sum += advance
  end
  position[-1] = sum

  { :lower_left   => [brect[0], brect[1]],
    :lower_right  => [brect[2], brect[3]],
    :upper_right  => [brect[4], brect[5]],
    :upper_left   => [brect[6], brect[7]],
    :position     => position
  }
end

#draw_circle(image_ptr, cx, cy, radius, text_radius, fill_portion, top, bottom, fgcolor) ⇒ Object

Raises:


268
269
270
271
272
273
274
275
276
277
278
279
# File 'lib/gd2/font.rb', line 268

def draw_circle(
  image_ptr, cx, cy, radius, text_radius, fill_portion,
  top, bottom, fgcolor
) #:nodoc:
  r = ::GD2::GD2FFI.send(
    :gdImageStringFTCircle, image_ptr, cx.to_i, cy.to_i,
    radius.to_f, text_radius.to_f, fill_portion.to_f, @fontname, @ptsize,
    top || '', bottom || '', fgcolor.to_i
  )
  raise FreeTypeError.new(r.read_string) unless r.null?
  nil
end

#inspectObject

:nodoc:


223
224
225
226
227
228
229
230
231
# File 'lib/gd2/font.rb', line 223

def inspect   #:nodoc:
  result  = "#<#{self.class} #{@fontpath.inspect}, #{@ptsize}"
  result += ", :linespacing => #{@linespacing}" if @linespacing
  result += ", :charmap => #{@charmap}" if @charmap
  result += ", :hdpi => #{@hdpi}" if @hdpi
  result += ", :vdpi => #{@vdpi}" if @vdpi
  result += ", :kerning => #{@kerning}" unless @kerning
  result += '>'
end