fontisan 0.2.5 → 0.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.adoc CHANGED
@@ -10,7 +10,7 @@ Fontisan is a Ruby gem providing font analysis tools and utilities.
10
10
 
11
11
  It is designed as a pure Ruby implementation with full object-oriented
12
12
  architecture, supporting extraction of information from OpenType and TrueType
13
- fonts (OTF, TTF, TTC).
13
+ fonts (OTF, TTF, OTC, TTC, dfont).
14
14
 
15
15
  The gem provides both a Ruby library API and a command-line interface, with
16
16
  structured output formats (YAML, JSON, text) via lutaml-model.
@@ -71,6 +71,7 @@ gem install fontisan
71
71
  * Font validation with multiple severity levels
72
72
  * Collection management (pack/unpack TTC/OTC files with table deduplication)
73
73
  * Support for TTF, OTF, TTC, OTC font formats (production ready)
74
+ * Apple legacy font support: 'true' signature TrueType fonts and dfont (Data Fork Font) format
74
75
  * WOFF format support (reading complete, writing functional, pending full integration)
75
76
  * WOFF2 format support (reading complete with table transformations, writing planned)
76
77
  * SVG font generation (complete)
@@ -100,12 +101,13 @@ and font metrics.
100
101
 
101
102
  [source,shell]
102
103
  ----
104
+
103
105
  $ fontisan info FONT_FILE [--format FORMAT] [--brief]
104
106
  ----
105
107
 
106
108
  Where,
107
109
 
108
- `FONT_FILE`:: Path to the font file (OTF, TTF, TTC, OTF)
110
+ `FONT_FILE`:: Path to the font file (OTF, TTF, TTC, OTC, dfont)
109
111
  `FORMAT`:: Output format: `text` (default), `json`, or `yaml`
110
112
  `--brief`:: Show only basic font information
111
113
 
@@ -182,7 +184,7 @@ Family: Noto Serif CJK JP ExtraLight
182
184
  ====
183
185
  [source,shell]
184
186
  ----
185
- $ fontisan info spec/fixtures/fonts/MonaSans/mona-sans-2.0.8/googlefonts/variable/MonaSans[wdth,wght].ttf --brief
187
+ $ fontisan info spec/fixtures/fonts/MonaSans/variable/MonaSans[wdth,wght].ttf --brief
186
188
 
187
189
  Font type: TrueType (Variable)
188
190
  Family: Mona Sans ExtraLight
@@ -366,7 +368,7 @@ $ fontisan tables FONT_FILE [--format FORMAT]
366
368
 
367
369
  Where,
368
370
 
369
- `FONT_FILE`:: Path to the font file (OTF, TTF, or TTC)
371
+ `FONT_FILE`:: Path to the font file (OTF, TTF, TTC, OTC, dfont)
370
372
  `FORMAT`:: Output format: `text` (default), `json`, or `yaml`
371
373
 
372
374
  .List of OpenType tables in Libertinus Serif Regular
@@ -424,10 +426,6 @@ tables:
424
426
  length: 17870
425
427
  offset: 542992
426
428
  checksum: 701383168
427
- - tag: OS/2
428
- length: 96
429
- offset: 392
430
- checksum: 1193824355
431
429
  ...
432
430
  ----
433
431
  ====
@@ -451,7 +449,7 @@ $ fontisan glyphs FONT_FILE [--format FORMAT]
451
449
 
452
450
  Where,
453
451
 
454
- `FONT_FILE`:: Path to the font file (OTF, TTF, or TTC)
452
+ `FONT_FILE`:: Path to the font file (OTF, TTF, TTC, OTC, dfont)
455
453
  `FORMAT`:: Output format: `text` (default), `json`, or `yaml`
456
454
 
457
455
 
@@ -466,7 +464,7 @@ $ fontisan glyphs spec/fixtures/fonts/libertinus/ttf/LibertinusSerif-Regular.ttf
466
464
  [source,text]
467
465
  ----
468
466
  Glyph count: 2731
469
- Source: post_2.0
467
+ Source: post-2.0
470
468
 
471
469
  Glyph names:
472
470
  0 .notdef
@@ -512,7 +510,7 @@ $ fontisan unicode FONT_FILE [--format FORMAT]
512
510
 
513
511
  Where:
514
512
 
515
- `FONT_FILE`:: Path to the font file (OTF, TTF, or TTC)
513
+ `FONT_FILE`:: Path to the font file (OTF, TTF, TTC, OTC, dfont)
516
514
  `FORMAT`:: Output format: `text` (default), `json`, or `yaml`
517
515
 
518
516
 
@@ -802,7 +800,7 @@ $ fontisan scripts FONT_FILE [--format FORMAT]
802
800
 
803
801
  Where,
804
802
 
805
- `FONT_FILE`:: Path to the font file (OTF, TTF, or TTC)
803
+ `FONT_FILE`:: Path to the font file (OTF, TTF, TTC, OTC, dfont)
806
804
  `FORMAT`:: Output format: `text` (default), `json`, or `yaml`
807
805
 
808
806
 
@@ -845,7 +843,7 @@ $ fontisan features FONT_FILE [--script SCRIPT] [--format FORMAT]
845
843
 
846
844
  Where,
847
845
 
848
- `FONT_FILE`:: Path to the font file (OTF, TTF, or TTC)
846
+ `FONT_FILE`:: Path to the font file (OTF, TTF, TTC, OTC, dfont)
849
847
  `SCRIPT`:: Optional 4-character script tag (e.g., `latn`, `cyrl`, `arab`). If not specified, shows features for all scripts
850
848
  `FORMAT`:: Output format: `text` (default), `json`, or `yaml`
851
849
 
@@ -942,7 +940,7 @@ $ fontisan dump-table FONT_FILE TABLE_TAG
942
940
 
943
941
  Where,
944
942
 
945
- `FONT_FILE`:: Path to the font file (OTF, TTF, or TTC)
943
+ `FONT_FILE`:: Path to the font file (OTF, TTF, TTC, OTC, dfont)
946
944
  `TABLE_TAG`:: Four-character table tag (e.g., `name`, `head`, `GSUB`, `GPOS`)
947
945
 
948
946
 
@@ -980,7 +978,7 @@ $ fontisan export FONT_FILE [--output FILE] [--format FORMAT] [--tables TABLES]
980
978
 
981
979
  Where,
982
980
 
983
- `FONT_FILE`:: Path to the font file (OTF, TTF, or TTC)
981
+ `FONT_FILE`:: Path to the font file (OTF, TTF, TTC, OTC, dfont)
984
982
  `--output FILE`:: Output file path (default: stdout)
985
983
  `--format FORMAT`:: Export format: `yaml` (default), `json`, or `ttx`
986
984
  `--tables TABLES`:: Specific tables to export (space-separated list)
@@ -1243,7 +1241,7 @@ validator = Fontisan::Validators::OpenTypeValidator.new
1243
1241
  report = validator.validate(font)
1244
1242
 
1245
1243
  # Check individual results
