1package Font::TTF::OldMort; 2 3=head1 NAME 4 5Font::TTF::OldMort - Glyph Metamorphosis table in a font 6 7=head1 DESCRIPTION 8 9=head1 INSTANCE VARIABLES 10 11=over 12 13=item version 14 15table version number (Fixed: currently 1.0) 16 17=item chains 18 19list of metamorphosis chains, each of which has its own fields: 20 21=over 22 23=item defaultFlags 24 25chain's default subfeature flags (UInt32) 26 27=item featureEntries 28 29list of feature entries, each of which has fields: 30 31=over 32 33=item type 34 35=item setting 36 37=item enable 38 39=item disable 40 41=back 42 43=item subtables 44 45list of metamorphosis subtables, each of which has fields: 46 47=over 48 49=item type 50 51subtable type (0: rearrangement; 1: contextual substitution; 2: ligature; 524: non-contextual substitution; 5: insertion) 53 54=item direction 55 56processing direction ('LR' or 'RL') 57 58=item orientation 59 60applies to text in which orientation ('VH', 'V', or 'H') 61 62=item subFeatureFlags 63 64the subfeature flags controlling whether the table is used (UInt32) 65 66=back 67 68Further fields depend on the type of subtable: 69 70=over 71 72Rearrangement table: 73 74=over 75 76=item classes 77 78array of lists of glyphs 79 80=item states 81 82array of arrays of hashes{'nextState', 'flags'} 83 84=back 85 86Contextual substitution table: 87 88=over 89 90=item classes 91 92array of lists of glyphs 93 94=item states 95 96array of array of hashes{'nextState', 'flags', 'actions'}, where C<actions> 97is an array of two elements which are offsets to be added to [marked, current] 98glyph to get index into C<mappings> (or C<undef> if no mapping to be applied) 99 100=item mappings 101 102list of glyph codes mapped to through the state table mappings 103 104=back 105 106Ligature table: 107 108Non-contextual substitution table: 109 110Insertion table: 111 112=back 113 114=back 115 116=back 117 118=head1 METHODS 119 120=cut 121 122use strict; 123use vars qw(@ISA); 124use Font::TTF::Utils; 125use Font::TTF::AATutils; 126use IO::File; 127 128@ISA = qw(Font::TTF::Table); 129 130=over 131 132=back 133 134=head2 $t->read 135 136Reads the table into memory 137 138=cut 139 140sub read 141{ 142 my ($self) = @_; 143 my ($dat, $fh, $numChains); 144 145 $self->SUPER::read or return $self; 146 147 $fh = $self->{' INFILE'}; 148 149 $fh->read($dat, 8); 150 ($self->{'version'}, $numChains) = TTF_Unpack("fL", $dat); 151 152 my $chains = []; 153 foreach (1 .. $numChains) { 154 my $chainStart = $fh->tell(); 155 $fh->read($dat, 12); 156 my ($defaultFlags, $chainLength, $nFeatureEntries, $nSubtables) = TTF_Unpack("LLSS", $dat); 157 my $featureEntries = []; 158 foreach (1 .. $nFeatureEntries) { 159 $fh->read($dat, 12); 160 my ($featureType, $featureSetting, $enableFlags, $disableFlags) = TTF_Unpack("SSLL", $dat); 161 push @$featureEntries, { 162 'type' => $featureType, 163 'setting' => $featureSetting, 164 'enable' => $enableFlags, 165 'disable' => $disableFlags 166 }; 167 } 168 my $subtables = []; 169 foreach (1 .. $nSubtables) { 170 my $subtableStart = $fh->tell(); 171 $fh->read($dat, 8); 172 my ($length, $coverage, $subFeatureFlags) = TTF_Unpack("SSL", $dat); 173 my $type = $coverage & 0x0007; 174 175 my $subtable = { 176 'type' => $type, 177 'direction' => (($coverage & 0x4000) ? 'RL' : 'LR'), 178 'orientation' => (($coverage & 0x2000) ? 'VH' : ($coverage & 0x8000) ? 'V' : 'H'), 179 'subFeatureFlags' => $subFeatureFlags 180 }; 181 182 if ($type == 0) { # rearrangement 183 my ($classes, $states) = AAT_read_state_table($fh, 0); 184 $subtable->{'classes'} = $classes; 185 $subtable->{'states'} = $states; 186 } 187 188 elsif ($type == 1) { # contextual 189 my $stateTableStart = $fh->tell(); 190 my ($classes, $states, $entries) = AAT_read_state_table($fh, 2); 191 192 $fh->seek($stateTableStart, IO::File::SEEK_SET); 193 $fh->read($dat, 10); 194 my ($stateSize, $classTable, $stateArray, $entryTable, $mappingTables) = unpack("nnnnn", $dat); 195 my $limits = [$classTable, $stateArray, $entryTable, $mappingTables, $length - 8]; 196 197 foreach (@$entries) { 198 my $actions = $_->{'actions'}; 199 foreach (@$actions) { 200 $_ = $_ ? $_ - ($mappingTables / 2) : undef; 201 } 202 } 203 204 $subtable->{'classes'} = $classes; 205 $subtable->{'states'} = $states; 206 $subtable->{'mappings'} = [unpack("n*", AAT_read_subtable($fh, $stateTableStart, $mappingTables, $limits))]; 207 } 208 209 elsif ($type == 2) { # ligature 210 my $stateTableStart = $fh->tell(); 211 my ($classes, $states, $entries) = AAT_read_state_table($fh, 0); 212 213 $fh->seek($stateTableStart, IO::File::SEEK_SET); 214 $fh->read($dat, 14); 215 my ($stateSize, $classTable, $stateArray, $entryTable, 216 $ligActionTable, $componentTable, $ligatureTable) = unpack("nnnnnnn", $dat); 217 my $limits = [$classTable, $stateArray, $entryTable, $ligActionTable, $componentTable, $ligatureTable, $length - 8]; 218 219 my %actions; 220 my $actionLists; 221 foreach (@$entries) { 222 my $offset = $_->{'flags'} & 0x3fff; 223 $_->{'flags'} &= ~0x3fff; 224 if ($offset != 0) { 225 if (not defined $actions{$offset}) { 226 $fh->seek($stateTableStart + $offset, IO::File::SEEK_SET); 227 my $actionList; 228 while (1) { 229 $fh->read($dat, 4); 230 my $action = unpack("N", $dat); 231 my ($last, $store, $component) = (($action & 0x80000000) != 0, ($action & 0xC0000000) != 0, ($action & 0x3fffffff)); 232 $component -= 0x40000000 if $component > 0x1fffffff; 233 $component -= $componentTable / 2; 234 push @$actionList, { 'store' => $store, 'component' => $component }; 235 last if $last; 236 } 237 push @$actionLists, $actionList; 238 $actions{$offset} = $#$actionLists; 239 } 240 $_->{'actions'} = $actions{$offset}; 241 } 242 } 243 244 $subtable->{'componentTable'} = $componentTable; 245 my $components = [unpack("n*", AAT_read_subtable($fh, $stateTableStart, $componentTable, $limits))]; 246 foreach (@$components) { 247 $_ = ($_ - $ligatureTable) . " +" if $_ >= $ligatureTable; 248 } 249 $subtable->{'components'} = $components; 250 251 $subtable->{'ligatureTable'} = $ligatureTable; 252 $subtable->{'ligatures'} = [unpack("n*", AAT_read_subtable($fh, $stateTableStart, $ligatureTable, $limits))]; 253 254 $subtable->{'classes'} = $classes; 255 $subtable->{'states'} = $states; 256 $subtable->{'actionLists'} = $actionLists; 257 } 258 259 elsif ($type == 4) { # non-contextual 260 my ($format, $lookup) = AAT_read_lookup($fh, 2, $length - 8, undef); 261 $subtable->{'format'} = $format; 262 $subtable->{'lookup'} = $lookup; 263 } 264 265 elsif ($type == 5) { # insertion 266 my $stateTableStart = $fh->tell(); 267 my ($classes, $states, $entries) = AAT_read_state_table($fh, 2); 268 269 my %insertListHash; 270 my $insertLists; 271 foreach (@$entries) { 272 my $flags = $_->{'flags'}; 273 my @insertCount = (($flags & 0x03e0) >> 5, ($flags & 0x001f)); 274 my $actions = $_->{'actions'}; 275 foreach (0 .. 1) { 276 if ($insertCount[$_] > 0) { 277 $fh->seek($stateTableStart + $actions->[$_], IO::File::SEEK_SET); 278 $fh->read($dat, $insertCount[$_] * 2); 279 if (not defined $insertListHash{$dat}) { 280 push @$insertLists, [unpack("n*", $dat)]; 281 $insertListHash{$dat} = $#$insertLists; 282 } 283 $actions->[$_] = $insertListHash{$dat}; 284 } 285 else { 286 $actions->[$_] = undef; 287 } 288 } 289 } 290 291 $subtable->{'classes'} = $classes; 292 $subtable->{'states'} = $states; 293 $subtable->{'insertLists'} = $insertLists; 294 } 295 296 else { 297 die "unknown subtable type"; 298 } 299 300 push @$subtables, $subtable; 301 $fh->seek($subtableStart + $length, IO::File::SEEK_SET); 302 } 303 304 push @$chains, { 305 'defaultFlags' => $defaultFlags, 306 'featureEntries' => $featureEntries, 307 'subtables' => $subtables 308 }; 309 $fh->seek($chainStart + $chainLength, IO::File::SEEK_SET); 310 } 311 312 $self->{'chains'} = $chains; 313 314 $self; 315} 316 317=head2 $t->out($fh) 318 319Writes the table to a file either from memory or by copying 320 321=cut 322 323sub out 324{ 325 my ($self, $fh) = @_; 326 327 return $self->SUPER::out($fh) unless $self->{' read'}; 328 329 my $chains = $self->{'chains'}; 330 $fh->print(TTF_Pack("fL", $self->{'version'}, scalar @$chains)); 331 332 foreach (@$chains) { 333 my $chainStart = $fh->tell(); 334 my ($featureEntries, $subtables) = ($_->{'featureEntries'}, $_->{'subtables'}); 335 $fh->print(TTF_Pack("LLSS", $_->{'defaultFlags'}, 0, scalar @$featureEntries, scalar @$subtables)); # placeholder for length 336 337 foreach (@$featureEntries) { 338 $fh->print(TTF_Pack("SSLL", $_->{'type'}, $_->{'setting'}, $_->{'enable'}, $_->{'disable'})); 339 } 340 341 foreach (@$subtables) { 342 my $subtableStart = $fh->tell(); 343 my $type = $_->{'type'}; 344 my $coverage = $type; 345 $coverage += 0x4000 if $_->{'direction'} eq 'RL'; 346 $coverage += 0x2000 if $_->{'orientation'} eq 'VH'; 347 $coverage += 0x8000 if $_->{'orientation'} eq 'V'; 348 349 $fh->print(TTF_Pack("SSL", 0, $coverage, $_->{'subFeatureFlags'})); # placeholder for length 350 351 if ($type == 0) { # rearrangement 352 AAT_write_state_table($fh, $_->{'classes'}, $_->{'states'}, 0); 353 } 354 355 elsif ($type == 1) { # contextual 356 my $stHeader = $fh->tell(); 357 $fh->print(pack("nnnnn", (0) x 5)); # placeholders for stateSize, classTable, stateArray, entryTable, mappingTables 358 359 my $classTable = $fh->tell() - $stHeader; 360 my $classes = $_->{'classes'}; 361 AAT_write_classes($fh, $classes); 362 363 my $stateArray = $fh->tell() - $stHeader; 364 my $states = $_->{'states'}; 365 my ($stateSize, $entries) = AAT_write_states($fh, $classes, $stateArray, $states, 366 sub { 367 my $actions = $_->{'actions'}; 368 ( $_->{'flags'}, @$actions ) 369 } 370 ); 371 372 my $entryTable = $fh->tell() - $stHeader; 373 my $offset = ($entryTable + 8 * @$entries) / 2; 374 foreach (@$entries) { 375 my ($nextState, $flags, @parts) = split /,/; 376 $fh->print(pack("nnnn", $nextState, $flags, map { $_ eq "" ? 0 : $_ + $offset } @parts)); 377 } 378 379 my $mappingTables = $fh->tell() - $stHeader; 380 my $mappings = $_->{'mappings'}; 381 $fh->print(pack("n*", @$mappings)); 382 383 my $loc = $fh->tell(); 384 $fh->seek($stHeader, IO::File::SEEK_SET); 385 $fh->print(pack("nnnnn", $stateSize, $classTable, $stateArray, $entryTable, $mappingTables)); 386 $fh->seek($loc, IO::File::SEEK_SET); 387 } 388 389 elsif ($type == 2) { # ligature 390 my $stHeader = $fh->tell(); 391 $fh->print(pack("nnnnnnn", (0) x 7)); # placeholders for stateSize, classTable, stateArray, entryTable, actionLists, components, ligatures 392 393 my $classTable = $fh->tell() - $stHeader; 394 my $classes = $_->{'classes'}; 395 AAT_write_classes($fh, $classes); 396 397 my $stateArray = $fh->tell() - $stHeader; 398 my $states = $_->{'states'}; 399 400 my ($stateSize, $entries) = AAT_write_states($fh, $classes, $stateArray, $states, 401 sub { 402 ( $_->{'flags'} & 0xc000, $_->{'actions'} ) 403 } 404 ); 405 406 my $actionLists = $_->{'actionLists'}; 407 my %actionListOffset; 408 my $actionListDataLength = 0; 409 my @actionListEntries; 410 foreach (0 .. $#$entries) { 411 my ($nextState, $flags, $offset) = split(/,/, $entries->[$_]); 412 if ($offset eq "") { 413 $offset = undef; 414 } 415 else { 416 if (defined $actionListOffset{$offset}) { 417 $offset = $actionListOffset{$offset}; 418 } 419 else { 420 $actionListOffset{$offset} = $actionListDataLength; 421 my $list = $actionLists->[$offset]; 422 $actionListDataLength += 4 * @$list; 423 push @actionListEntries, $list; 424 $offset = $actionListOffset{$offset}; 425 } 426 } 427 $entries->[$_] = [ $nextState, $flags, $offset ]; 428 } 429 my $entryTable = $fh->tell() - $stHeader; 430 my $ligActionLists = ($entryTable + @$entries * 4 + 3) & ~3; 431 foreach (@$entries) { 432 $_->[2] += $ligActionLists if defined $_->[2]; 433 $fh->print(pack("nn", $_->[0], $_->[1] + $_->[2])); 434 } 435 $fh->print(pack("C*", (0) x ($ligActionLists - $entryTable - @$entries * 4))); 436 437 die "internal error" if $fh->tell() != $ligActionLists + $stHeader; 438 439 my $componentTable = $fh->tell() - $stHeader + $actionListDataLength; 440 my $actionList; 441 foreach $actionList (@actionListEntries) { 442 foreach (0 .. $#$actionList) { 443 my $action = $actionList->[$_]; 444 my $val = $action->{'component'} + $componentTable / 2; 445 $val += 0x40000000 if $val < 0; 446 $val &= 0x3fffffff; 447 $val |= 0x40000000 if $action->{'store'}; 448 $val |= 0x80000000 if $_ == $#$actionList; 449 $fh->print(pack("N", $val)); 450 } 451 } 452 453 die "internal error" if $fh->tell() != $componentTable + $stHeader; 454 455 my $components = $_->{'components'}; 456 my $ligatureTable = $componentTable + @$components * 2; 457 $fh->print(pack("n*", map { (index($_, '+') >= 0 ? $ligatureTable : 0) + $_ } @$components)); 458 459 my $ligatures = $_->{'ligatures'}; 460 $fh->print(pack("n*", @$ligatures)); 461 462 my $loc = $fh->tell(); 463 $fh->seek($stHeader, IO::File::SEEK_SET); 464 $fh->print(pack("nnnnnnn", $stateSize, $classTable, $stateArray, $entryTable, $ligActionLists, $componentTable, $ligatureTable)); 465 $fh->seek($loc, IO::File::SEEK_SET); 466 } 467 468 elsif ($type == 4) { # non-contextual 469 AAT_write_lookup($fh, $_->{'format'}, $_->{'lookup'}, 2, undef); 470 } 471 472 elsif ($type == 5) { # insertion 473 } 474 475 else { 476 die "unknown subtable type"; 477 } 478 479 my $length = $fh->tell() - $subtableStart; 480 my $padBytes = (4 - ($length & 3)) & 3; 481 $fh->print(pack("C*", (0) x $padBytes)); 482 $length += $padBytes; 483 $fh->seek($subtableStart, IO::File::SEEK_SET); 484 $fh->print(pack("n", $length)); 485 $fh->seek($subtableStart + $length, IO::File::SEEK_SET); 486 } 487 488 my $chainLength = $fh->tell() - $chainStart; 489 $fh->seek($chainStart + 4, IO::File::SEEK_SET); 490 $fh->print(pack("N", $chainLength)); 491 $fh->seek($chainStart + $chainLength, IO::File::SEEK_SET); 492 } 493} 494 495=head2 $t->print($fh) 496 497Prints a human-readable representation of the table 498 499=cut 500 501sub print 502{ 503 my ($self, $fh) = @_; 504 505 $self->read; 506 my $feat = $self->{' PARENT'}->{'feat'}; 507 $feat->read; 508 my $post = $self->{' PARENT'}->{'post'}; 509 $post->read; 510 511 $fh = 'STDOUT' unless defined $fh; 512 513 $fh->printf("version %f\n", $self->{'version'}); 514 515 my $chains = $self->{'chains'}; 516 foreach (@$chains) { 517 my $defaultFlags = $_->{'defaultFlags'}; 518 $fh->printf("chain: defaultFlags = %08x\n", $defaultFlags); 519 520 my $featureEntries = $_->{'featureEntries'}; 521 foreach (@$featureEntries) { 522 $fh->printf("\tfeature %d, setting %d : enableFlags = %08x, disableFlags = %08x # '%s: %s'\n", 523 $_->{'type'}, $_->{'setting'}, $_->{'enable'}, $_->{'disable'}, 524 $feat->settingName($_->{'type'}, $_->{'setting'})); 525 } 526 527 my $subtables = $_->{'subtables'}; 528 foreach (@$subtables) { 529 my $type = $_->{'type'}; 530 my $subFeatureFlags = $_->{'subFeatureFlags'}; 531 $fh->printf("\n\t%s table, %s, %s, subFeatureFlags = %08x # %s (%s)\n", 532 subtable_type_($type), $_->{'direction'}, $_->{'orientation'}, $subFeatureFlags, 533 "Default " . ((($subFeatureFlags & $defaultFlags) != 0) ? "On" : "Off"), 534 join(", ", 535 map { 536 join(": ", $feat->settingName($_->{'type'}, $_->{'setting'}) ) 537 } grep { ($_->{'enable'} & $subFeatureFlags) != 0 } @$featureEntries 538 ) ); 539 540 if ($type == 0) { # rearrangement 541 print_classes_($fh, $_, $post); 542 543 $fh->print("\n"); 544 my $states = $_->{'states'}; 545 my @verbs = ( "0", "Ax->xA", "xD->Dx", "AxD->DxA", 546 "ABx->xAB", "ABx->xBA", "xCD->CDx", "xCD->DCx", 547 "AxCD->CDxA", "AxCD->DCxA", "ABxD->DxAB", "ABxD->DxBA", 548 "ABxCD->CDxAB", "ABxCD->CDxBA", "ABxCD->DCxAB", "ABxCD->DCxBA"); 549 foreach (0 .. $#$states) { 550 $fh->printf("\t\tState %d:", $_); 551 my $state = $states->[$_]; 552 foreach (@$state) { 553 my $flags; 554 $flags .= "!" if ($_->{'flags'} & 0x4000); 555 $flags .= "<" if ($_->{'flags'} & 0x8000); 556 $flags .= ">" if ($_->{'flags'} & 0x2000); 557 $fh->printf("\t(%s%d,%s)", $flags, $_->{'nextState'}, $verbs[($_->{'flags'} & 0x000f)]); 558 } 559 $fh->print("\n"); 560 } 561 } 562 563 elsif ($type == 1) { # contextual 564 print_classes_($fh, $_, $post); 565 566 $fh->print("\n"); 567 my $states = $_->{'states'}; 568 foreach (0 .. $#$states) { 569 $fh->printf("\t\tState %d:", $_); 570 my $state = $states->[$_]; 571 foreach (@$state) { 572 my $flags; 573 $flags .= "!" if ($_->{'flags'} & 0x4000); 574 $flags .= "*" if ($_->{'flags'} & 0x8000); 575 my $actions = $_->{'actions'}; 576 $fh->printf("\t(%s%d,%s,%s)", $flags, $_->{'nextState'}, map { defined $_ ? $_ : "=" } @$actions); 577 } 578 $fh->print("\n"); 579 } 580 581 $fh->print("\n"); 582 my $mappings = $_->{'mappings'}; 583 foreach (0 .. $#$mappings) { 584 $fh->printf("\t\tMapping %d: %d [%s]\n", $_, $mappings->[$_], $post->{'VAL'}[$mappings->[$_]]); 585 } 586 } 587 588 elsif ($type == 2) { # ligature 589 print_classes_($fh, $_, $post); 590 591 $fh->print("\n"); 592 my $states = $_->{'states'}; 593 foreach (0 .. $#$states) { 594 $fh->printf("\t\tState %d:", $_); 595 my $state = $states->[$_]; 596 foreach (@$state) { 597 my $flags; 598 $flags .= "!" if ($_->{'flags'} & 0x4000); 599 $flags .= "*" if ($_->{'flags'} & 0x8000); 600 $fh->printf("\t(%s%d,%s)", $flags, $_->{'nextState'}, defined $_->{'actions'} ? $_->{'actions'} : "="); 601 } 602 $fh->print("\n"); 603 } 604 605 $fh->print("\n"); 606 my $actionLists = $_->{'actionLists'}; 607 foreach (0 .. $#$actionLists) { 608 $fh->printf("\t\tList %d:\t", $_); 609 my $actionList = $actionLists->[$_]; 610 $fh->printf("%s\n", join(", ", map { ($_->{'component'} . ($_->{'store'} ? "*" : "") ) } @$actionList)); 611 } 612 613 my $ligatureTable = $_->{'ligatureTable'}; 614 615 $fh->print("\n"); 616 my $components = $_->{'components'}; 617 foreach (0 .. $#$components) { 618 $fh->printf("\t\tComponent %d: %s\n", $_, $components->[$_]); 619 } 620 621 $fh->print("\n"); 622 my $ligatures = $_->{'ligatures'}; 623 foreach (0 .. $#$ligatures) { 624 $fh->printf("\t\tLigature %d: %d [%s]\n", $_, $ligatures->[$_], $post->{'VAL'}[$ligatures->[$_]]); 625 } 626 } 627 628 elsif ($type == 4) { # non-contextual 629 my $lookup = $_->{'lookup'}; 630 $fh->printf("\t\tLookup format %d\n", $_->{'format'}); 631 if (defined $lookup) { 632 foreach (sort { $a <=> $b } keys %$lookup) { 633 $fh->printf("\t\t\t%d [%s] -> %d [%s])\n", $_, $post->{'VAL'}[$_], $lookup->{$_}, $post->{'VAL'}[$lookup->{$_}]); 634 } 635 } 636 } 637 638 elsif ($type == 5) { # insertion 639 print_classes_($fh, $_, $post); 640 641 $fh->print("\n"); 642 my $states = $_->{'states'}; 643 foreach (0 .. $#$states) { 644 $fh->printf("\t\tState %d:", $_); 645 my $state = $states->[$_]; 646 foreach (@$state) { 647 my $flags; 648 $flags .= "!" if ($_->{'flags'} & 0x4000); 649 $flags .= "*" if ($_->{'flags'} & 0x8000); 650 my $actions = $_->{'actions'}; 651 $fh->printf("\t(%s%d,%s,%s)", $flags, $_->{'nextState'}, map { defined $_ ? $_ : "=" } @$actions); 652 } 653 $fh->print("\n"); 654 } 655 656 $fh->print("\n"); 657 my $insertLists = $_->{'insertLists'}; 658 foreach (0 .. $#$insertLists) { 659 my $insertList = $insertLists->[$_]; 660 $fh->printf("\t\tList %d: %s\n", $_, join(", ", map { $_ . " [" . $post->{'VAL'}[$_] . "]" } @$insertList)); 661 } 662 } 663 664 else { 665 # unknown 666 } 667 } 668 } 669} 670 671sub print_classes_ 672{ 673 my ($fh, $subtable, $post) = @_; 674 675 my $classes = $subtable->{'classes'}; 676 foreach (0 .. $#$classes) { 677 my $class = $classes->[$_]; 678 if (defined $class) { 679 $fh->printf("\t\tClass %d:\t%s\n", $_, join(", ", map { $_ . " [" . $post->{'VAL'}[$_] . "]" } @$class)); 680 } 681 } 682} 683 684sub subtable_type_ 685{ 686 my ($val) = @_; 687 my ($res); 688 689 my @types = ( 690 'Rearrangement', 691 'Contextual', 692 'Ligature', 693 undef, 694 'Non-contextual', 695 'Insertion', 696 ); 697 $res = $types[$val] or ('Undefined (' . $val . ')'); 698 699 $res; 700} 701 7021; 703 704=head1 BUGS 705 706None known 707 708=head1 AUTHOR 709 710Jonathan Kew L<http://scripts.sil.org/FontUtils>. 711 712 713=head1 LICENSING 714 715Copyright (c) 1998-2016, SIL International (http://www.sil.org) 716 717This module is released under the terms of the Artistic License 2.0. 718For details, see the full text of the license in the file LICENSE. 719 720 721 722=cut