Class: Font
- Inherits:
-
Object
- Object
- Font
- Defined in:
- lib/skrift/font.rb
Constant Summary collapse
- FILE_MAGIC =
TrueType, TrueType, OpenType
["\0\1\0\0", "true", "OTTO"]
- REPEAT_FLAG =
0x08- X_CHANGE_IS_SMALL =
x2 for Y
0x02- X_CHANGE_IS_ZERO =
x2 for Y
0x10- X_CHANGE_IS_POSITIVE =
x2 for Y
0x10- POINT_IS_ON_CURVE =
0x01- OFFSETS_ARE_LARGE =
0x001- ACTUAL_XY_OFFSETS =
0x002- GOT_A_SINGLE_SCALE =
0x008- THERE_ARE_MORE_COMPONENTS =
0x020- GOT_AN_X_AND_Y_SCALE =
0x040- GOT_A_SCALE_MATRIX =
0x080- HORIZONTAL_KERNING =
0x01- MINIMUM_KERNING =
0x02- CROSS_STREAM_KERNING =
0x04- OVERRIDE_KERNING =
0x08
Instance Attribute Summary collapse
-
#memory ⇒ Object
readonly
Returns the value of attribute memory.
-
#units_per_em ⇒ Object
readonly
Returns the value of attribute units_per_em.
Class Method Summary collapse
-
.load(filename) ⇒ Object
loadfile, 103.
Instance Method Summary collapse
- #at(offset, len = 1) ⇒ Object
- #cmap_fmt12_13(table, char_code, which) ⇒ Object
-
#cmap_fmt4(table, char_code) ⇒ Object
572.
-
#cmap_fmt6(table, char_code) ⇒ Object
621.
-
#compound_outline(offset, rec_depth, outl) ⇒ Object
1057.
- #decode_contour(outl, flags, off, base_point, count) ⇒ Object
- #decode_outline(offset, rec_depth = 0, outl = Outline.new) ⇒ Object
- #each_cmap_entry ⇒ Object
- #geti16(offset) ⇒ Object
- #geti8(offset) ⇒ Object
- #getu16(offset) ⇒ Object
- #getu32(offset) ⇒ Object
- #getu8(offset) ⇒ Object
- #glyph_bbox(outline) ⇒ Object
-
#glyph_id(char_code) ⇒ Object
Maps unicode code points to glyph indices.
- #hor_metrics(glyph) ⇒ Object
-
#initialize(memory) ⇒ Font
constructor
A new instance of Font.
- #kerning ⇒ Object
-
#outline_offset(glyph) ⇒ Object
Returns the offset into the font that the glyph’s outline is stored at.
- #reqtable(tag) ⇒ Object
-
#simple_flags(off, num_pts, flags) ⇒ Object
For a simple outline, determines each point of the outline with a set of flags.
- #simple_outline(offset, num_contours, outl = Outline.new) ⇒ Object
- #simple_points(offset, num_pts, points, base_point) ⇒ Object
- #tables ⇒ Object
Constructor Details
#initialize(memory) ⇒ Font
Returns a new instance of Font.
7 8 9 10 11 12 13 14 15 |
# File 'lib/skrift/font.rb', line 7 def initialize(memory) @memory = memory raise "Unsupported format (magic value: #{at(0,4).inspect})" if !FILE_MAGIC.member?(at(0,4)) head = reqtable("head") @units_per_em = getu16(head + 18) @loca_format = geti16(head + 50) hhea = reqtable("hhea") @num_long_hmtx = getu16(hhea + 34) end |
Instance Attribute Details
#memory ⇒ Object (readonly)
Returns the value of attribute memory.
5 6 7 |
# File 'lib/skrift/font.rb', line 5 def memory @memory end |
#units_per_em ⇒ Object (readonly)
Returns the value of attribute units_per_em.
5 6 7 |
# File 'lib/skrift/font.rb', line 5 def units_per_em @units_per_em end |
Class Method Details
Instance Method Details
#at(offset, len = 1) ⇒ Object
22 23 24 25 |
# File 'lib/skrift/font.rb', line 22 def at(offset, len=1) raise "Out of bounds #{offset} / len #{len} (max: #{@memory.size})" if offset.to_i + len.to_i >= @memory.size @memory[offset..(offset+len-1)] end |
#cmap_fmt12_13(table, char_code, which) ⇒ Object
151 152 153 154 155 156 157 158 159 |
# File 'lib/skrift/font.rb', line 151 def cmap_fmt12_13(table, char_code, which) getu32(table + 12).times do |i| first_code, last_code, glyph_offset = at(table + (i*12) + 16, 12).unpack("N*") next if char_code < first_code || char_code > last_code glyph_offset += char_code-first_code if which == 12 return glyph_offset end return nil end |
#cmap_fmt4(table, char_code) ⇒ Object
572
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 |
# File 'lib/skrift/font.rb', line 109 def cmap_fmt4(table, char_code) # 572 # cmap format 4 only supports the Unicode BMP return nil if char_code > 0xffff seg_count_x2 = getu16(table) raise "Error" if (seg_count_x2 & 1) != 0 or seg_count_x2 == 0 # Find starting positions of the relevant arrays end_codes = table + 8 start_codes = end_codes + seg_count_x2 + 2 id_deltas = start_codes + seg_count_x2 id_range_offsets = id_deltas + seg_count_x2 @ecodes ||= at(end_codes,seg_count_x2 -1).unpack("n*") seg_id_x_x2 = @ecodes.bsearch_index {|i| i > char_code }.to_i * 2 # Look up segment info from the arrays & short circuit if the spec requires start_code = getu16(start_codes + seg_id_x_x2) return 0 if start_code > char_code id_delta = getu16(id_deltas + seg_id_x_x2) if (id_range_offset = getu16(id_range_offsets + seg_id_x_x2)) == 0 # Intentional integer under- and overflow return (char_code + id_delta) & 0xffff end # Calculate offset into glyph array and determine ultimate value id = getu16(id_range_offsets + seg_id_x_x2 + id_range_offset + 2 * (char_code - start_code)) return id ? (id + id_delta) & 0xffff : 0 end |
#cmap_fmt6(table, char_code) ⇒ Object
621
143 144 145 146 147 148 149 |
# File 'lib/skrift/font.rb', line 143 def cmap_fmt6(table, char_code) # 621 first_code, entry_count = at(table,4).unpack("S>*") return nil if !char_code.between?(first_code, 0xffff) char_code -= first_code return nil if (char_code >= entry_count) return getu16(table + 4 + 2 * char_code) end |
#compound_outline(offset, rec_depth, outl) ⇒ Object
1057
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 |
# File 'lib/skrift/font.rb', line 275 def compound_outline(offset, rec_depth, outl) # 1057 # Guard against infinite recursion (compound glyphs that have themselves as component). return nil if rec_depth >= 4 flags = THERE_ARE_MORE_COMPONENTS while flags.allbits?(THERE_ARE_MORE_COMPONENTS) flags, glyph = at(offset,4).unpack("S>*") offset += 4 # We don't implement point matching, and neither does stb truetype return nil if (flags & ACTUAL_XY_OFFSETS) == 0 # Read additional X and Y offsets (in FUnits) of this component. if (flags & OFFSETS_ARE_LARGE) != 0 local = [[1.0, 0.0, geti16(offset)], [0.0,1.0, geti16(offset+2)]] offset += 4 else local = [[1.0, 0.0, geti8(offset)], [0.0, 1.0, geti8(offset)+1]] offset += 2 end if flags.allbits?(GOT_A_SINGLE_SCALE) local[0][0] = local[1][0] = geti16(offset) / 16384.0 offset += 2 elsif flags.allbits?(GOT_AN_X_AND_Y_SCALE) local[0][0] = geti16(offset + 0) / 16384.0 local[1][0] = geti16(offset + 2) / 16384.0 offset += 4 elsif flags.allbits?(GOT_A_SCALE_MATRIX) local[0][0] = geti16(offset + 0) / 16384.0 local[0][1] = geti16(offset + 2) / 16384.0 local[1][0] = geti16(offset + 4) / 16384.0 local[1][1] = geti16(offset + 6) / 16384.0 offset += 8 end outline = outline_offset(glyph) return nil if outline.nil? base_point = outl.points.length return nil if decode_outline(outline, rec_depth + 1, outl).nil? transform_points(local,outl.points[base_point..-1]) end return outl end |
#decode_contour(outl, flags, off, base_point, count) ⇒ Object
231 232 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/skrift/font.rb', line 231 def decode_contour(outl, flags, off, base_point, count) return true if count < 2 # Invisible (no area) if flags[off].allbits?(POINT_IS_ON_CURVE) loose_end = base_point base_point+= 1 off += 1 count -= 1 elsif flags[off + count - 1].allbits?(POINT_IS_ON_CURVE) count -= 1 loose_end = base_point + count else loose_end = outl.points.length outl.points << midpoint(outl.points[base_point], outl.points[base_point + count - 1]) end beg = loose_end ctrl = nil count.times do |i| cur = base_point + i if flags[off+i].allbits?(POINT_IS_ON_CURVE) outl.segments << Outline::Segment.new(beg, cur, ctrl) beg = cur ctrl = nil else if ctrl # 2x control points in a row -> insert midpoint center = outl.points.length outl.points << midpoint(outl.points[ctrl], outl.points[cur]) outl.segments << Outline::Segment.new(beg, center, ctrl) beg = center end ctrl = cur end end outl.segments << Outline::Segment.new(beg, loose_end, ctrl) return true end |
#decode_outline(offset, rec_depth = 0, outl = Outline.new) ⇒ Object
136 137 138 139 140 141 |
# File 'lib/skrift/font.rb', line 136 def decode_outline(offset, rec_depth = 0, outl = Outline.new) num_contours = geti16(offset) return nil if num_contours == 0 return simple_outline(offset + 10, num_contours, outl) if num_contours > 0 return compound_outline(offset + 10, rec_depth, outl) end |
#each_cmap_entry ⇒ Object
61 62 63 64 65 66 67 68 69 70 |
# File 'lib/skrift/font.rb', line 61 def each_cmap_entry cmap = reqtable("cmap") getu16(cmap + 2).times do |idx| entry = cmap + 4 + idx * 8 type = getu16(entry) * 0100 + getu16(entry + 2) table = cmap + getu32(entry + 4) format = getu16(table) yield(type, table, format) end end |
#geti16(offset) ⇒ Object
30 |
# File 'lib/skrift/font.rb', line 30 def geti16(offset); at(offset,2).unpack1("s>"); end |
#geti8(offset) ⇒ Object
28 |
# File 'lib/skrift/font.rb', line 28 def geti8(offset); at(offset).unpack1("c"); end |
#getu16(offset) ⇒ Object
29 |
# File 'lib/skrift/font.rb', line 29 def getu16(offset); at(offset,2).unpack1("S>"); end |
#getu32(offset) ⇒ Object
31 |
# File 'lib/skrift/font.rb', line 31 def getu32(offset); at(offset,4).unpack1("N"); end |
#getu8(offset) ⇒ Object
27 |
# File 'lib/skrift/font.rb', line 27 def getu8(offset); at(offset).ord; end |
#glyph_bbox(outline) ⇒ Object
41 42 43 44 45 |
# File 'lib/skrift/font.rb', line 41 def glyph_bbox(outline) box = at(outline+2, 8).unpack("s>*") raise "Broken bbox #{box.inspect}" if box[2] < box[0] || box[3] < box[1] return box end |
#glyph_id(char_code) ⇒ Object
Maps unicode code points to glyph indices
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
# File 'lib/skrift/font.rb', line 73 def glyph_id(char_code) each_cmap_entry do |type, table, format| if (type == 0004 || type == 0312) return cmap_fmt12_13(table, char_code, 12) if format == 12 return nil end end # If no full repertoire cmap was found, try looking for a Unicode BMP map each_cmap_entry do |type, table, format| if type == 0003 || type == 0301 return cmap_fmt4(table + 6, char_code) if format == 4 return cmap_fmt6(table + 6, char_code) if format == 6 return nil end end return nil end |
#hor_metrics(glyph) ⇒ Object
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 |
# File 'lib/skrift/font.rb', line 92 def hor_metrics(glyph) hmtx = reqtable("hmtx") return nil if hmtx.nil? if glyph < @num_long_hmtx # In long metrics segment? offset = hmtx + 4 * glyph return getu16(offset), geti16(offset + 2) end # Glyph is inside short metrics segment boundary = hmtx + 4 * @num_long_hmtx return nil if boundary < 4 offset = boundary - 4 advance_width = getu16(offset) offset = boundary + 2 * (glyph - @num_long_hmtx) return advance_width, geti16(offset) end |
#kerning ⇒ Object
322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 |
# File 'lib/skrift/font.rb', line 322 def kerning return @kerning if @kerning offset = tables["kern"] return nil if offset.nil? || getu16(offset) != 0 offset += 4 @kerning = {} getu16(offset - 2).times do length,format,flags = at(offset+2,6).unpack("S>CC") offset += 6 if format == 0 && flags.allbits?(HORIZONTAL_KERNING) && flags.nobits?(MINIMUM_KERNING) offset += 8 getu16(offset-8).times do |i| v = geti16(offset+i*6+4) @kerning[at(offset+i*6,4)] = Kerning.new(* flags.allbits?(CROSS_STREAM_KERNING) ? [0,v] : [v,0]) end end offset += length end @kerning end |
#outline_offset(glyph) ⇒ Object
Returns the offset into the font that the glyph’s outline is stored at
48 49 50 51 52 53 54 55 56 57 58 59 |
# File 'lib/skrift/font.rb', line 48 def outline_offset(glyph) # 806 loca = reqtable("loca") glyf = reqtable("glyf") if @loca_format == 0 base = loca + 2 * glyph this = 2 * getu16(base) next_ = 2 * getu16(base + 2) else this, next_ = at(loca + 4 * glyph,8).unpack("NN") end return this == next_ ? nil : glyf + this end |
#reqtable(tag) ⇒ Object
39 |
# File 'lib/skrift/font.rb', line 39 def reqtable(tag) = (tables[tag] or raise "Unable to get table '#{tag}'") |
#simple_flags(off, num_pts, flags) ⇒ Object
For a simple outline, determines each point of the outline with a set of flags
164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 |
# File 'lib/skrift/font.rb', line 164 def simple_flags(off, num_pts, flags) value = 0 repeat = 0 num_pts.times do |i| if repeat > 0 repeat -= 1 else value = getu8(off) off += 1 if value.allbits?(REPEAT_FLAG) repeat = getu8(off) off += 1 end end flags[i] = value end return off end |
#simple_outline(offset, num_contours, outl = Outline.new) ⇒ Object
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 |
# File 'lib/skrift/font.rb', line 210 def simple_outline(offset, num_contours, outl = Outline.new) base_points = outl.points.length num_pts = getu16(offset + (num_contours - 1) *2) + 1 end_pts = at(offset, num_contours*2).unpack("S>*") offset += 2*num_contours # Falling end_pts have no sensible interpretation, so treat as error end_pts.each_cons(2) { |a, b| raise if b < a + 1 } offset += 2 + getu16(offset) flags = simple_points(offset, num_pts, outl.points, base_points) beg = 0 num_contours.times do |i| decode_contour(outl, flags, beg, base_points+beg, end_pts[i] - beg + 1) beg = end_pts[i] + 1 end outl end |
#simple_points(offset, num_pts, points, base_point) ⇒ Object
187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 |
# File 'lib/skrift/font.rb', line 187 def simple_points(offset, num_pts, points, base_point) [].tap do |flags| offset = simple_flags(offset, num_pts, flags) accum = 0.0 accumulate = ->(i, factor) do if flags[i].allbits?(X_CHANGE_IS_SMALL * factor) offset += 1 bit = flags[i].allbits?(X_CHANGE_IS_POSITIVE * factor) ? 1 : 0 accum -= (getu8(offset-1) ^ -bit) + bit elsif flags[i].nobits?(X_CHANGE_IS_ZERO * factor) offset += 2 accum += geti16(offset-2) end accum end num_pts.times {|i| points << Raster::Vector.new(accumulate.call(i,1), 0.0) } accum = 0.0 num_pts.times {|i| points[base_point+i][1] = accumulate.call(i,2) } end end |
#tables ⇒ Object
33 34 35 36 37 |
# File 'lib/skrift/font.rb', line 33 def tables @tables ||= Hash[* getu16(4).times.map {|t| [at(t*16 + 12,4),getu32(t*16 + 20)] }.flatten ] end |