1246
- name_check = report.result_of(:name_version)
1244
+ name_check = report.result_of(:name_validation)
1247
1245
  puts name_check.passed?
1248
1246
  puts name_check.severity
1249
1247
  ----
@@ -1281,21 +1279,21 @@ class MyFontValidator < Fontisan::Validators::Validator
1281
1279
 
1282
1280
  def define_checks
1283
1281
  # Check name table
1284
- check_table :name_version, 'name', severity: :error do |table|
1285
- table.valid_version?
1286
- end
1287
-
1288
- check_table :family_name, 'name', severity: :error do |table|
1289
- table.family_name_present?
1282
+ check_table :name_validation, 'name', severity: :error do |table|
1283
+ table.valid_version? &&
1284
+ table.valid_encoding_heuristics? &&
1285
+ table.family_name_present? &&
1286
+ table.postscript_name_valid?
1290
1287
  end
1291
1288
 
1292
1289
  # Check head table
1293
- check_table :head_magic, 'head', severity: :error do |table|
1294
- table.valid_magic?
1295
- end
1296
-
1297
- check_table :units_per_em, 'head', severity: :error do |table|
1298
- table.valid_units_per_em?
1290
+ check_table :head_validation, 'head', severity: :error do |table|
1291
+ table.valid_magic? &&
1292
+ table.valid_version? &&
1293
+ table.valid_units_per_em? &&
1294
+ table.valid_bounding_box? &&
1295
+ table.valid_index_to_loc_format? &&
1296
+ table.valid_glyph_data_format?
1299
1297
  end
1300
1298
 
1301
1299
  # Check structure
@@ -1310,14 +1308,14 @@ font = Fontisan::FontLoader.load('font.ttf')
1310
1308
  validator = MyFontValidator.new
1311
1309
  report = validator.validate(font)
1312
1310
 
1313
- # Check results
1311
+ # Check validation results
1314
1312
  puts report.valid? # => true/false
1315
1313
  puts report.status # => "valid", "valid_with_warnings", "invalid"
1316
1314
  puts report.summary.errors # => number of errors
1317
1315
  puts report.summary.warnings # => number of warnings
1318
1316
 
1319
1317
  # Query specific checks
1320
- result = report.result_of(:name_version)
1318
+ result = report.result_of(:name_validation)
1321
1319
  puts result.passed? # => true/false
1322
1320
  puts result.severity # => "error"
1323
1321
  puts result.messages # => array of messages
@@ -1337,10 +1335,6 @@ puts report.to_json
1337
1335
  .Using table validation helpers
1338
1336
  [example]
1339
1337
  ====
1340
- The validation framework integrates with 56 built-in validation helpers across
1341
- core OpenType tables. These helpers perform specific validation checks and
1342
- return boolean values.
1343
-
1344
1338
  [source,ruby]
1345
1339
  ----
1346
1340
  class ComprehensiveValidator < Fontisan::Validators::Validator
@@ -1348,7 +1342,7 @@ class ComprehensiveValidator < Fontisan::Validators::Validator
1348
1342
 
1349
1343
  def define_checks
1350
1344
  # Name table validation helpers
1351
- check_table :name_validation, 'name' do |table|
1345
+ check_table :name_validation, 'name', severity: :error do |table|
1352
1346
  table.valid_version? &&
1353
1347
  table.valid_encoding_heuristics? &&
1354
1348
  table.family_name_present? &&
@@ -1356,7 +1350,7 @@ class ComprehensiveValidator < Fontisan::Validators::Validator
1356
1350
  end
1357
1351
 
1358
1352
  # Head table validation helpers
1359
- check_table :head_validation, 'head' do |table|
1353
+ check_table :head_validation, 'head', severity: :error do |table|
1360
1354
  table.valid_magic? &&
1361
1355
  table.valid_version? &&
1362
1356
  table.valid_units_per_em? &&
@@ -1366,7 +1360,7 @@ class ComprehensiveValidator < Fontisan::Validators::Validator
1366
1360
  end
1367
1361
 
1368
1362
  # Maxp table validation helpers
1369
- check_table :maxp_validation, 'maxp' do |table|
1363
+ check_table :maxp_validation, 'maxp', severity: :error do |table|
1370
1364
  table.valid_version? &&
1371
1365
  table.valid_num_glyphs? &&
1372
1366
  table.valid_max_zones? &&
@@ -1405,7 +1399,7 @@ else
1405
1399
  end
1406
1400
 
1407
1401
  # Check specific validation results
1408
- if report.result_of(:name_version)&.passed?
1402
+ if report.result_of(:name_validation)&.passed?
1409
1403
  puts "Name table version is valid"
1410
1404
  else
1411
1405
  puts "Name table version check failed"
@@ -1524,16 +1518,51 @@ fontisan version
1524
1518
  ----
1525
1519
 
1526
1520
 
1527
- == Font collections
1521
+ == Font containers and collections
1528
1522
 
1529
1523
  === General
1530
1524
 
1531
- Fontisan provides comprehensive tools for managing TrueType Collections (TTC)
1532
- and OpenType Collections (OTC). You can list fonts in a collection, extract
1533
- individual fonts, unpack entire collections, and validate collection integrity.
1525
+ Fontisan provides comprehensive tools for managing font containers and
1526
+ collections. A font container is defined as a packaging format that can hold one
1527
+ or more font assets.
1528
+
1529
+ In general, Fontisan supports the following levels of font packaging:
1530
+
1531
+ Level 1:: Outline formats. This level defines the actual glyph outline data
1532
+ format for fonts.
1533
+
1534
+ TrueType::: Quadratic Bézier curves (glyf/loca tables)
1535
+ CFF::: Cubic Bézier curves (CFF table, PostScript outlines)
1536
+ CFF2::: Variable CFF with cubic curves (CFF2 table)
1537
+
1538
+ Level 2:: Curve and metadata container. This level defines the overall font
1539
+ file format that encapsulates outline data along with various metadata tables.
1540
+
1541
+ SFNT::: "Spine/Scalable font" defines the overall structure and directory system
1542
+ for a font file, which houses different "tables" containing specific data like
1543
+ glyph outlines, character maps, and kerning information.
1544
+
1545
+ TrueType (TTF)::: A specific implementation of the SFNT container that uses
1546
+ TrueType outlines.
1547
+
1548
+ OpenType (OTF)::: A more versatile SFNT container that can house either TrueType
1549
+ or CFF outlines, along with additional typographic features.
1550
+
1551
+ Web Open Font Format (WOFF and WOFF2)::: Compressed SFNT-based formats optimized
1552
+ for web delivery.
1553
+
1554
+ Level 3:: Individual font file, representing a single font asset packaged in a
1555
+ specific container format.
1556
+
1557
+ TTF `.ttf`::: A single font file in TrueType format
1558
+ OTF `.otf`::: A single font file in OpenType format
1559
+ WOFF `.woff`::: A single font file in Web Open Font Format 1.0 that is SFNT-based
1560
+ and uses zlib compression
1561
+ WOFF2 `.woff2`::: A single font file in Web Open Font Format 2.0 that is
1562
+ SFNT-based and uses Brotli compression
1534
1563
 
