fontisan 0.2.17 → 0.2.22
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.
- checksums.yaml +4 -4
- data/.rubocop_todo.yml +14 -90
- data/README.adoc +257 -1
- data/docs/.vitepress/config.ts +68 -8
- data/docs/.vitepress/theme/style.css +570 -272
- data/docs/CONVERSION_GUIDE.adoc +31 -8
- data/docs/EXTRACT_TTC_MIGRATION.md +1 -1
- data/docs/WOFF_WOFF2_FORMATS.adoc +53 -0
- data/docs/api/conversion-options.md +37 -14
- data/docs/cli/audit.md +337 -0
- data/docs/cli/convert.md +20 -1
- data/docs/cli/index.md +31 -0
- data/docs/guide/color.md +1 -1
- data/docs/guide/conversion/options.md +32 -3
- data/docs/guide/conversion/ttf-otf.md +1 -1
- data/docs/guide/conversion/type1.md +1 -1
- data/docs/guide/conversion/web.md +91 -32
- data/docs/guide/conversion.md +6 -5
- data/docs/guide/formats/woff.md +35 -11
- data/docs/guide/index.md +2 -2
- data/docs/guide/migrations/extract-ttc.md +1 -1
- data/docs/guide/quick-start.md +4 -4
- data/docs/guide/type1.md +4 -4
- data/docs/guide/woff.md +19 -17
- data/docs/index.md +2 -0
- data/docs/lychee.toml +5 -1
- data/docs/package.json +1 -1
- data/docs/public/robots.txt +4 -0
- data/docs/scripts/post-build.mjs +81 -0
- data/lib/fontisan/audit/codepoint_range_coalescer.rb +41 -0
- data/lib/fontisan/audit/context.rb +122 -0
- data/lib/fontisan/audit/differ.rb +124 -0
- data/lib/fontisan/audit/extractors/aggregations.rb +54 -0
- data/lib/fontisan/audit/extractors/base.rb +26 -0
- data/lib/fontisan/audit/extractors/color_capabilities.rb +141 -0
- data/lib/fontisan/audit/extractors/coverage.rb +48 -0
- data/lib/fontisan/audit/extractors/hinting.rb +197 -0
- data/lib/fontisan/audit/extractors/identity.rb +52 -0
- data/lib/fontisan/audit/extractors/language_coverage.rb +37 -0
- data/lib/fontisan/audit/extractors/licensing.rb +79 -0
- data/lib/fontisan/audit/extractors/metrics.rb +103 -0
- data/lib/fontisan/audit/extractors/opentype_layout.rb +69 -0
- data/lib/fontisan/audit/extractors/provenance.rb +29 -0
- data/lib/fontisan/audit/extractors/style.rb +32 -0
- data/lib/fontisan/audit/extractors/variation_detail.rb +99 -0
- data/lib/fontisan/audit/extractors.rb +27 -0
- data/lib/fontisan/audit/library_aggregator.rb +83 -0
- data/lib/fontisan/audit/library_auditor.rb +90 -0
- data/lib/fontisan/audit/registry.rb +60 -0
- data/lib/fontisan/audit/style_extractor.rb +80 -0
- data/lib/fontisan/audit.rb +20 -0
- data/lib/fontisan/base_collection.rb +23 -9
- data/lib/fontisan/binary/structures.rb +0 -2
- data/lib/fontisan/binary.rb +11 -0
- data/lib/fontisan/cldr/aggregator.rb +33 -0
- data/lib/fontisan/cldr/cache_manager.rb +110 -0
- data/lib/fontisan/cldr/config.rb +59 -0
- data/lib/fontisan/cldr/download_error.rb +9 -0
- data/lib/fontisan/cldr/downloader.rb +79 -0
- data/lib/fontisan/cldr/error.rb +8 -0
- data/lib/fontisan/cldr/index.rb +64 -0
- data/lib/fontisan/cldr/index_builder.rb +72 -0
- data/lib/fontisan/cldr/unicode_set_parser.rb +172 -0
- data/lib/fontisan/cldr/unknown_version_error.rb +9 -0
- data/lib/fontisan/cldr/version_resolver.rb +91 -0
- data/lib/fontisan/cldr.rb +23 -0
- data/lib/fontisan/cli/cldr_cli.rb +85 -0
- data/lib/fontisan/cli/ucd_cli.rb +97 -0
- data/lib/fontisan/cli.rb +201 -2
- data/lib/fontisan/collection/builder.rb +0 -4
- data/lib/fontisan/collection/dfont_builder.rb +0 -4
- data/lib/fontisan/collection/shared_logic.rb +0 -2
- data/lib/fontisan/collection/writer.rb +0 -3
- data/lib/fontisan/collection.rb +15 -0
- data/lib/fontisan/commands/audit_command.rb +123 -0
- data/lib/fontisan/commands/audit_compare_command.rb +66 -0
- data/lib/fontisan/commands/audit_library_command.rb +46 -0
- data/lib/fontisan/commands/base_command.rb +0 -3
- data/lib/fontisan/commands/convert_command.rb +25 -20
- data/lib/fontisan/commands/dump_table_command.rb +0 -3
- data/lib/fontisan/commands/export_command.rb +0 -4
- data/lib/fontisan/commands/features_command.rb +0 -3
- data/lib/fontisan/commands/instance_command.rb +0 -5
- data/lib/fontisan/commands/ls_command.rb +0 -6
- data/lib/fontisan/commands/optical_size_command.rb +0 -3
- data/lib/fontisan/commands/pack_command.rb +0 -5
- data/lib/fontisan/commands/scripts_command.rb +0 -2
- data/lib/fontisan/commands/subset_command.rb +0 -3
- data/lib/fontisan/commands/unicode_command.rb +0 -3
- data/lib/fontisan/commands/unpack_command.rb +0 -7
- data/lib/fontisan/commands/validate_command.rb +0 -8
- data/lib/fontisan/commands/variable_command.rb +0 -3
- data/lib/fontisan/commands.rb +29 -0
- data/lib/fontisan/config/cldr.yml +22 -0
- data/lib/fontisan/config/conversion_matrix.yml +38 -0
- data/lib/fontisan/config/ucd.yml +23 -0
- data/lib/fontisan/constants.rb +19 -0
- data/lib/fontisan/conversion_options.rb +30 -19
- data/lib/fontisan/converters/cff_table_builder.rb +0 -3
- data/lib/fontisan/converters/collection_converter.rb +0 -8
- data/lib/fontisan/converters/conversion_strategy.rb +161 -46
- data/lib/fontisan/converters/format_converter.rb +143 -32
- data/lib/fontisan/converters/glyf_table_builder.rb +0 -2
- data/lib/fontisan/converters/outline_converter.rb +0 -19
- data/lib/fontisan/converters/outline_extraction.rb +0 -5
- data/lib/fontisan/converters/outline_optimizer.rb +0 -5
- data/lib/fontisan/converters/svg_generator.rb +0 -4
- data/lib/fontisan/converters/table_copier.rb +0 -2
- data/lib/fontisan/converters/type1_converter.rb +0 -11
- data/lib/fontisan/converters/woff2_encoder.rb +49 -20
- data/lib/fontisan/converters/woff_writer.rb +211 -282
- data/lib/fontisan/converters.rb +21 -0
- data/lib/fontisan/dfont_collection.rb +29 -10
- data/lib/fontisan/export/exporter.rb +0 -6
- data/lib/fontisan/export/transformers/font_to_ttx.rb +0 -9
- data/lib/fontisan/export/transformers/head_transformer.rb +0 -2
- data/lib/fontisan/export/transformers/hhea_transformer.rb +0 -2
- data/lib/fontisan/export/transformers/maxp_transformer.rb +0 -2
- data/lib/fontisan/export/transformers/name_transformer.rb +0 -2
- data/lib/fontisan/export/transformers/os2_transformer.rb +0 -2
- data/lib/fontisan/export/transformers/post_transformer.rb +0 -2
- data/lib/fontisan/export/transformers.rb +17 -0
- data/lib/fontisan/export.rb +13 -0
- data/lib/fontisan/font_loader.rb +14 -19
- data/lib/fontisan/font_writer.rb +0 -1
- data/lib/fontisan/formatters/audit_diff_text_renderer.rb +122 -0
- data/lib/fontisan/formatters/audit_text_renderer.rb +324 -0
- data/lib/fontisan/formatters/library_summary_text_renderer.rb +99 -0
- data/lib/fontisan/formatters/text_formatter.rb +6 -0
- data/lib/fontisan/formatters.rb +12 -0
- data/lib/fontisan/hints/hint_converter.rb +0 -1
- data/lib/fontisan/hints/postscript_hint_applier.rb +0 -9
- data/lib/fontisan/hints/postscript_hint_extractor.rb +0 -2
- data/lib/fontisan/hints/truetype_hint_extractor.rb +0 -2
- data/lib/fontisan/hints.rb +16 -0
- data/lib/fontisan/metrics_calculator.rb +0 -2
- data/lib/fontisan/models/all_scripts_features_info.rb +0 -1
- data/lib/fontisan/models/audit/audit_axis.rb +30 -0
- data/lib/fontisan/models/audit/audit_block.rb +32 -0
- data/lib/fontisan/models/audit/audit_diff.rb +77 -0
- data/lib/fontisan/models/audit/audit_report.rb +153 -0
- data/lib/fontisan/models/audit/codepoint_range.rb +40 -0
- data/lib/fontisan/models/audit/codepoint_set_diff.rb +34 -0
- data/lib/fontisan/models/audit/color_capabilities.rb +93 -0
- data/lib/fontisan/models/audit/duplicate_group.rb +23 -0
- data/lib/fontisan/models/audit/embedding_type.rb +76 -0
- data/lib/fontisan/models/audit/field_change.rb +28 -0
- data/lib/fontisan/models/audit/fs_selection_flags.rb +61 -0
- data/lib/fontisan/models/audit/gasp_range.rb +63 -0
- data/lib/fontisan/models/audit/hinting.rb +93 -0
- data/lib/fontisan/models/audit/library_summary.rb +40 -0
- data/lib/fontisan/models/audit/licensing.rb +48 -0
- data/lib/fontisan/models/audit/metrics.rb +111 -0
- data/lib/fontisan/models/audit/named_instance.rb +41 -0
- data/lib/fontisan/models/audit/opentype_layout.rb +40 -0
- data/lib/fontisan/models/audit/script_coverage_row.rb +26 -0
- data/lib/fontisan/models/audit/script_features.rb +28 -0
- data/lib/fontisan/models/audit/variation_detail.rb +44 -0
- data/lib/fontisan/models/audit.rb +33 -0
- data/lib/fontisan/models/cldr/language_coverage.rb +31 -0
- data/lib/fontisan/models/cldr.rb +12 -0
- data/lib/fontisan/models/collection_brief_info.rb +0 -1
- data/lib/fontisan/models/collection_info.rb +0 -2
- data/lib/fontisan/models/collection_list_info.rb +0 -1
- data/lib/fontisan/models/collection_validation_report.rb +0 -2
- data/lib/fontisan/models/color_glyph.rb +0 -1
- data/lib/fontisan/models/font_report.rb +0 -1
- data/lib/fontisan/models/ttx/tables.rb +21 -0
- data/lib/fontisan/models/ttx/ttfont.rb +0 -8
- data/lib/fontisan/models/ttx.rb +14 -0
- data/lib/fontisan/models/ucd/ucd.rb +38 -0
- data/lib/fontisan/models/ucd/ucd_char.rb +67 -0
- data/lib/fontisan/models/ucd.rb +19 -0
- data/lib/fontisan/models.rb +47 -0
- data/lib/fontisan/open_type_collection.rb +6 -5
- data/lib/fontisan/open_type_font.rb +8 -2
- data/lib/fontisan/open_type_font_extensions.rb +9 -9
- data/lib/fontisan/optimizers/pattern_analyzer.rb +0 -1
- data/lib/fontisan/optimizers.rb +14 -0
- data/lib/fontisan/outline_extractor.rb +0 -2
- data/lib/fontisan/parsers/dfont_parser.rb +0 -1
- data/lib/fontisan/parsers.rb +10 -0
- data/lib/fontisan/pipeline/format_detector.rb +29 -102
- data/lib/fontisan/pipeline/output_writer.rb +11 -9
- data/lib/fontisan/pipeline/strategies/instance_strategy.rb +0 -4
- data/lib/fontisan/pipeline/strategies/named_strategy.rb +0 -4
- data/lib/fontisan/pipeline/strategies/preserve_strategy.rb +0 -2
- data/lib/fontisan/pipeline/strategies.rb +14 -0
- data/lib/fontisan/pipeline/transformation_pipeline.rb +0 -7
- data/lib/fontisan/pipeline/variation_resolver.rb +0 -7
- data/lib/fontisan/pipeline.rb +13 -0
- data/lib/fontisan/sfnt_font.rb +29 -14
- data/lib/fontisan/sfnt_table.rb +0 -4
- data/lib/fontisan/subset/builder.rb +0 -6
- data/lib/fontisan/subset.rb +13 -0
- data/lib/fontisan/svg/font_generator.rb +0 -4
- data/lib/fontisan/svg/glyph_generator.rb +0 -2
- data/lib/fontisan/svg.rb +12 -0
- data/lib/fontisan/tables/cbdt.rb +0 -1
- data/lib/fontisan/tables/cblc.rb +0 -1
- data/lib/fontisan/tables/cff/charset.rb +0 -1
- data/lib/fontisan/tables/cff/charstring.rb +0 -1
- data/lib/fontisan/tables/cff/charstring_rebuilder.rb +0 -4
- data/lib/fontisan/tables/cff/charstrings_index.rb +0 -3
- data/lib/fontisan/tables/cff/dict.rb +0 -1
- data/lib/fontisan/tables/cff/encoding.rb +0 -1
- data/lib/fontisan/tables/cff/header.rb +0 -2
- data/lib/fontisan/tables/cff/hint_operation_injector.rb +0 -2
- data/lib/fontisan/tables/cff/index.rb +0 -1
- data/lib/fontisan/tables/cff/private_dict.rb +0 -2
- data/lib/fontisan/tables/cff/private_dict_writer.rb +0 -2
- data/lib/fontisan/tables/cff/table_builder.rb +0 -6
- data/lib/fontisan/tables/cff/top_dict.rb +0 -2
- data/lib/fontisan/tables/cff.rb +22 -15
- data/lib/fontisan/tables/cff2/charstring_parser.rb +0 -2
- data/lib/fontisan/tables/cff2/table_builder.rb +0 -11
- data/lib/fontisan/tables/cff2/table_reader.rb +0 -2
- data/lib/fontisan/tables/cff2.rb +13 -14
- data/lib/fontisan/tables/cmap.rb +24 -2
- data/lib/fontisan/tables/cmap_table.rb +0 -3
- data/lib/fontisan/tables/colr.rb +0 -1
- data/lib/fontisan/tables/cpal.rb +0 -1
- data/lib/fontisan/tables/cvar.rb +0 -2
- data/lib/fontisan/tables/fvar.rb +0 -1
- data/lib/fontisan/tables/glyf/compound_glyph_resolver.rb +0 -2
- data/lib/fontisan/tables/glyf/glyph_builder.rb +0 -3
- data/lib/fontisan/tables/glyf.rb +0 -6
- data/lib/fontisan/tables/glyf_table.rb +0 -3
- data/lib/fontisan/tables/gpos.rb +0 -2
- data/lib/fontisan/tables/gsub.rb +0 -2
- data/lib/fontisan/tables/gvar.rb +0 -2
- data/lib/fontisan/tables/head.rb +0 -2
- data/lib/fontisan/tables/head_table.rb +0 -3
- data/lib/fontisan/tables/hhea.rb +0 -2
- data/lib/fontisan/tables/hhea_table.rb +0 -3
- data/lib/fontisan/tables/hmtx.rb +0 -2
- data/lib/fontisan/tables/hmtx_table.rb +0 -3
- data/lib/fontisan/tables/hvar.rb +0 -3
- data/lib/fontisan/tables/loca.rb +0 -2
- data/lib/fontisan/tables/loca_table.rb +0 -3
- data/lib/fontisan/tables/maxp.rb +0 -2
- data/lib/fontisan/tables/maxp_table.rb +0 -3
- data/lib/fontisan/tables/mvar.rb +0 -3
- data/lib/fontisan/tables/name.rb +0 -2
- data/lib/fontisan/tables/name_table.rb +0 -3
- data/lib/fontisan/tables/os2_table.rb +0 -3
- data/lib/fontisan/tables/post_table.rb +0 -3
- data/lib/fontisan/tables/sbix.rb +0 -1
- data/lib/fontisan/tables/svg.rb +0 -1
- data/lib/fontisan/tables/variation_common.rb +0 -1
- data/lib/fontisan/tables/vvar.rb +0 -3
- data/lib/fontisan/tables.rb +54 -0
- data/lib/fontisan/true_type_collection.rb +6 -14
- data/lib/fontisan/true_type_font.rb +8 -2
- data/lib/fontisan/true_type_font_extensions.rb +9 -9
- data/lib/fontisan/type1/afm_generator.rb +0 -4
- data/lib/fontisan/type1/conversion_options.rb +0 -2
- data/lib/fontisan/type1/encodings.rb +0 -2
- data/lib/fontisan/type1/generator.rb +0 -8
- data/lib/fontisan/type1/pfa_generator.rb +0 -3
- data/lib/fontisan/type1/pfb_generator.rb +0 -5
- data/lib/fontisan/type1/pfm_generator.rb +0 -4
- data/lib/fontisan/type1.rb +42 -69
- data/lib/fontisan/type1_font.rb +40 -11
- data/lib/fontisan/ucd/aggregator.rb +73 -0
- data/lib/fontisan/ucd/cache_manager.rb +111 -0
- data/lib/fontisan/ucd/config.rb +59 -0
- data/lib/fontisan/ucd/download_error.rb +9 -0
- data/lib/fontisan/ucd/downloader.rb +88 -0
- data/lib/fontisan/ucd/error.rb +8 -0
- data/lib/fontisan/ucd/index.rb +103 -0
- data/lib/fontisan/ucd/index_builder.rb +107 -0
- data/lib/fontisan/ucd/range_entry.rb +56 -0
- data/lib/fontisan/ucd/unknown_version_error.rb +9 -0
- data/lib/fontisan/ucd/version_resolver.rb +79 -0
- data/lib/fontisan/ucd.rb +23 -0
- data/lib/fontisan/utilities/checksum_calculator.rb +0 -1
- data/lib/fontisan/utilities.rb +10 -0
- data/lib/fontisan/utils.rb +10 -0
- data/lib/fontisan/validation/collection_validator.rb +0 -2
- data/lib/fontisan/validation.rb +9 -0
- data/lib/fontisan/validators/basic_validator.rb +0 -2
- data/lib/fontisan/validators/font_book_validator.rb +0 -2
- data/lib/fontisan/validators/opentype_validator.rb +0 -2
- data/lib/fontisan/validators/profile_loader.rb +0 -5
- data/lib/fontisan/validators/validator.rb +0 -2
- data/lib/fontisan/validators/web_font_validator.rb +0 -2
- data/lib/fontisan/validators.rb +14 -0
- data/lib/fontisan/variable/delta_applicator.rb +0 -4
- data/lib/fontisan/variable/instancer.rb +0 -3
- data/lib/fontisan/variable/static_font_builder.rb +0 -3
- data/lib/fontisan/variable.rb +16 -0
- data/lib/fontisan/variation/blend_applier.rb +0 -2
- data/lib/fontisan/variation/cache.rb +0 -2
- data/lib/fontisan/variation/converter.rb +0 -3
- data/lib/fontisan/variation/data_extractor.rb +0 -2
- data/lib/fontisan/variation/delta_applier.rb +0 -5
- data/lib/fontisan/variation/inspector.rb +0 -1
- data/lib/fontisan/variation/instance_generator.rb +0 -6
- data/lib/fontisan/variation/instance_writer.rb +0 -5
- data/lib/fontisan/variation/metrics_adjuster.rb +0 -4
- data/lib/fontisan/variation/optimizer.rb +0 -3
- data/lib/fontisan/variation/parallel_generator.rb +0 -3
- data/lib/fontisan/variation/subsetter.rb +0 -4
- data/lib/fontisan/variation/tuple_variation_header.rb +0 -2
- data/lib/fontisan/variation/variable_svg_generator.rb +0 -3
- data/lib/fontisan/variation/variation_context.rb +0 -3
- data/lib/fontisan/variation/variation_preserver.rb +0 -3
- data/lib/fontisan/variation.rb +31 -0
- data/lib/fontisan/version.rb +1 -1
- data/lib/fontisan/woff2.rb +13 -0
- data/lib/fontisan/woff2_font.rb +31 -9
- data/lib/fontisan/woff_font.rb +31 -2
- data/lib/fontisan.rb +124 -196
- metadata +114 -7
- data/fontisan.gemspec +0 -48
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Fontisan
|
|
4
|
+
module Formatters
|
|
5
|
+
# Human-readable overview of a {Models::Audit::LibrarySummary}.
|
|
6
|
+
#
|
|
7
|
+
# Lists the per-face rollup counts, aggregate metrics, script coverage
|
|
8
|
+
# matrix, duplicate groups, and license distribution. The full per-face
|
|
9
|
+
# AuditReports are attached to the model; this view only shows the
|
|
10
|
+
# cross-face summaries (use YAML/JSON output for the full per-face data).
|
|
11
|
+
class LibrarySummaryTextRenderer
|
|
12
|
+
SEPARATOR = "=" * 80
|
|
13
|
+
LIST_LIMIT = 15
|
|
14
|
+
|
|
15
|
+
# @param summary [Models::Audit::LibrarySummary]
|
|
16
|
+
def initialize(summary)
|
|
17
|
+
@summary = summary
|
|
18
|
+
@lines = []
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# @return [String]
|
|
22
|
+
def render
|
|
23
|
+
render_header
|
|
24
|
+
render_aggregates
|
|
25
|
+
render_script_coverage
|
|
26
|
+
render_duplicates
|
|
27
|
+
render_license_distribution
|
|
28
|
+
@lines.join("\n")
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
def render_header
|
|
34
|
+
@lines << "LIBRARY SUMMARY"
|
|
35
|
+
@lines << SEPARATOR
|
|
36
|
+
@lines << "root: #{@summary.root_path}"
|
|
37
|
+
@lines << "files: #{@summary.total_files} faces: #{@summary.total_faces}"
|
|
38
|
+
exts = Array(@summary.scanned_extensions)
|
|
39
|
+
@lines << "formats: #{exts.empty? ? '(none)' : exts.join(', ')}"
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def render_aggregates
|
|
43
|
+
m = @summary.aggregate_metrics || {}
|
|
44
|
+
section("AGGREGATES")
|
|
45
|
+
@lines << " codepoints: #{m[:total_codepoints] || 0}"
|
|
46
|
+
@lines << " glyphs: #{m[:total_glyphs] || 0}"
|
|
47
|
+
@lines << " total size: #{format_bytes(m[:total_size_bytes] || 0)}"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def render_script_coverage
|
|
51
|
+
rows = Array(@summary.script_coverage)
|
|
52
|
+
return if rows.empty?
|
|
53
|
+
|
|
54
|
+
section("SCRIPT COVERAGE (top #{LIST_LIMIT})")
|
|
55
|
+
rows.first(LIST_LIMIT).each do |row|
|
|
56
|
+
@lines << " #{row.script}: #{row.face_count} face#{'s' unless row.face_count == 1}"
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def render_duplicates
|
|
61
|
+
groups = Array(@summary.duplicate_groups)
|
|
62
|
+
return if groups.empty?
|
|
63
|
+
|
|
64
|
+
section("DUPLICATES (#{groups.size} group#{'s' unless groups.size == 1})")
|
|
65
|
+
groups.each do |group|
|
|
66
|
+
@lines << " sha #{group.source_sha256[0, 12]}:"
|
|
67
|
+
group.files.each { |path| @lines << " #{path}" }
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def render_license_distribution
|
|
72
|
+
dist = @summary.license_distribution || {}
|
|
73
|
+
return if dist.empty?
|
|
74
|
+
|
|
75
|
+
section("LICENSE DISTRIBUTION")
|
|
76
|
+
dist.sort_by { |_url, count| -count }.each do |url, count|
|
|
77
|
+
@lines << " #{count} #{url}"
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def section(title)
|
|
82
|
+
@lines << ""
|
|
83
|
+
@lines << title
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def format_bytes(bytes)
|
|
87
|
+
return "0 B" if bytes.nil? || bytes.zero?
|
|
88
|
+
|
|
89
|
+
if bytes < 1024
|
|
90
|
+
"#{bytes} B"
|
|
91
|
+
elsif bytes < 1024 * 1024
|
|
92
|
+
"#{(bytes / 1024.0).round(2)} KB"
|
|
93
|
+
else
|
|
94
|
+
"#{(bytes / (1024.0 * 1024)).round(2)} MB"
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
@@ -44,6 +44,12 @@ module Fontisan
|
|
|
44
44
|
format_collection_info(model)
|
|
45
45
|
when Models::CollectionBriefInfo
|
|
46
46
|
format_collection_brief_info(model)
|
|
47
|
+
when Models::Audit::AuditReport
|
|
48
|
+
Formatters::AuditTextRenderer.new(model).render
|
|
49
|
+
when Models::Audit::AuditDiff
|
|
50
|
+
Formatters::AuditDiffTextRenderer.new(model).render
|
|
51
|
+
when Models::Audit::LibrarySummary
|
|
52
|
+
Formatters::LibrarySummaryTextRenderer.new(model).render
|
|
47
53
|
else
|
|
48
54
|
model.to_s
|
|
49
55
|
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Autoload hub for the Fontisan::Formatters namespace.
|
|
4
|
+
|
|
5
|
+
module Fontisan
|
|
6
|
+
module Formatters
|
|
7
|
+
autoload :AuditDiffTextRenderer, "fontisan/formatters/audit_diff_text_renderer"
|
|
8
|
+
autoload :AuditTextRenderer, "fontisan/formatters/audit_text_renderer"
|
|
9
|
+
autoload :LibrarySummaryTextRenderer, "fontisan/formatters/library_summary_text_renderer"
|
|
10
|
+
autoload :TextFormatter, "fontisan/formatters/text_formatter"
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative "../models/hint"
|
|
4
3
|
require "json"
|
|
5
4
|
|
|
6
5
|
module Fontisan
|
|
@@ -80,9 +79,6 @@ module Fontisan
|
|
|
80
79
|
cff2_data = tables["CFF2"] || tables["CFF2 "]
|
|
81
80
|
|
|
82
81
|
begin
|
|
83
|
-
require_relative "../tables/cff2/table_reader"
|
|
84
|
-
require_relative "../tables/cff2/table_builder"
|
|
85
|
-
|
|
86
82
|
reader = Tables::Cff2::TableReader.new(cff2_data)
|
|
87
83
|
|
|
88
84
|
# Validate CFF2 version
|
|
@@ -131,11 +127,6 @@ module Fontisan
|
|
|
131
127
|
|
|
132
128
|
# Apply hints (both font-level and per-glyph)
|
|
133
129
|
begin
|
|
134
|
-
require_relative "../tables/cff/table_builder"
|
|
135
|
-
require_relative "../tables/cff/charstring_rebuilder"
|
|
136
|
-
require_relative "../tables/cff/hint_operation_injector"
|
|
137
|
-
require_relative "../tables/cff"
|
|
138
|
-
|
|
139
130
|
# Parse CFF binary data into Cff object if needed
|
|
140
131
|
cff_data = tables["CFF "]
|
|
141
132
|
cff_table = if cff_data.is_a?(Tables::Cff)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Autoload hub for the Fontisan::Hints namespace.
|
|
4
|
+
|
|
5
|
+
module Fontisan
|
|
6
|
+
module Hints
|
|
7
|
+
autoload :HintConverter, "fontisan/hints/hint_converter"
|
|
8
|
+
autoload :HintValidator, "fontisan/hints/hint_validator"
|
|
9
|
+
autoload :PostScriptHintApplier, "fontisan/hints/postscript_hint_applier"
|
|
10
|
+
autoload :PostScriptHintExtractor, "fontisan/hints/postscript_hint_extractor"
|
|
11
|
+
autoload :TrueTypeHintApplier, "fontisan/hints/truetype_hint_applier"
|
|
12
|
+
autoload :TrueTypeHintExtractor, "fontisan/hints/truetype_hint_extractor"
|
|
13
|
+
autoload :TrueTypeInstructionAnalyzer, "fontisan/hints/truetype_instruction_analyzer"
|
|
14
|
+
autoload :TrueTypeInstructionGenerator, "fontisan/hints/truetype_instruction_generator"
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "lutaml/model"
|
|
4
|
+
|
|
5
|
+
module Fontisan
|
|
6
|
+
module Models
|
|
7
|
+
module Audit
|
|
8
|
+
# One fvar axis descriptor on an AuditReport.
|
|
9
|
+
#
|
|
10
|
+
# `min_value` / `default_value` / `max_value` are used (rather than
|
|
11
|
+
# `min` / `default` / `max`) to avoid colliding with Ruby's built-in
|
|
12
|
+
# `default` method on classes.
|
|
13
|
+
class AuditAxis < Lutaml::Model::Serializable
|
|
14
|
+
attribute :tag, :string
|
|
15
|
+
attribute :min_value, :float
|
|
16
|
+
attribute :default_value, :float
|
|
17
|
+
attribute :max_value, :float
|
|
18
|
+
attribute :name, :string
|
|
19
|
+
|
|
20
|
+
key_value do
|
|
21
|
+
map "tag", to: :tag
|
|
22
|
+
map "min_value", to: :min_value
|
|
23
|
+
map "default_value", to: :default_value
|
|
24
|
+
map "max_value", to: :max_value
|
|
25
|
+
map "name", to: :name
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "lutaml/model"
|
|
4
|
+
|
|
5
|
+
module Fontisan
|
|
6
|
+
module Models
|
|
7
|
+
module Audit
|
|
8
|
+
# One Unicode block coverage row on an AuditReport.
|
|
9
|
+
class AuditBlock < Lutaml::Model::Serializable
|
|
10
|
+
attribute :name, :string
|
|
11
|
+
attribute :first_cp, :integer
|
|
12
|
+
attribute :last_cp, :integer
|
|
13
|
+
attribute :range, :string
|
|
14
|
+
attribute :total, :integer
|
|
15
|
+
attribute :covered, :integer
|
|
16
|
+
attribute :fill_ratio, :float
|
|
17
|
+
attribute :complete, Lutaml::Model::Type::Boolean
|
|
18
|
+
|
|
19
|
+
key_value do
|
|
20
|
+
map "name", to: :name
|
|
21
|
+
map "first_cp", to: :first_cp
|
|
22
|
+
map "last_cp", to: :last_cp
|
|
23
|
+
map "range", to: :range
|
|
24
|
+
map "total", to: :total
|
|
25
|
+
map "covered", to: :covered
|
|
26
|
+
map "fill_ratio", to: :fill_ratio
|
|
27
|
+
map "complete", to: :complete
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "lutaml/model"
|
|
4
|
+
|
|
5
|
+
module Fontisan
|
|
6
|
+
module Models
|
|
7
|
+
module Audit
|
|
8
|
+
# Structural diff between two AuditReports.
|
|
9
|
+
#
|
|
10
|
+
# `left_source`/`right_source` are the original source_file paths
|
|
11
|
+
# (or report paths) so a consumer reading the diff alone can locate
|
|
12
|
+
# the inputs.
|
|
13
|
+
#
|
|
14
|
+
# `field_changes` lists scalar fields whose values changed.
|
|
15
|
+
# `codepoints` is the cmap delta (CodepointSetDiff).
|
|
16
|
+
# The remaining fields are array set-diffs over the report's
|
|
17
|
+
# structural inventory: OpenType features, scripts, UCD blocks, and
|
|
18
|
+
# CLDR languages. Each is split into `added_*` (in right, not left)
|
|
19
|
+
# and `removed_*` (in left, not right).
|
|
20
|
+
class AuditDiff < Lutaml::Model::Serializable
|
|
21
|
+
attribute :left_source, :string
|
|
22
|
+
attribute :right_source, :string
|
|
23
|
+
attribute :field_changes, FieldChange, collection: true
|
|
24
|
+
attribute :codepoints, CodepointSetDiff
|
|
25
|
+
attribute :added_features, :string, collection: true
|
|
26
|
+
attribute :removed_features, :string, collection: true
|
|
27
|
+
attribute :added_scripts, :string, collection: true
|
|
28
|
+
attribute :removed_scripts, :string, collection: true
|
|
29
|
+
attribute :added_blocks, :string, collection: true
|
|
30
|
+
attribute :removed_blocks, :string, collection: true
|
|
31
|
+
attribute :added_languages, :string, collection: true
|
|
32
|
+
attribute :removed_languages, :string, collection: true
|
|
33
|
+
|
|
34
|
+
key_value do
|
|
35
|
+
map "left_source", to: :left_source
|
|
36
|
+
map "right_source", to: :right_source
|
|
37
|
+
map "field_changes", to: :field_changes
|
|
38
|
+
map "codepoints", to: :codepoints
|
|
39
|
+
map "added_features", to: :added_features
|
|
40
|
+
map "removed_features", to: :removed_features
|
|
41
|
+
map "added_scripts", to: :added_scripts
|
|
42
|
+
map "removed_scripts", to: :removed_scripts
|
|
43
|
+
map "added_blocks", to: :added_blocks
|
|
44
|
+
map "removed_blocks", to: :removed_blocks
|
|
45
|
+
map "added_languages", to: :added_languages
|
|
46
|
+
map "removed_languages", to: :removed_languages
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# True when nothing differs. Useful for the text formatter.
|
|
50
|
+
#
|
|
51
|
+
# @return [Boolean]
|
|
52
|
+
def empty?
|
|
53
|
+
collection_empty?(field_changes) &&
|
|
54
|
+
added_codepoints.zero? && removed_codepoints.zero? &&
|
|
55
|
+
collection_empty?(added_features) && collection_empty?(removed_features) &&
|
|
56
|
+
collection_empty?(added_scripts) && collection_empty?(removed_scripts) &&
|
|
57
|
+
collection_empty?(added_blocks) && collection_empty?(removed_blocks) &&
|
|
58
|
+
collection_empty?(added_languages) && collection_empty?(removed_languages)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def added_codepoints
|
|
62
|
+
codepoints&.added_count || 0
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def removed_codepoints
|
|
66
|
+
codepoints&.removed_count || 0
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
private
|
|
70
|
+
|
|
71
|
+
def collection_empty?(value)
|
|
72
|
+
value.nil? || value.empty?
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "lutaml/model"
|
|
4
|
+
|
|
5
|
+
module Fontisan
|
|
6
|
+
module Models
|
|
7
|
+
module Audit
|
|
8
|
+
# Complete font audit report for a single face.
|
|
9
|
+
#
|
|
10
|
+
# Self-describing: one face per file. Carries source provenance
|
|
11
|
+
# (`source_file`, `source_sha256`, `font_index`, `num_fonts_in_source`)
|
|
12
|
+
# so a consumer reading a single face report knows whether the
|
|
13
|
+
# source was a standalone font or a collection face, and can locate
|
|
14
|
+
# siblings via the source hash.
|
|
15
|
+
#
|
|
16
|
+
# Constructed by Commands::AuditCommand. The model is passive —
|
|
17
|
+
# no font-parsing logic lives here.
|
|
18
|
+
class AuditReport < Lutaml::Model::Serializable
|
|
19
|
+
# Provenance
|
|
20
|
+
attribute :generated_at, :string
|
|
21
|
+
attribute :fontisan_version, :string
|
|
22
|
+
attribute :source_file, :string
|
|
23
|
+
attribute :source_sha256, :string
|
|
24
|
+
attribute :source_format, :string
|
|
25
|
+
|
|
26
|
+
# Source layout
|
|
27
|
+
attribute :font_index, :integer
|
|
28
|
+
attribute :num_fonts_in_source, :integer
|
|
29
|
+
|
|
30
|
+
# Identity (name table)
|
|
31
|
+
attribute :family_name, :string
|
|
32
|
+
attribute :subfamily_name, :string
|
|
33
|
+
attribute :full_name, :string
|
|
34
|
+
attribute :postscript_name, :string
|
|
35
|
+
attribute :version, :string
|
|
36
|
+
attribute :font_revision, :float
|
|
37
|
+
|
|
38
|
+
# Style (OS/2 + head)
|
|
39
|
+
attribute :weight_class, :integer
|
|
40
|
+
attribute :width_class, :integer
|
|
41
|
+
attribute :italic, Lutaml::Model::Type::Boolean
|
|
42
|
+
attribute :bold, Lutaml::Model::Type::Boolean
|
|
43
|
+
attribute :panose, :string
|
|
44
|
+
|
|
45
|
+
# Coverage
|
|
46
|
+
attribute :total_codepoints, :integer
|
|
47
|
+
attribute :total_glyphs, :integer
|
|
48
|
+
attribute :cmap_subtables, :integer, collection: true
|
|
49
|
+
attribute :codepoint_ranges, CodepointRange, collection: true
|
|
50
|
+
attribute :codepoints, :string, collection: true
|
|
51
|
+
|
|
52
|
+
# Aggregations (require UCD)
|
|
53
|
+
attribute :ucd_version, :string
|
|
54
|
+
attribute :blocks, AuditBlock, collection: true
|
|
55
|
+
attribute :unicode_scripts, :string, collection: true
|
|
56
|
+
|
|
57
|
+
# Per-language coverage (requires CLDR; opt-in via
|
|
58
|
+
# `--with-language-coverage`)
|
|
59
|
+
attribute :cldr_version, :string
|
|
60
|
+
attribute :language_coverage, Models::Cldr::LanguageCoverage, collection: true
|
|
61
|
+
|
|
62
|
+
# Licensing + embedding permissions (nil for Type 1)
|
|
63
|
+
attribute :licensing, Licensing
|
|
64
|
+
|
|
65
|
+
# Layout-critical metrics from head/hhea/OS/2/post (nil for Type 1)
|
|
66
|
+
attribute :metrics, Metrics
|
|
67
|
+
|
|
68
|
+
# Hinting summary from fpgm/prep/cvt/gasp/CFF charstrings (nil for Type 1)
|
|
69
|
+
attribute :hinting, Hinting
|
|
70
|
+
|
|
71
|
+
# Color-font capability summary from COLR/CPAL/SVG/CBDT/CBLC/sbix
|
|
72
|
+
# (nil for Type 1)
|
|
73
|
+
attribute :color_capabilities, ColorCapabilities
|
|
74
|
+
|
|
75
|
+
# Variable-font detail from fvar + variation side-tables
|
|
76
|
+
# (nil for non-variable faces and Type 1)
|
|
77
|
+
attribute :variation, VariationDetail
|
|
78
|
+
|
|
79
|
+
# OpenType layout summary from GSUB + GPOS
|
|
80
|
+
# (nil for Type 1)
|
|
81
|
+
attribute :opentype_layout, OpenTypeLayout
|
|
82
|
+
|
|
83
|
+
# Set when UCD download failed or any non-fatal issue was encountered.
|
|
84
|
+
attribute :warning, :string
|
|
85
|
+
|
|
86
|
+
key_value do
|
|
87
|
+
# Provenance
|
|
88
|
+
map "generated_at", to: :generated_at
|
|
89
|
+
map "fontisan_version", to: :fontisan_version
|
|
90
|
+
map "source_file", to: :source_file
|
|
91
|
+
map "source_sha256", to: :source_sha256
|
|
92
|
+
map "source_format", to: :source_format
|
|
93
|
+
|
|
94
|
+
# Source layout
|
|
95
|
+
map "font_index", to: :font_index
|
|
96
|
+
map "num_fonts_in_source", to: :num_fonts_in_source
|
|
97
|
+
|
|
98
|
+
# Identity
|
|
99
|
+
map "family_name", to: :family_name
|
|
100
|
+
map "subfamily_name", to: :subfamily_name
|
|
101
|
+
map "full_name", to: :full_name
|
|
102
|
+
map "postscript_name", to: :postscript_name
|
|
103
|
+
map "version", to: :version
|
|
104
|
+
map "font_revision", to: :font_revision
|
|
105
|
+
|
|
106
|
+
# Style
|
|
107
|
+
map "weight_class", to: :weight_class
|
|
108
|
+
map "width_class", to: :width_class
|
|
109
|
+
map "italic", to: :italic
|
|
110
|
+
map "bold", to: :bold
|
|
111
|
+
map "panose", to: :panose
|
|
112
|
+
|
|
113
|
+
# Coverage
|
|
114
|
+
map "total_codepoints", to: :total_codepoints
|
|
115
|
+
map "total_glyphs", to: :total_glyphs
|
|
116
|
+
map "cmap_subtables", to: :cmap_subtables
|
|
117
|
+
map "codepoint_ranges", to: :codepoint_ranges
|
|
118
|
+
map "codepoints", to: :codepoints
|
|
119
|
+
|
|
120
|
+
# Aggregations
|
|
121
|
+
map "ucd_version", to: :ucd_version
|
|
122
|
+
map "blocks", to: :blocks
|
|
123
|
+
map "unicode_scripts", to: :unicode_scripts
|
|
124
|
+
|
|
125
|
+
# CLDR per-language coverage
|
|
126
|
+
map "cldr_version", to: :cldr_version
|
|
127
|
+
map "language_coverage", to: :language_coverage
|
|
128
|
+
|
|
129
|
+
# Licensing
|
|
130
|
+
map "licensing", to: :licensing
|
|
131
|
+
|
|
132
|
+
# Metrics
|
|
133
|
+
map "metrics", to: :metrics
|
|
134
|
+
|
|
135
|
+
# Hinting
|
|
136
|
+
map "hinting", to: :hinting
|
|
137
|
+
|
|
138
|
+
# Color capabilities
|
|
139
|
+
map "color_capabilities", to: :color_capabilities
|
|
140
|
+
|
|
141
|
+
# Variation detail
|
|
142
|
+
map "variation", to: :variation
|
|
143
|
+
|
|
144
|
+
# OpenType layout
|
|
145
|
+
map "opentype_layout", to: :opentype_layout
|
|
146
|
+
|
|
147
|
+
# Warning
|
|
148
|
+
map "warning", to: :warning
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "lutaml/model"
|
|
4
|
+
|
|
5
|
+
module Fontisan
|
|
6
|
+
module Models
|
|
7
|
+
module Audit
|
|
8
|
+
# A contiguous run of covered codepoints.
|
|
9
|
+
#
|
|
10
|
+
# `first_cp`/`last_cp` are inclusive integer endpoints. A single-codepoint
|
|
11
|
+
# "range" has first_cp == last_cp and renders as `U+XXXX` (no dash).
|
|
12
|
+
#
|
|
13
|
+
# Produced by {Audit::CodepointRangeCoalescer} from the cmap coverage.
|
|
14
|
+
# The range view replaces the previous flat per-codepoint list as the
|
|
15
|
+
# default report shape — a 60k-codepoint CJK font produces tens of
|
|
16
|
+
# ranges rather than 60k strings.
|
|
17
|
+
class CodepointRange < Lutaml::Model::Serializable
|
|
18
|
+
attribute :first_cp, :integer
|
|
19
|
+
attribute :last_cp, :integer
|
|
20
|
+
|
|
21
|
+
key_value do
|
|
22
|
+
map "first_cp", to: :first_cp
|
|
23
|
+
map "last_cp", to: :last_cp
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Human-readable form: `U+XXXX` for single codepoints,
|
|
27
|
+
# `U+XXXX-U+XXXX` for true ranges.
|
|
28
|
+
#
|
|
29
|
+
# @return [String]
|
|
30
|
+
def to_s
|
|
31
|
+
if first_cp == last_cp
|
|
32
|
+
format("U+%04<cp>X", cp: first_cp)
|
|
33
|
+
else
|
|
34
|
+
format("U+%04<first>X-U+%04<last>X", first: first_cp, last: last_cp)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "lutaml/model"
|
|
4
|
+
|
|
5
|
+
module Fontisan
|
|
6
|
+
module Models
|
|
7
|
+
module Audit
|
|
8
|
+
# Diff between two cmap codepoint sets.
|
|
9
|
+
#
|
|
10
|
+
# `added`/`removed` are compact range lists (CodepointRange) so a
|
|
11
|
+
# large delta (e.g. CJK extension added) renders as a handful of
|
|
12
|
+
# ranges rather than thousands of codepoints.
|
|
13
|
+
#
|
|
14
|
+
# `unchanged_count` is the intersection size — useful as a sanity
|
|
15
|
+
# check that the two reports share enough coverage to be meaningfully
|
|
16
|
+
# comparable.
|
|
17
|
+
class CodepointSetDiff < Lutaml::Model::Serializable
|
|
18
|
+
attribute :added, CodepointRange, collection: true
|
|
19
|
+
attribute :removed, CodepointRange, collection: true
|
|
20
|
+
attribute :added_count, :integer
|
|
21
|
+
attribute :removed_count, :integer
|
|
22
|
+
attribute :unchanged_count, :integer
|
|
23
|
+
|
|
24
|
+
key_value do
|
|
25
|
+
map "added", to: :added
|
|
26
|
+
map "removed", to: :removed
|
|
27
|
+
map "added_count", to: :added_count
|
|
28
|
+
map "removed_count", to: :removed_count
|
|
29
|
+
map "unchanged_count", to: :unchanged_count
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "lutaml/model"
|
|
4
|
+
|
|
5
|
+
module Fontisan
|
|
6
|
+
module Models
|
|
7
|
+
module Audit
|
|
8
|
+
# Color-font capability summary for one face.
|
|
9
|
+
#
|
|
10
|
+
# Answers: is this a color font, and if so, which format(s)?
|
|
11
|
+
# Modern color font formats are mutually exclusive in design but a
|
|
12
|
+
# single face can carry more than one (e.g. NotoColorEmoji ships
|
|
13
|
+
# COLR + CBDT + SVG so legacy and modern stacks all render).
|
|
14
|
+
#
|
|
15
|
+
# `color_formats` is derived at extraction time so consumers read a
|
|
16
|
+
# flat string list instead of re-deriving from the boolean lattice.
|
|
17
|
+
# Empty array ⇒ no color support.
|
|
18
|
+
class ColorCapabilities < Lutaml::Model::Serializable
|
|
19
|
+
# COLR (vector color glyphs).
|
|
20
|
+
attribute :has_colr, Lutaml::Model::Type::Boolean
|
|
21
|
+
attribute :colr_version, :integer # 0 or 1
|
|
22
|
+
attribute :colr_base_glyph_count, :integer
|
|
23
|
+
attribute :colr_layer_count, :integer
|
|
24
|
+
|
|
25
|
+
# CPAL (color palette).
|
|
26
|
+
attribute :has_cpal, Lutaml::Model::Type::Boolean
|
|
27
|
+
attribute :cpal_palette_count, :integer
|
|
28
|
+
attribute :cpal_color_count, :integer
|
|
29
|
+
|
|
30
|
+
# SVG-in-OpenType.
|
|
31
|
+
attribute :has_svg, Lutaml::Model::Type::Boolean
|
|
32
|
+
attribute :svg_document_count, :integer
|
|
33
|
+
|
|
34
|
+
# CBDT/CBLC (color bitmaps — paired tables).
|
|
35
|
+
attribute :has_cbdt, Lutaml::Model::Type::Boolean
|
|
36
|
+
attribute :has_cblc, Lutaml::Model::Type::Boolean
|
|
37
|
+
# Strike count comes from the paired CBLC locator table.
|
|
38
|
+
attribute :cbdt_strike_count, :integer
|
|
39
|
+
|
|
40
|
+
# sbix (Apple color bitmaps).
|
|
41
|
+
attribute :has_sbix, Lutaml::Model::Type::Boolean
|
|
42
|
+
attribute :sbix_strike_count, :integer
|
|
43
|
+
|
|
44
|
+
# Derived: ordered list of active color format tags. One of
|
|
45
|
+
# "colr_v0", "colr_v1", "cpal", "svg", "cbdt", "sbix".
|
|
46
|
+
attribute :color_formats, :string, collection: true
|
|
47
|
+
|
|
48
|
+
key_value do
|
|
49
|
+
map "has_colr", to: :has_colr
|
|
50
|
+
map "colr_version", to: :colr_version
|
|
51
|
+
map "colr_base_glyph_count", to: :colr_base_glyph_count
|
|
52
|
+
map "colr_layer_count", to: :colr_layer_count
|
|
53
|
+
map "has_cpal", to: :has_cpal
|
|
54
|
+
map "cpal_palette_count", to: :cpal_palette_count
|
|
55
|
+
map "cpal_color_count", to: :cpal_color_count
|
|
56
|
+
map "has_svg", to: :has_svg
|
|
57
|
+
map "svg_document_count", to: :svg_document_count
|
|
58
|
+
map "has_cbdt", to: :has_cbdt
|
|
59
|
+
map "has_cblc", to: :has_cblc
|
|
60
|
+
map "cbdt_strike_count", to: :cbdt_strike_count
|
|
61
|
+
map "has_sbix", to: :has_sbix
|
|
62
|
+
map "sbix_strike_count", to: :sbix_strike_count
|
|
63
|
+
map "color_formats", to: :color_formats
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Canonical format tags, kept in spec order.
|
|
67
|
+
FORMAT_COLR_V0 = "colr_v0"
|
|
68
|
+
FORMAT_COLR_V1 = "colr_v1"
|
|
69
|
+
FORMAT_CPAL = "cpal"
|
|
70
|
+
FORMAT_SVG = "svg"
|
|
71
|
+
FORMAT_CBDT = "cbdt"
|
|
72
|
+
FORMAT_SBIX = "sbix"
|
|
73
|
+
|
|
74
|
+
# Derive the canonical color_formats list from individual flags.
|
|
75
|
+
# COLR v1 takes precedence over v0 — a v1 table can serve both.
|
|
76
|
+
#
|
|
77
|
+
# @return [Array<String>]
|
|
78
|
+
def self.derive_formats(has_colr:, colr_version:, has_cpal:,
|
|
79
|
+
has_svg:, has_cbdt:, has_sbix:)
|
|
80
|
+
[].tap do |arr|
|
|
81
|
+
if has_colr
|
|
82
|
+
arr << (colr_version == 1 ? FORMAT_COLR_V1 : FORMAT_COLR_V0)
|
|
83
|
+
end
|
|
84
|
+
arr << FORMAT_CPAL if has_cpal
|
|
85
|
+
arr << FORMAT_SVG if has_svg
|
|
86
|
+
arr << FORMAT_CBDT if has_cbdt
|
|
87
|
+
arr << FORMAT_SBIX if has_sbix
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|