fontisan 0.2.16 → 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/Gemfile +6 -3
- 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/api/font-loader.md +21 -15
- 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 +48 -6
- 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 +189 -328
- 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 +128 -7
- data/fontisan.gemspec +0 -47
|
@@ -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
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "lutaml/model"
|
|
4
|
+
|
|
5
|
+
module Fontisan
|
|
6
|
+
module Models
|
|
7
|
+
module Audit
|
|
8
|
+
# Group of files that share an identical `source_sha256`.
|
|
9
|
+
#
|
|
10
|
+
# Detecting duplicate byte-identical files (regardless of filename)
|
|
11
|
+
# is the cheapest form of library hygiene: same bytes = same font.
|
|
12
|
+
class DuplicateGroup < Lutaml::Model::Serializable
|
|
13
|
+
attribute :source_sha256, :string
|
|
14
|
+
attribute :files, :string, collection: true
|
|
15
|
+
|
|
16
|
+
key_value do
|
|
17
|
+
map "source_sha256", to: :source_sha256
|
|
18
|
+
map "files", to: :files
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "lutaml/model"
|
|
4
|
+
|
|
5
|
+
module Fontisan
|
|
6
|
+
module Models
|
|
7
|
+
module Audit
|
|
8
|
+
# Decoded OS/2 fsType bitfield → canonical embedding-permission string.
|
|
9
|
+
#
|
|
10
|
+
# Per OpenType spec, fsType is a bitfield. Only one of bits 0-3 should
|
|
11
|
+
# be set (the basic permission level); bits 4-7 are modifiers that
|
|
12
|
+
# only apply when INSTALLABLE (bit 3) is set.
|
|
13
|
+
#
|
|
14
|
+
# The decoder normalizes to one of seven canonical strings so
|
|
15
|
+
# downstream consumers don't need to know the bit layout.
|
|
16
|
+
class EmbeddingType < Lutaml::Model::Serializable
|
|
17
|
+
# Bit masks (OpenType fsType bitfield).
|
|
18
|
+
RESTRICTED_LICENSE_NO_EMBEDDING = 0x0001
|
|
19
|
+
PREVIEW_AND_PRINT = 0x0002
|
|
20
|
+
EDITABLE_EMBEDDING = 0x0004
|
|
21
|
+
INSTALLABLE_EMBEDDING = 0x0008
|
|
22
|
+
NO_SUBSETTING = 0x0100
|
|
23
|
+
BITMAP_EMBEDDING_ONLY = 0x0200
|
|
24
|
+
|
|
25
|
+
attribute :value, :string
|
|
26
|
+
|
|
27
|
+
# Decoded canonical string for the given fsType bitfield.
|
|
28
|
+
#
|
|
29
|
+
# @param fs_type [Integer, nil] raw OS/2 fsType value
|
|
30
|
+
# @return [String, nil] canonical permission name, or nil when
|
|
31
|
+
# fs_type is nil
|
|
32
|
+
def self.decode(fs_type)
|
|
33
|
+
return nil if fs_type.nil?
|
|
34
|
+
|
|
35
|
+
if fs_type & RESTRICTED_LICENSE_NO_EMBEDDING != 0
|
|
36
|
+
"restricted_license"
|
|
37
|
+
elsif fs_type & PREVIEW_AND_PRINT != 0
|
|
38
|
+
"preview_print"
|
|
39
|
+
elsif fs_type & EDITABLE_EMBEDDING != 0
|
|
40
|
+
"editable"
|
|
41
|
+
elsif fs_type & INSTALLABLE_EMBEDDING != 0
|
|
42
|
+
installable_subcategory(fs_type)
|
|
43
|
+
elsif fs_type.zero?
|
|
44
|
+
"installable"
|
|
45
|
+
else
|
|
46
|
+
"unknown"
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Construct from a decoded canonical string.
|
|
51
|
+
#
|
|
52
|
+
# @param fs_type [Integer, nil]
|
|
53
|
+
def self.from_fs_type(fs_type)
|
|
54
|
+
new(value: decode(fs_type))
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def to_s
|
|
58
|
+
value
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def self.installable_subcategory(fs_type)
|
|
62
|
+
if fs_type & NO_SUBSETTING != 0 && fs_type & BITMAP_EMBEDDING_ONLY != 0
|
|
63
|
+
"installable_no_subsetting_bitmap_only"
|
|
64
|
+
elsif fs_type & NO_SUBSETTING != 0
|
|
65
|
+
"installable_no_subsetting"
|
|
66
|
+
elsif fs_type & BITMAP_EMBEDDING_ONLY != 0
|
|
67
|
+
"installable_bitmap_only"
|
|
68
|
+
else
|
|
69
|
+
"installable"
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
private_class_method :installable_subcategory
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "lutaml/model"
|
|
4
|
+
|
|
5
|
+
module Fontisan
|
|
6
|
+
module Models
|
|
7
|
+
module Audit
|
|
8
|
+
# One scalar field that differs between two AuditReports.
|
|
9
|
+
#
|
|
10
|
+
# `field` is the dotted attribute name (e.g. "weight_class").
|
|
11
|
+
# `left`/`right` are stringified values: nil → "", String → itself,
|
|
12
|
+
# anything else → its YAML form. Comparing the YAML form of nested
|
|
13
|
+
# models is intentionally avoided here — those diffs surface as
|
|
14
|
+
# structural add/remove lists on AuditDiff itself.
|
|
15
|
+
class FieldChange < Lutaml::Model::Serializable
|
|
16
|
+
attribute :field, :string
|
|
17
|
+
attribute :left, :string
|
|
18
|
+
attribute :right, :string
|
|
19
|
+
|
|
20
|
+
key_value do
|
|
21
|
+
map "field", to: :field
|
|
22
|
+
map "left", to: :left
|
|
23
|
+
map "right", to: :right
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "lutaml/model"
|
|
4
|
+
|
|
5
|
+
module Fontisan
|
|
6
|
+
module Models
|
|
7
|
+
module Audit
|
|
8
|
+
# Decoded OS/2 fsSelection bitfield → sorted array of flag names.
|
|
9
|
+
#
|
|
10
|
+
# Per OpenType spec, fsSelection is a bitfield with these bits:
|
|
11
|
+
#
|
|
12
|
+
# bit 0 (0x001): italic
|
|
13
|
+
# bit 1 (0x002): underscore
|
|
14
|
+
# bit 2 (0x004): negative
|
|
15
|
+
# bit 3 (0x008): outlined
|
|
16
|
+
# bit 4 (0x010): strikeout
|
|
17
|
+
# bit 5 (0x020): bold
|
|
18
|
+
# bit 6 (0x040): regular
|
|
19
|
+
# bit 7 (0x080): use_typo_metrics
|
|
20
|
+
# bit 8 (0x100): wws
|
|
21
|
+
# bit 9 (0x200): oblique
|
|
22
|
+
#
|
|
23
|
+
# Returns names in spec order (bit ascending).
|
|
24
|
+
class FsSelectionFlags < Lutaml::Model::Serializable
|
|
25
|
+
FLAGS = {
|
|
26
|
+
0x001 => "italic",
|
|
27
|
+
0x002 => "underscore",
|
|
28
|
+
0x004 => "negative",
|
|
29
|
+
0x008 => "outlined",
|
|
30
|
+
0x010 => "strikeout",
|
|
31
|
+
0x020 => "bold",
|
|
32
|
+
0x040 => "regular",
|
|
33
|
+
0x080 => "use_typo_metrics",
|
|
34
|
+
0x100 => "wws",
|
|
35
|
+
0x200 => "oblique",
|
|
36
|
+
}.freeze
|
|
37
|
+
|
|
38
|
+
attribute :flags, :string, collection: true
|
|
39
|
+
|
|
40
|
+
# Decoded array of flag names in spec order (bit ascending).
|
|
41
|
+
#
|
|
42
|
+
# @param fs_selection [Integer, nil] raw OS/2 fsSelection value
|
|
43
|
+
# @return [Array<String>, nil]
|
|
44
|
+
def self.decode(fs_selection)
|
|
45
|
+
return nil if fs_selection.nil?
|
|
46
|
+
|
|
47
|
+
FLAGS.each_with_object([]) do |(mask, name), acc|
|
|
48
|
+
acc << name if fs_selection & mask != 0
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Construct from a raw fsSelection value.
|
|
53
|
+
#
|
|
54
|
+
# @param fs_selection [Integer, nil]
|
|
55
|
+
def self.from_fs_selection(fs_selection)
|
|
56
|
+
new(flags: decode(fs_selection))
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "lutaml/model"
|
|
4
|
+
|
|
5
|
+
module Fontisan
|
|
6
|
+
module Models
|
|
7
|
+
module Audit
|
|
8
|
+
# One entry from the TrueType `gasp` (Grid-fitting And Scan-conversion
|
|
9
|
+
# Procedure) table.
|
|
10
|
+
#
|
|
11
|
+
# Each entry describes the hinting/smoothing policy that applies up to
|
|
12
|
+
# the given `max_ppem` (pixels-per-em). The OpenType spec defines four
|
|
13
|
+
# single-bit flags; the high 12 bits of the raw rangeFlags uint16 are
|
|
14
|
+
# reserved.
|
|
15
|
+
#
|
|
16
|
+
# Construct via {.from_flags} from the raw uint16 pair; never hand-build
|
|
17
|
+
# the bit decoding at call sites.
|
|
18
|
+
class GaspRange < Lutaml::Model::Serializable
|
|
19
|
+
# OpenType gasp rangeFlags bit masks.
|
|
20
|
+
GRIDFIT = 0x0001
|
|
21
|
+
DO_GRAY = 0x0002
|
|
22
|
+
SYMMETRIC_GRIDFIT = 0x0004
|
|
23
|
+
SYMMETRIC_SMOOTHING = 0x0008
|
|
24
|
+
|
|
25
|
+
attribute :max_ppem, :integer
|
|
26
|
+
attribute :gridfit, Lutaml::Model::Type::Boolean
|
|
27
|
+
attribute :do_gray, Lutaml::Model::Type::Boolean
|
|
28
|
+
attribute :symmetric_gridfit, Lutaml::Model::Type::Boolean
|
|
29
|
+
attribute :symmetric_smoothing, Lutaml::Model::Type::Boolean
|
|
30
|
+
|
|
31
|
+
key_value do
|
|
32
|
+
map "max_ppem", to: :max_ppem
|
|
33
|
+
map "gridfit", to: :gridfit
|
|
34
|
+
map "do_gray", to: :do_gray
|
|
35
|
+
map "symmetric_gridfit", to: :symmetric_gridfit
|
|
36
|
+
map "symmetric_smoothing", to: :symmetric_smoothing
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Build a GaspRange from the raw uint16 pair stored in the gasp table.
|
|
40
|
+
#
|
|
41
|
+
# @param max_ppem [Integer] rangeMaxPPEM (exclusive upper bound)
|
|
42
|
+
# @param flags [Integer] raw rangeFlags bitfield
|
|
43
|
+
# @return [GaspRange]
|
|
44
|
+
def self.from_flags(max_ppem, flags)
|
|
45
|
+
new(
|
|
46
|
+
max_ppem: max_ppem,
|
|
47
|
+
gridfit: (flags & GRIDFIT).positive?,
|
|
48
|
+
do_gray: (flags & DO_GRAY).positive?,
|
|
49
|
+
symmetric_gridfit: (flags & SYMMETRIC_GRIDFIT).positive?,
|
|
50
|
+
symmetric_smoothing: (flags & SYMMETRIC_SMOOTHING).positive?,
|
|
51
|
+
)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Derived: both gridfit and do_gray are set. Mac historically treated
|
|
55
|
+
# this combination as "do everything". Not serialized — compute on
|
|
56
|
+
# demand.
|
|
57
|
+
def gridfit_and_smoothing?
|
|
58
|
+
gridfit && do_gray
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|