1535
- Both TTC and OTC files use the same `ttcf` tag in their binary format, but
1536
- differ in the type of font data they contain:
1564
+ Level 4:: Font collections, which are container formats that can hold multiple
1565
+ font assets within a single file.
1537
1566
 
1538
1567
  TTC (TrueType Collection):: Supported since OpenType 1.4. Contains fonts with
1539
1568
  TrueType outlines (glyf table). Multiple fonts can share identical tables for
@@ -1543,7 +1572,46 @@ OTC (OpenType Collection):: Supported since OpenType 1.8. Contains fonts with
1543
1572
  CFF-format outlines (CFF table). Provides the same storage benefits and
1544
1573
  glyph-count advantages as TTC but for CFF fonts. File extension: `.otc`
1545
1574
 
1546
- The collection format allows:
1575
+ dfont (Apple suitcase)::: Apple proprietary format storing complete Mac font
1576
+ suitcase resources in the data fork. Supports any SFNT fonts (TTF, OTF, or
1577
+ mixed). Mac OS X specific. File extension: `.dfont`
1578
+
1579
+ Fontist returns the appropriate collection type based on the font data:
1580
+
1581
+ * Examines font data within collection to determine type (TTC vs OTC)
1582
+ * TTC contains fonts with TrueType outlines (glyf table)
1583
+ * OTC contains fonts with CFF outlines (CFF table)
1584
+ * If ANY font in the collection has CFF outlines, use OpenTypeCollection
1585
+ * Only use TrueTypeCollection if ALL fonts have TrueType outlines
1586
+
1587
+
1588
+ === Collection compatibility
1589
+
1590
+ .Compatibility matrix
1591
+ |===
1592
+ | Container | TrueType | CFF | Mixed | WOFF/WOFF2 | Platform
1593
+
1594
+ | TTC | ✅ ONLY | ❌ | ❌ | ❌ | Cross-platform
1595
+ | OTC | ✅ | ✅ | ✅ | ❌ | Cross-platform
1596
+ | dfont | ✅ | ✅ | ✅ | ❌ | Apple only
1597
+
1598
+ |===
1599
+
1600
+ NOTE: WOFF/WOFF2 cannot be packaged into TTC, OTC or dfont collections. Web
1601
+ fonts are designed for single-font delivery only.
1602
+
1603
+
1604
+ === Working with collections
1605
+
1606
+ Fontist supports working with TrueType Collections (TTC), OpenType Collections
1607
+ (OTC) and the legacy Apple dfont format. You can list fonts in a collection,
1608
+ extract individual fonts, unpack entire collections, and validate collection
1609
+ integrity.
1610
+
1611
+ NOTE: Both TTC and OTC files use the same `ttcf` tag in their binary format, but
1612
+ differ in the type of font data they contain.
1613
+
1614
+ In particular, the TTC and OTC formats allows:
1547
1615
 
1548
1616
  Table sharing::
1549
1617
  Identical tables are stored once and referenced by multiple fonts
@@ -1568,8 +1636,7 @@ Fontist returns the appropriate collection type based on the font data:
1568
1636
 
1569
1637
  ==== General
1570
1638
 
1571
- List all fonts in a TrueType Collection (TTC) or OpenType Collection (OTC), with
1572
- their index, family name, and style.
1639
+ List all fonts in a font collection, with their index, family name, and style.
1573
1640
 
1574
1641
  ==== Command-line usage
1575
1642
 
@@ -1580,7 +1647,6 @@ $ fontisan ls FONT.{ttc,otc}
1580
1647
 
1581
1648
  NOTE: In `extract_ttc`, this was done with `extract_ttc --list FONT.ttc`.
1582
1649
 
1583
-
1584
1650
  .List collection contents
1585
1651
  [example]
1586
1652
  ====
@@ -1611,6 +1677,27 @@ Font 3: Noto Serif CJK TC
1611
1677
  ----
1612
1678
  ====
1613
1679
 
1680
+ .List fonts in dfont suitcase
1681
+ [example]
1682
+ ====
1683
+ [source,shell]
1684
+ ----
1685
+ $ fontisan ls family.dfont
1686
+
1687
+ Font 0: Arial
1688
+ Family: Arial
1689
+ Subfamily: Regular
1690
+
1691
+ Font 1: Arial
1692
+ Family: Arial
1693
+ Subfamily: Bold
1694
+
1695
+ Font 2: Arial
1696
+ Family: Arial
1697
+ Subfamily: Italic
1698
+ ----
1699
+ ====
1700
+
1614
1701
 
1615
1702
  === Show collection info
1616
1703
 
@@ -1771,863 +1858,111 @@ Collection created successfully:
1771
1858
  $ fontisan pack Regular.otf Bold.otf Italic.otf --output family.otc --format otc
1772
1859
  ----
1773
1860
 
1774
-
1775
- === Validate collection
1776
-
1777
- ==== General
1778
-
1779
- Validate the structure and checksums of a TrueType Collection (TTC) or OpenType
1780
- Collection (OTC).
1781
-
1782
- ==== Command-line usage
1783
-
1784
- [source,shell]
1785
- ----
1786
- $ fontisan validate FONT.{ttc,otc}
1787
- ----
1788
-
1789
- NOTE: In `extract_ttc`, this was done with `extract_ttc --validate FONT.ttc`.
1790
-
1791
-
1792
- == Advanced features
1793
-
1794
- Fontisan provides capabilities:
1795
-
1796
- .Font analysis and inspection
1797
- * Extract OpenType tables with checksums and offsets
1798
- * Display Unicode mappings and glyph names
1799
- * Analyze variable font axes and instances
1800
- * Show supported scripts and OpenType features
1801
- * Dump raw binary table data
1802
-
1803
- .Format conversion and subsetting
1804
- * Convert between TTF, OTF, WOFF, and WOFF2 formats
1805
- * Create font subsets with specific glyph ranges
1806
- * Validate font structure and integrity
1807
- * Generate SVG representations of glyphs
1808
-
1809
- .Collection creation
1810
- * Build new TTC files from individual fonts
1811
- * Optimize collection with table deduplication
1812
- * Pack fonts with shared tables for smaller file sizes
1813
-
1814
- For complete migration guide, see link:docs/EXTRACT_TTC_MIGRATION.md[extract_ttc Migration Guide].
1815
-
1816
-
1817
-
1818
-
1819
- == Loading modes
1820
-
1821
- === General
1822
-
1823
- Fontisan provides a flexible loading modes architecture that enables efficient
1824
- font parsing for different use cases.
1825
-
1826
- The system supports two distinct modes:
1827
-
1828
- `:full` mode:: (default) Loads all tables in the font for complete analysis and
1829
- manipulation
1830
-
1831
- `:metadata` mode:: Loads only metadata tables needed for font identification and
1832
- metrics (similar to `otfinfo` functionality). This mode is around 5x faster
1833
- than full parsing and uses significantly less memory.
1834
-
1835
- This architecture is particularly useful for software that only
1836
- needs basic font information without full parsing overhead, such as
1837
- font indexing systems or font discovery tools.
1838
-
1839
- This mode was developed to improve performance in font indexing in the
1840
- https://github.com/fontist/fontist[Fontist] library, where system fonts
1841
- need to be scanned quickly without loading unnecessary data.
1842
-
1843
- A font file opened in `:metadata` mode will only have a subset of tables
1844
- loaded, and attempts to access non-loaded tables will return `nil`.
1845
-
1846
- [source,ruby]
1847
- ----
1848
- font = Fontisan::FontLoader.load('font.ttf', mode: :metadata)
1849
-
1850
- # Check table availability before accessing
1851
- font.table_available?("name") # => true
1852
- font.table_available?("GSUB") # => false
1853
-
1854
- # Access allowed tables
1855
- font.table("name") # => Works
1856
- font.table("head") # => Works
1857
-
1858
- # Restricted tables return nil
1859
- font.table("GSUB") # => nil (not loaded in metadata mode)
1860
- ----
1861
-
1862
- You can also set loading modes via the environment:
1863
-
1864
- [source,ruby]
1865
- ----
1866
- # Set defaults via environment
1867
- ENV['FONTISAN_MODE'] = 'metadata'
1868
- ENV['FONTISAN_LAZY'] = 'false'
1869
-
1870
- # Uses environment settings
1871
- font = Fontisan::FontLoader.load('font.ttf')
1872
-
1873
- # Explicit parameters override environment
1874
- font = Fontisan::FontLoader.load('font.ttf', mode: :full)
1875
- ----
1876
-
1877
- The loading mode can be queried at any time.
1878
-
1879
- [source,ruby]
1880
- ----
1881
- # Mode stored as font property
1882
- font.loading_mode # => :metadata or :full
1883
-
1884
- # Table availability checked before access
1885
- font.table_available?(tag) # => boolean
1886
-
1887
- # Access restricted based on mode
1888
- font.table(tag) # => Returns table or raises error
1889
- ----
1890
-
1891
-
1892
-
1893
- === Metadata mode
1894
-
1895
- Loads only 6 tables (name, head, hhea, maxp, OS/2, post) instead of 15-20 tables.
1896
-
1897
- .Metadata mode: Fast loading for font identification
1898
- [source,ruby]
1899
- ----
1900
- font = Fontisan::FontLoader.load('font.ttf', mode: :metadata)
1901
- puts font.family_name # => "Arial"
1902
- puts font.subfamily_name # => "Regular"
1903
- puts font.post_script_name # => "ArialMT"
1904
- ----
1905
-
1906
- Tables loaded:
1907
-
1908
- name:: Font names and metadata
1909
- head:: Font header with global metrics
1910
- hhea:: Horizontal header with line spacing
1911
- maxp:: Maximum profile with glyph count
1912
- OS/2:: OS/2 and Windows metrics
1913
- post:: PostScript information
1914
-
1915
-
1916
- In metadata mode, these convenience methods provide direct access to name table
1917
- fields:
1918
-
1919
- `family_name`:: Font family name (nameID 1)
1920
- `subfamily_name`:: Font subfamily/style name (nameID 2)
1921
- `full_name`:: Full font name (nameID 4)
1922
- `post_script_name`:: PostScript name (nameID 6)
1923
- `preferred_family_name`:: Preferred family name (nameID 16, may be nil)
1924
- `preferred_subfamily_name`:: Preferred subfamily name (nameID 17, may be nil)
1925
- `units_per_em`:: Units per em from head table
1926
-
1927
-
1928
- === Full mode
1929
-
1930
- Loads all tables in the font for complete analysis and manipulation.
1931
-
1932
- .Full mode: Complete font analysis
1933
- [source,ruby]
1934
- ----
1935
- font = Fontisan::FontLoader.load('font.ttf', mode: :full)
1936
- font.table("GSUB") # => Available
1937
- font.table("GPOS") # => Available
1938
-
1939
- # Check which mode is active
1940
- puts font.loading_mode # => :metadata or :full
1941
- ----
1942
-
1943
- Tables loaded:
1944
-
1945
- * All tables in the font
1946
- * Including GSUB, GPOS, cmap, glyf/CFF, etc.
1947
-
1948
- === Lazy loading option
1949
-
1950
- Fontisan supports lazy loading of tables in both `:metadata` and `:full` modes.
1951
- When lazy loading is enabled (optional), tables are only parsed when accessed.
1952
-
1953
- Options:
1954
-
1955
- `false`:: (default) Eager loading. All tables for the selected mode are parsed
1956
- upfront.
1957
-
1958
- `true`:: Lazy loading enabled. Tables are parsed on-demand.
1959
-
1960
- [source,ruby]
1961
- ----
1962
- # Metadata mode with lazy loading (default, fastest)
1963
- font = Fontisan::FontLoader.load('font.ttf', mode: :metadata, lazy: true)
1964
-
1965
- # Metadata mode with eager loading (loads all metadata tables upfront)
1966
- font = Fontisan::FontLoader.load('font.ttf', mode: :metadata, lazy: false)
1967
-
1968
- # Full mode with lazy loading (tables loaded on-demand)
1969
- font = Fontisan::FontLoader.load('font.ttf', mode: :full, lazy: true)
1970
-
1971
- # Full mode with eager loading (all tables loaded upfront)
1972
- font = Fontisan::FontLoader.load('font.ttf', mode: :full, lazy: false)
1973
- ----
1974
-
1975
-
1976
-
1977
-
1978
- == Outline format conversion
1979
-
1980
- === General
1981
-
1982
- Fontisan supports bidirectional conversion between TrueType (TTF) and
1983
- OpenType/CFF (OTF) outline formats through the Fontist universal outline model
1984
- (UOM).
1985
-
1986
- The outline converter enables transformation between glyph outline formats:
1987
-
1988
- TrueType (TTF):: Uses quadratic Bézier curves stored in glyf/loca tables
1989
- OpenType/CFF (OTF):: Uses cubic Bézier curves stored in CFF table
1990
-
1991
- Conversion uses a format-agnostic universal outline model as an intermediate
1992
- representation, ensuring high-quality results while preserving glyph metrics and
1993
- bounding boxes.
1994
-
1995
-
1996
- === Convert between TTF and OTF
1997
-
1998
- ==== Command-line usage
1999
-
2000
- Syntax:
2001
-
2002
- [source,bash]
2003
- ----
2004
- $ fontisan convert INPUT_FONT --to FORMAT --output OUTPUT_FONT
2005
- ----
2006
-
2007
- Where,
2008
-
2009
- `INPUT_FONT`:: Path to the input font file (TTF or OTF)
2010
- `FORMAT`:: Target format:
2011
- `ttf`, `truetype`::: TrueType format
2012
- `otf`, `opentype`, `cff`::: OpenType/CFF format
2013
- `OUTPUT_FONT`:: Path to the output font file
2014
-
2015
-
2016
- [source,bash]
2017
- ----
2018
- # Convert TrueType font to OpenType/CFF
2019
- fontisan convert input.ttf --to otf --output output.otf
2020
-
2021
- # Convert OpenType/CFF font to TrueType
2022
- fontisan convert input.otf --to ttf --output output.ttf
2023
- ----
2024
-
2025
-
2026
- ==== Ruby API usage
2027
-
2028
- Basic conversion with OutlineConverter:
2029
-
2030
- [source,ruby]
2031
- ----
2032
- require 'fontisan'
2033
-
2034
- # Load a TrueType font
2035
- font = Fontisan::FontLoader.load('input.ttf')
2036
-
2037
- # Convert to OpenType/CFF
2038
- converter = Fontisan::Converters::OutlineConverter.new
2039
- tables = converter.convert(font, target_format: :otf)
2040
-
2041
- # Write output
2042
- Fontisan::FontWriter.write_to_file(
2043
- tables,
2044
- 'output.otf',
2045
- sfnt_version: 0x4F54544F # 'OTTO' for OpenType/CFF
2046
- )
2047
- ----
2048
-
2049
- Using FormatConverter:
2050
-
2051
- [source,ruby]
2052
- ----
2053
- require 'fontisan'
2054
-
2055
- # Load font
2056
- font = Fontisan::FontLoader.load('input.ttf')
2057
-
2058
- # Convert using high-level API
2059
- converter = Fontisan::Converters::FormatConverter.new
2060
- if converter.supported?(:ttf, :otf)
2061
- tables = converter.convert(font, :otf)
2062
-
2063
- # Write output
2064
- Fontisan::FontWriter.write_to_file(
2065
- tables,
2066
- 'output.otf',
2067
- sfnt_version: 0x4F54544F
2068
- )
2069
- end
2070
- ----
2071
-
2072
- To check supported conversions:
2073
-
2074
- [source,ruby]
2075
- ----
2076
- converter = Fontisan::Converters::FormatConverter.new
2077
-
2078
- # Check if conversion is supported
2079
- converter.supported?(:ttf, :otf) # => true
2080
- converter.supported?(:otf, :ttf) # => true
2081
-
2082
- # Get all supported conversions
2083
- converter.all_conversions
2084
- # => [{from: :ttf, to: :otf}, {from: :otf, to: :ttf}, ...]
2085
-
2086
- # Get supported targets for a source format
2087
- converter.supported_targets(:ttf)
2088
- # => [:ttf, :otf, :woff2, :svg]
2089
- ----
2090
-
2091
- === Convert into WOFF2
2092
-
2093
- ==== Validation
2094
-
2095
- WOFF2 encoding can be validated automatically to ensure spec compliance:
2096
-
2097
- .Validation example
1861
+ .Pack into OTC
2098
1862
  [example]
2099
1863
  ====
2100
- [source,ruby]
2101
- ----
2102
- encoder = Fontisan::Converters::Woff2Encoder.new
2103
- result = encoder.convert(font, {
2104
- transform_tables: true,
2105
- validate: true,
2106
- validation_level: :strict # :strict, :standard, or :lenient
2107
- })
2108
-
2109
- report = result[:validation_report]
2110
- puts "Valid: #{report.valid}"
2111
- puts "Compression: #{report.info_issues.first.message}"
2112
- ----
2113
- ====
2114
-
2115
-
2116
-
2117
- === Validation
2118
-
2119
- Font integrity validation is enabled by default for all conversions.
2120
-
2121
- The validator ensures proper OpenType checksum calculation including correct
2122
- handling of the head table's checksumAdjustment field per the OpenType
2123
- specification.
2124
-
2125
- After conversion, validate the output font:
2126
-
2127
- [source,bash]
2128
- ----
2129
- fontisan validate output.otf
2130
- fontisan info output.otf
2131
- fontisan tables output.otf
2132
- ----
2133
-
2134
-
2135
- == Technical details of outline conversion
2136
-
2137
- === General
2138
-
2139
- The converter uses a three-stage pipeline:
2140
-
2141
- [source]
2142
- ----
2143
- Source Format Universal Outline Target Format
2144
- ------------- ------------------ -------------
2145
- TrueType (glyf) →→→ Command-based model →→→ OpenType/CFF
2146
- Quadratic curves Path representation Cubic curves
2147
- On/off-curve pts (format-agnostic) CharStrings
2148
- Delta encoding Bounding boxes Type 2 operators
2149
- Metrics Compact encoding
2150
- ----
2151
-
2152
- === Conversion steps
2153
-
2154
- TTF → OTF conversion:
2155
-
2156
- . Extract glyphs from glyf/loca tables
2157
- . Convert quadratic Bézier curves to universal outline format
2158
- . Build CFF table with CharStrings INDEX
2159
- . Update maxp table to version 0.5 (CFF format)
2160
- . Update head table (clear indexToLocFormat)
2161
- . Remove glyf/loca tables
2162
- . Preserve all other tables
2163
-
2164
- OTF → TTF conversion:
2165
-
2166
- . Extract CharStrings from CFF table
2167
- . Convert cubic Bézier curves to universal outline format
2168
- . Convert cubic curves to quadratic using adaptive subdivision
2169
- . Build glyf and loca tables with optimal format selection
2170
- . Update maxp table to version 1.0 (TrueType format)
2171
- . Update head table (set indexToLocFormat)
2172
- . Remove CFF table
2173
- . Preserve all other tables
2174
-
2175
- === Curve conversion
2176
-
2177
- **Quadratic to cubic** (lossless):
2178
-
2179
- [source]
2180
- ----
2181
- Given quadratic curve with control point Q:
2182
- P0 (start), Q (control), P2 (end)
2183
-
2184
- Calculate cubic control points:
2185
- CP1 = P0 + (2/3) × (Q - P0)
2186
- CP2 = P2 + (2/3) × (Q - P2)
2187
-
2188
- Result: Exact mathematical equivalent
2189
- ----
2190
-
2191
- **Cubic to quadratic** (adaptive):
2192
-
2193
- [source]
2194
- ----
2195
- Given cubic curve with control points:
2196
- P0 (start), CP1, CP2, P3 (end)
2197
-
2198
- Use adaptive subdivision algorithm:
2199
- 1. Estimate error of quadratic approximation
2200
- 2. If error > threshold (0.5 units):
2201
- - Subdivide cubic curve at midpoint
2202
- - Recursively convert each half
2203
- 3. Otherwise: Output quadratic approximation
2204
-
2205
- Result: High-quality approximation with < 0.5 unit deviation
2206
- ----
2207
-
2208
- === Compound glyph support
2209
-
2210
- ==== General
2211
-
2212
- Fontisan fully supports compound (composite) glyphs in both conversion directions:
2213
-
2214
- * **TTF → OTF**: Compound glyphs are decomposed into simple outlines with transformations applied
2215
- * **OTF → TTF**: CFF glyphs are converted to simple TrueType glyphs
2216
-
2217
- ==== Decomposition process
2218
-
2219
- When converting TTF to OTF, compound glyphs undergo the following process:
2220
-
2221
- . Detected from glyf table flags (numberOfContours = -1)
2222
- . Components recursively resolved (handling nested compound glyphs)
2223
- . Transformation matrices applied to each component (translation, scale, rotation)
2224
- . All components merged into a single simple outline
2225
- . Converted to CFF CharString format
2226
-
2227
- This ensures that all glyphs render identically while maintaining proper metrics
2228
- and bounding boxes.
2229
-
2230
- ==== Technical implementation
2231
-
2232
- Compound glyphs reference other glyphs by index and apply 2×3 affine
2233
- transformation matrices:
2234
-
2235
- [source]
2236
- ----
2237
- x' = a*x + c*y + e
2238
- y' = b*x + d*y + f
2239
-
2240
- Where:
2241
- - a, d: Scale factors for x and y axes
2242
- - b, c: Rotation/skew components
2243
- - e, f: Translation offsets (x, y position)
2244
- ----
2245
-
2246
- The resolver handles:
2247
-
2248
- * Simple glyphs referenced by compounds
2249
- * Nested compound glyphs (compounds referencing other compounds)
2250
- * Circular reference detection with maximum recursion depth (32 levels)
2251
- * Complex transformation matrices (uniform scale, x/y scale, full 2×2 matrix)
2252
-
2253
- === Subroutine optimization
2254
-
2255
- ==== General
2256
-
2257
- When converting TrueType (TTF) to OpenType/CFF (OTF), Fontisan can automatically
2258
- generate CFF subroutines to reduce file size.
2259
-
2260
- Subroutines extract repeated CharString patterns across glyphs and store them
2261
- once, significantly reducing CFF table size while maintaining identical glyph
2262
- rendering.
2263
-
2264
- Key features:
2265
-
2266
- * Pattern analysis: Analyzes byte sequences across all CharStrings to identify repeating patterns
2267
- * Frequency-based selection: Prioritizes patterns that provide maximum space savings
2268
- * Configurable thresholds: Customizable minimum pattern length and maximum subroutine count
2269
- * Ordering optimization: Automatically orders subroutines by frequency for better compression
2270
-
2271
- Typical space savings: 30-50% reduction in CFF table size for fonts with similar
2272
- glyph shapes.
2273
-
2274
- NOTE: Current implementation calculates accurate optimization metrics but does
2275
- not modify the output CFF table. Full CFF serialization with subroutines will be
2276
- available in the next development phase.
2277
-
2278
- ==== Edge cases
2279
-
2280
- The optimizer correctly handles:
2281
-
2282
- * Multi-byte numbers: Number encodings from 1-5 bytes (CFF Type 2 format)
2283
- * Two-byte operators: Operators with 0x0c prefix (e.g., `div` in `lib/fontisan/tables/cff/charstring.rb`, `flex` in `lib/fontisan/tables/cff/charstring.rb`)
2284
- * Overlapping patterns: Multiple patterns at same byte positions
2285
- * Stack-neutral validation: Patterns verified to maintain consistent stack state
2286
-
2287
- ==== Technical details
2288
-
2289
- The subroutine optimizer uses a four-stage pipeline:
2290
-
2291
- [source]
2292
- ----
2293
- CharStrings → Pattern Analysis → Selection → Ordering → Metadata
2294
- (Input) (Find repeats) (Optimize) (Frequency) (Output)
2295
- ----
2296
-
2297
- **Pattern analysis**:
2298
-
2299
- . Extracts byte sequences from all CharStrings
2300
- . Identifies repeating patterns across glyphs
2301
- . Filters by minimum pattern length (default: 10 bytes)
2302
- . Builds pattern frequency map
2303
-
2304
- **Selection algorithm**:
2305
-
2306
- . Calculates savings for each pattern: `frequency × (length - overhead)`
2307
- . Ranks patterns by total savings (descending)
2308
- . Selects top patterns up to `max_subroutines` limit
2309
- . Ensures selected patterns don't exceed CFF limits
2310
-
2311
- **Ordering optimization**:
2312
-
2313
- . Sorts subroutines by usage frequency (most used first)
2314
- . Optimizes CFF bias calculation for better compression
2315
- . Ensures subroutine indices fit within CFF constraints
2316
-
2317
- **CFF bias calculation**:
2318
-
2319
- [source]
2320
- ----
2321
- Subroutine count CFF Bias
2322
- ----------------- ---------
2323
- 0-1239 107
2324
- 1240-33899 1131
2325
- 33900-65535 32768
2326
- ----
2327
-
2328
- The bias value determines how subroutine indices are encoded in CharStrings,
2329
- affecting the final size.
2330
-
2331
-
2332
- ==== Troubleshooting
2333
-
2334
- If you encounter CharString parsing errors after optimization:
2335
-
2336
- . Verify bias calculation: Ensure bias matches CFF specification (107, 1131, or 32768)
2337
- . Check operator boundaries: Patterns should only be extracted at valid boundaries
2338
- . Ensure no overlaps: Multiple patterns should not occupy same byte positions
2339
- . Enable verbose mode: Use `--verbose` flag for detailed diagnostics
2340
-
2341
- .Subroutine debugging workflow example
2342
- ====
2343
- [source,bash]
2344
- ----
2345
- # Convert with verbose output
2346
- $ fontisan convert input.ttf --to otf --output output.otf --optimize --verbose
2347
-
2348
- # Validate the output
2349
- $ fontisan validate output.otf
2350
-
2351
- # Check CharString structure
2352
- $ fontisan info output.otf
2353
- ----
2354
-
2355
- If validation fails, try:
2356
-
2357
- [source,bash]
2358
- ----
2359
- # Disable optimization
2360
- $ fontisan convert input.ttf --to otf --output output.otf
2361
-
2362
- # Use stack-aware mode for safer optimization
2363
- $ fontisan convert input.ttf --to otf --output output.otf --optimize --stack-aware
1864
+ [source,shell]
2364
1865
  ----
2365
- ====
1866
+ $ fontisan pack Regular.otf Bold.otf Italic.otf --output family.otc --format otc
2366
1867
 
2367
- .Optimization parameters
2368
- [example]
2369
- ====
2370
- [source,bash]
1868
+ Collection created successfully:
1869
+ Output: family.otc
1870
+ Format: OTC
1871
+ Fonts: 3
2371
1872
  ----
2372
- # Adjust pattern matching sensitivity
2373
- $ fontisan convert input.ttf --to otf --output output.otf \
2374
- --optimize \
2375
- --min-pattern-length 15 \
2376
- --max-subroutines 10000 \
2377
- --verbose
2378
1873
 
2379
- # Disable ordering optimization
2380
- $ fontisan convert input.ttf --to otf --output output.otf \
2381
- --optimize \
2382
- --no-optimize-ordering
2383
- ----
1874
+ NOTE: dfont supports mixed TrueType and OpenType fonts in the same suitcase.
1875
+ dfont does not perform table deduplication like TTC/OTC.
2384
1876
  ====
2385
1877
 
2386
- Where,
2387
1878
 
2388
- `--optimize`:: Enable subroutine optimization (default: false)
2389
- `--min-pattern-length N`:: Minimum pattern length in bytes (default: 10)
2390
- `--max-subroutines N`:: Maximum number of subroutines to generate (default: 65,535)
2391
- `--optimize-ordering`:: Optimize subroutine ordering by frequency (default: true)
2392
- `--verbose`:: Show detailed optimization statistics
2393
-
2394
-
2395
- === Stack-aware optimization
1879
+ === Convert between font container formats
2396
1880
 
2397
1881
  ==== General
2398
1882
 
2399
- Stack-aware optimization is an advanced mode that ensures all extracted patterns
2400
- are stack-neutral, guaranteeing 100% safety and reliability.
1883
+ Fontisan supports converting font collections between TrueType Collection (TTC),
1884
+ OpenType Collection (OTC), and Apple dfont formats.
2401
1885
 
2402
- Unlike normal byte-level pattern matching, stack-aware mode simulates CharString
2403
- execution to track operand stack depth, only extracting patterns that maintain
2404
- consistent stack state.
1886
+ All collection formats (TTC, OTC, dfont) support mixed TrueType and OpenType fonts.
2405
1887
 
2406
- Key benefits:
1888
+ By default, original font formats are preserved during conversion. Use the
1889
+ `--target-format` option to standardize all fonts to a specific outline format:
2407
1890
 
2408
- * **100% Reliability**: All patterns are validated to be stack-neutral
2409
- * **No Stack Errors**: Eliminates stack underflow/overflow issues
2410
- * **Faster Processing**: 6-12x faster than normal optimization due to early filtering
2411
- * **Smaller Pattern Set**: Significantly fewer candidates reduce memory usage
1891
+ * `--target-format preserve` (default) - Keep original mixed TTF+OTF formats
1892
+ * `--target-format ttf` - Convert all fonts to TrueType outlines
1893
+ * `--target-format otf` - Convert all fonts to OpenType/CFF outlines
2412
1894
 
2413
- Trade-offs:
2414
1895
 
2415
- * **Lower Compression**: ~6% reduction vs ~11% with normal mode
2416
- * **Fewer Patterns**: Filters out 90%+ of raw patterns for safety
2417
- * **Stack Validation Overhead**: Adds stack tracking during analysis
1896
+ NOTE: dfont supports mixed TrueType and OpenType fonts in the same suitcase.
2418
1897
 
2419
- ==== Command-line usage
2420
-
2421
- .Enable stack-aware optimization
1898
+ .TTC to OTC conversion (preserves formats)
2422
1899
  [example]
2423
1900
  ====
2424
- [source,bash]
1901
+ [source,shell]
2425
1902
  ----
2426
- # Convert with stack-aware optimization
2427
- $ fontisan convert input.ttf --to otf --output output.otf \
2428
- --optimize \
2429
- --stack-aware \
2430
- --verbose
2431
-
2432
- Converting input.ttf to otf...
2433
-
2434
- Analyzing CharString patterns (4515 glyphs)...
2435
- Found 8566 potential patterns
2436
- Selecting optimal patterns...
2437
- Selected 832 patterns for subroutinization
2438
- Building subroutines...
2439
- Generated 832 subroutines
2440
- Rewriting CharStrings with subroutine calls...
2441
- Rewrote 4515 CharStrings
2442
-
2443
- Subroutine Optimization Results:
2444
- Patterns found: 8566
2445
- Patterns selected: 832
2446
- Subroutines generated: 832
2447
- Estimated bytes saved: 46,280
2448
- CFF bias: 0
1903
+ $ fontisan convert family.ttc --to otc --output family.otc
2449
1904
 
2450
1905
  Conversion complete!
2451
- Input: input.ttf (806.3 KB)
2452
- Output: output.otf (660.7 KB)
1906
+ Input: family.ttc (245.8 KB)
1907
+ Output: family.otc (312.4 KB)
1908
+ Format: TTC → OTC
1909
+ Fonts: 3
2453
1910
  ----
2454
- ====
2455
1911
 
2456
- .Stack-aware vs normal mode
2457
- [example]
2458
- ====
2459
- [source,bash]
2460
- ----
2461
- # Use the comparison script
2462
- $ ruby scripts/compare_stack_aware.rb input.ttf
2463
-
2464
- File Size Reduction:
2465
- Normal: 81.49 KB (11.27%)
2466
- Stack-Aware: 43.17 KB (6.13%)
2467
-
2468
- Processing Times:
2469
- Normal: 18.38 s
2470
- Stack-Aware: 1.54 s (12x faster)
2471
-
2472
- Stack-Aware Efficiency: 52.97% of normal optimization
2473
- ----
1912
+ Original font formats are preserved (mixed TTF+OTF supported).
2474
1913
  ====
2475
1914
 
2476
- Where,
2477
-
2478
- `--stack-aware`:: Enable stack-aware pattern detection (default: false)
2479
-
2480
- ==== Using the Ruby API
2481
-
2482
- .Basic stack-aware optimization
1915
+ .TTC to OTC with forced conversion
2483
1916
  [example]
2484
1917
  ====
2485
- [source,ruby]
2486
- ----
2487
- require 'fontisan'
2488
-
2489
- # Load TrueType font
2490
- font = Fontisan::FontLoader.load('input.ttf')
2491
-
2492
- # Convert with stack-aware optimization
2493
- converter = Fontisan::Converters::OutlineConverter.new
2494
- tables = converter.convert(font, {
2495
- target_format: :otf,
2496
- optimize_subroutines: true,
2497
- stack_aware: true # Enable safe mode
2498
- })
2499
-
2500
- # Access optimization results
2501
- optimization = tables.instance_variable_get(:@subroutine_optimization)
2502
- puts "Patterns found: #{optimization[:pattern_count]}"
2503
- puts "Stack-neutral patterns: #{optimization[:selected_count]}"
2504
- puts "Processing time: #{optimization[:processing_time]}s"
2505
-
2506
- # Write output
2507
- Fontisan::FontWriter.write_to_file(
2508
- tables,
2509
- 'output.otf',
2510
- sfnt_version: 0x4F54544F
2511
- )
2512
- ----
2513
- ====
2514
-
2515
- ==== Technical details
2516
-
2517
- Stack-aware mode uses a three-stage validation process:
2518
-
2519
- [source]
2520
- ----
2521
- CharString Bytes → Stack Tracking → Pattern Validation → Safe Patterns
2522
- (Input) (Simulate) (Filter) (Output)
1918
+ [source,shell]
2523
1919
  ----
1920
+ $ fontisan convert family.ttc --to otc --output family.otc --target-format otf
2524
1921
 
2525
- **Stack tracking**:
2526
-
2527
- . Simulates CharString execution without full interpretation
2528
- . Records stack depth at each byte position
2529
- . Handles 40+ Type 2 CharString operators with correct stack effects
2530
-
2531
- **Pattern validation**:
2532
-
2533
- . Checks if pattern start and end have same stack depth
2534
- . Ensures no stack underflow during pattern execution
2535
- . Verifies consistent results regardless of initial stack state
2536
-
2537
- **Stack-neutral pattern** criteria. Pattern is stack-neutral if:
2538
-
2539
- . depth_at(pattern_start) == depth_at(pattern_end)
2540
- . No negative depth during pattern execution
2541
- . Pattern produces same result for any valid initial stack
2542
- +
2543
- .Example Stack-Neutral Pattern
2544
- [source]
2545
- ----
2546
- 10 20 rmoveto # Pushes 2 operands, consumes 2 → neutral
2547
- ----
2548
- +
2549
- .Example Non-Neutral Pattern
2550
- [source]
2551
- ----
2552
- 10 20 add # Pushes 2, consumes 2, produces 1 → NOT neutral
1922
+ Conversion complete!
1923
+ Input: family.ttc (245.8 KB)
1924
+ Output: family.otc (312.4 KB)
1925
+ Format: TTC OTC
1926
+ Fonts: 3
2553
1927
  ----
2554
1928
 
2555
- ==== When to use stack-aware mode
2556
-
2557
- **Recommended for**:
2558
-
2559
- * Production font conversion where reliability is critical
2560
- * Fonts that will undergo further processing
2561
- * Web fonts where correctness matters more than minimal size
2562
- * Situations where testing/validation is limited
2563
-
2564
- **Normal mode acceptable for**:
2565
-
2566
- * Development/testing environments
2567
- * When full validation will be performed post-conversion
2568
- * Maximum compression is priority over guaranteed safety
2569
-
2570
- ==== Using the Ruby API
1929
+ All TrueType fonts are converted to OpenType/CFF format.
1930
+ ====
2571
1931
 
2572
- .Basic optimization
1932
+ .dfont to OTC conversion (preserves formats)
2573
1933
  [example]
2574
1934
  ====
2575
- [source,ruby]
1935
+ [source,shell]
2576
1936
  ----
2577
- require 'fontisan'
2578
-
2579
- # Load TrueType font
2580
- font = Fontisan::FontLoader.load('input.ttf')
1937
+ $ fontisan convert family.dfont --to otc --output family.otc
2581
1938
 
2582
- # Convert with optimization
2583
- converter = Fontisan::Converters::OutlineConverter.new
2584
- tables = converter.convert(font, {
2585
- target_format: :otf,
2586
- optimize_subroutines: true
2587
- })
2588
-
2589
- # Access optimization results
2590
- optimization = tables.instance_variable_get(:@subroutine_optimization)
2591
- puts "Patterns found: #{optimization[:pattern_count]}"
2592
- puts "Selected: #{optimization[:selected_count]}"
2593
- puts "Savings: #{optimization[:savings]} bytes"
2594
-
2595
- # Write output
2596
- Fontisan::FontWriter.write_to_file(
2597
- tables,
2598
- 'output.otf',
2599
- sfnt_version: 0x4F54544F
2600
- )
1939
+ Conversion complete!
1940
+ Input: family.dfont (387.6 KB)
1941
+ Output: family.otc (312.4 KB)
1942
+ Format: DFONT → OTC
1943
+ Fonts: 3
2601
1944
  ----
1945
+
1946
+ Fonts are extracted from dfont and repacked as OTC.
1947
+ Original font formats are preserved (mixed TTF+OTF supported).
2602
1948
  ====
2603
1949
 
2604
- .Custom optimization parameters
1950
+ .dfont to OTC with forced conversion
2605
1951
  [example]
2606
1952
  ====
2607
- [source,ruby]
1953
+ [source,shell]
2608
1954
  ----
2609
- require 'fontisan'
1955
+ $ fontisan convert family.dfont --to otc --output family.otc --target-format otf
2610
1956
 
2611
- font = Fontisan::FontLoader.load('input.ttf')
2612
- converter = Fontisan::Converters::OutlineConverter.new
2613
-
2614
- # Fine-tune optimization
2615
- tables = converter.convert(font, {
2616
- target_format: :otf,
2617
- optimize_subroutines: true,
2618
- min_pattern_length: 15,
2619
- max_subroutines: 5000,
2620
- optimize_ordering: true,
2621
- verbose: true
2622
- })
2623
-
2624
- # Analyze results
2625
- optimization = tables.instance_variable_get(:@subroutine_optimization)
2626
- if optimization[:selected_count] > 0
2627
- efficiency = optimization[:savings].to_f / optimization[:selected_count]
2628
- puts "Average savings per subroutine: #{efficiency.round(2)} bytes"
2629
- end
1957
+ Conversion complete!
1958
+ Input: family.dfont (387.6 KB)
1959
+ Output: family.otc (312.4 KB)
1960
+ Format: DFONT → OTC
1961
+ Fonts: 3
2630
1962
  ----
1963
+
1964
+ Fonts are extracted from dfont and repacked as OTC.
1965
+ All TrueType fonts are converted to OpenType/CFF format.
2631
1966
  ====
2632
1967
 
2633
1968
 
@@ -2736,7 +2071,7 @@ Fontisan can:
2736
2071
 
2737
2072
  === Universal color layers
2738
2073
 
2739
- (Converted TTF, OTF files)
2074
+ (Fontisan, converted TTF, OTF files)
2740
2075
 
2741
2076
  Fontisan can:
2742
2077
 
@@ -2749,6 +2084,41 @@ Fontisan can:
2749
2084
  * Importing and generation PNG block ruler layers
2750
2085
 
2751
2086
 
2087
+ == Testing
2088
+
2089
+ === General
2090
+
2091
+ Fontisan has a comprehensive test suite covering all font operations, formats,
2092
+ and features.
2093
+
2094
+ === Running tests
2095
+
2096
+ [source,shell]
2097
+ ----
2098
+ # Run full test suite
2099
+ bundle exec rspec
2100
+
2101
+ # Run with documentation format
2102
+ bundle exec rspec --format documentation
2103
+
2104
+ # Run specific file
2105
+ bundle exec rspec spec/fontisan/tables/maxp_spec.rb
2106
+ ----
2107
+
2108
+ === Test fixtures
2109
+
2110
+ All required font fixtures are automatically downloaded before tests run. The
2111
+ test suite uses a centralized fixture configuration system that ensures all
2112
+ necessary fonts are available.
2113
+
2114
+ [source,shell]
2115
+ ----
2116
+ # Manual fixture management (if needed)
2117
+ bundle exec rake fixtures:download # Download all test fonts
2118
+ bundle exec rake fixtures:clean # Remove downloaded fonts
2119
+ ----
2120
+
2121
+
2752
2122
  == Copyright and license
2753
2123
 
2754
2124
  Copyright https://www.ribose.com[Ribose].