fontisan 0.2.11 → 0.2.12
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 +214 -51
- data/README.adoc +160 -0
- data/lib/fontisan/cli.rb +177 -6
- data/lib/fontisan/commands/convert_command.rb +32 -1
- data/lib/fontisan/config/conversion_matrix.yml +132 -4
- data/lib/fontisan/constants.rb +12 -0
- data/lib/fontisan/conversion_options.rb +378 -0
- data/lib/fontisan/converters/collection_converter.rb +45 -10
- data/lib/fontisan/converters/format_converter.rb +2 -0
- data/lib/fontisan/converters/outline_converter.rb +78 -4
- data/lib/fontisan/converters/type1_converter.rb +559 -0
- data/lib/fontisan/font_loader.rb +46 -3
- data/lib/fontisan/type1/afm_generator.rb +436 -0
- data/lib/fontisan/type1/afm_parser.rb +298 -0
- data/lib/fontisan/type1/agl.rb +456 -0
- data/lib/fontisan/type1/charstring_converter.rb +240 -0
- data/lib/fontisan/type1/charstrings.rb +408 -0
- data/lib/fontisan/type1/conversion_options.rb +243 -0
- data/lib/fontisan/type1/decryptor.rb +183 -0
- data/lib/fontisan/type1/encodings.rb +697 -0
- data/lib/fontisan/type1/font_dictionary.rb +514 -0
- data/lib/fontisan/type1/generator.rb +220 -0
- data/lib/fontisan/type1/inf_generator.rb +332 -0
- data/lib/fontisan/type1/pfa_generator.rb +343 -0
- data/lib/fontisan/type1/pfa_parser.rb +158 -0
- data/lib/fontisan/type1/pfb_generator.rb +291 -0
- data/lib/fontisan/type1/pfb_parser.rb +166 -0
- data/lib/fontisan/type1/pfm_generator.rb +610 -0
- data/lib/fontisan/type1/pfm_parser.rb +433 -0
- data/lib/fontisan/type1/private_dict.rb +285 -0
- data/lib/fontisan/type1/ttf_to_type1_converter.rb +327 -0
- data/lib/fontisan/type1/upm_scaler.rb +118 -0
- data/lib/fontisan/type1.rb +73 -0
- data/lib/fontisan/type1_font.rb +331 -0
- data/lib/fontisan/version.rb +1 -1
- data/lib/fontisan.rb +2 -0
- metadata +26 -2
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "fileutils"
|
|
4
|
+
require_relative "upm_scaler"
|
|
5
|
+
require_relative "encodings"
|
|
6
|
+
require_relative "conversion_options"
|
|
7
|
+
require_relative "afm_generator"
|
|
8
|
+
require_relative "pfm_generator"
|
|
9
|
+
require_relative "pfa_generator"
|
|
10
|
+
require_relative "pfb_generator"
|
|
11
|
+
require_relative "inf_generator"
|
|
12
|
+
|
|
13
|
+
module Fontisan
|
|
14
|
+
module Type1
|
|
15
|
+
# Unified Type 1 font generator
|
|
16
|
+
#
|
|
17
|
+
# [`Generator`](lib/fontisan/type1/generator.rb) provides a unified interface
|
|
18
|
+
# for generating all Type 1 font formats from TrueType/OpenType fonts.
|
|
19
|
+
#
|
|
20
|
+
# This generator creates:
|
|
21
|
+
# - AFM (Adobe Font Metrics) - Text-based font metrics
|
|
22
|
+
# - PFM (Printer Font Metrics) - Windows font metrics
|
|
23
|
+
# - PFA (Printer Font ASCII) - Unix Type 1 font (ASCII-hex encoded)
|
|
24
|
+
# - PFB (Printer Font Binary) - Windows Type 1 font (binary)
|
|
25
|
+
# - INF (Font Information) - Windows installation metadata
|
|
26
|
+
#
|
|
27
|
+
# @example Generate all Type 1 formats with default options (1000 UPM)
|
|
28
|
+
# font = Fontisan::FontLoader.load("font.ttf")
|
|
29
|
+
# result = Fontisan::Type1::Generator.generate(font)
|
|
30
|
+
# result[:afm] # => AFM file content
|
|
31
|
+
# result[:pfm] # => PFM file content
|
|
32
|
+
# result[:pfb] # => PFB file content
|
|
33
|
+
# result[:inf] # => INF file content
|
|
34
|
+
#
|
|
35
|
+
# @example Generate Unix Type 1 (PFA) with custom options
|
|
36
|
+
# result = Fontisan::Type1::Generator.generate(font,
|
|
37
|
+
# format: :pfa,
|
|
38
|
+
# upm_scale: 1000,
|
|
39
|
+
# encoding: Fontisan::Type1::Encodings::AdobeStandard
|
|
40
|
+
# )
|
|
41
|
+
#
|
|
42
|
+
# @example Generate with ConversionOptions preset
|
|
43
|
+
# options = Fontisan::Type1::ConversionOptions.windows_type1
|
|
44
|
+
# result = Fontisan::Type1::Generator.generate(font, options)
|
|
45
|
+
#
|
|
46
|
+
# @see https://www.adobe.com/devnet/font/pdfs/5178.Type1.pdf
|
|
47
|
+
class Generator
|
|
48
|
+
# Default generation options
|
|
49
|
+
DEFAULT_OPTIONS = {
|
|
50
|
+
upm_scale: 1000,
|
|
51
|
+
encoding: Encodings::AdobeStandard,
|
|
52
|
+
decompose_composites: false,
|
|
53
|
+
convert_curves: true,
|
|
54
|
+
autohint: false,
|
|
55
|
+
preserve_hinting: false,
|
|
56
|
+
format: :pfb,
|
|
57
|
+
}.freeze
|
|
58
|
+
|
|
59
|
+
# Generate all Type 1 formats from a font
|
|
60
|
+
#
|
|
61
|
+
# @param font [Fontisan::TrueTypeFont, Fontisan::OpenTypeFont] Source font
|
|
62
|
+
# @param options [ConversionOptions, Hash] Generation options
|
|
63
|
+
# @return [Hash] Generated file contents
|
|
64
|
+
def self.generate(font, options = {})
|
|
65
|
+
options = normalize_options(options)
|
|
66
|
+
new(font, options).generate
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Generate Type 1 files and write to disk
|
|
70
|
+
#
|
|
71
|
+
# @param font [Fontisan::TrueTypeFont, Fontisan::OpenTypeFont] Source font
|
|
72
|
+
# @param output_dir [String] Directory to write files
|
|
73
|
+
# @param options [ConversionOptions, Hash] Generation options
|
|
74
|
+
# @return [Array<String>] Paths to generated files
|
|
75
|
+
def self.generate_to_files(font, output_dir, options = {})
|
|
76
|
+
options = normalize_options(options)
|
|
77
|
+
result = generate(font, options)
|
|
78
|
+
|
|
79
|
+
# Ensure output directory exists
|
|
80
|
+
FileUtils.mkdir_p(output_dir)
|
|
81
|
+
|
|
82
|
+
# Get base filename from font
|
|
83
|
+
base_name = extract_base_name(font)
|
|
84
|
+
|
|
85
|
+
# Write files
|
|
86
|
+
written_files = []
|
|
87
|
+
|
|
88
|
+
# Write AFM
|
|
89
|
+
if result[:afm]
|
|
90
|
+
afm_path = File.join(output_dir, "#{base_name}.afm")
|
|
91
|
+
File.write(afm_path, result[:afm], encoding: "ISO-8859-1")
|
|
92
|
+
written_files << afm_path
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Write PFM
|
|
96
|
+
if result[:pfm]
|
|
97
|
+
pfm_path = File.join(output_dir, "#{base_name}.pfm")
|
|
98
|
+
File.binwrite(pfm_path, result[:pfm])
|
|
99
|
+
written_files << pfm_path
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Write PFB or PFA
|
|
103
|
+
if result[:pfb]
|
|
104
|
+
pfb_path = File.join(output_dir, "#{base_name}.pfb")
|
|
105
|
+
File.binwrite(pfb_path, result[:pfb])
|
|
106
|
+
written_files << pfb_path
|
|
107
|
+
elsif result[:pfa]
|
|
108
|
+
pfa_path = File.join(output_dir, "#{base_name}.pfa")
|
|
109
|
+
File.write(pfa_path, result[:pfa])
|
|
110
|
+
written_files << pfa_path
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Write INF
|
|
114
|
+
if result[:inf]
|
|
115
|
+
inf_path = File.join(output_dir, "#{base_name}.inf")
|
|
116
|
+
File.write(inf_path, result[:inf], encoding: "ISO-8859-1")
|
|
117
|
+
written_files << inf_path
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
written_files
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# Initialize a new Generator
|
|
124
|
+
#
|
|
125
|
+
# @param font [Fontisan::TrueTypeFont, Fontisan::OpenTypeFont] Source font
|
|
126
|
+
# @param options [ConversionOptions, Hash] Generation options
|
|
127
|
+
def initialize(font, options = {})
|
|
128
|
+
@font = font
|
|
129
|
+
@options = normalize_options_value(options)
|
|
130
|
+
@metrics = MetricsCalculator.new(font)
|
|
131
|
+
|
|
132
|
+
# Set up scaler
|
|
133
|
+
upm_scale = @options.upm_scale || 1000
|
|
134
|
+
@scaler = if upm_scale == :native
|
|
135
|
+
UPMScaler.native(font)
|
|
136
|
+
else
|
|
137
|
+
UPMScaler.new(font, target_upm: upm_scale)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# Set up encoding
|
|
141
|
+
@encoding = @options.encoding || Encodings::AdobeStandard
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# Generate all Type 1 formats
|
|
145
|
+
#
|
|
146
|
+
# @return [Hash] Generated file contents
|
|
147
|
+
def generate
|
|
148
|
+
result = {}
|
|
149
|
+
|
|
150
|
+
# Always generate AFM
|
|
151
|
+
result[:afm] = AFMGenerator.generate(@font, to_hash)
|
|
152
|
+
|
|
153
|
+
# Always generate PFM (for Windows compatibility)
|
|
154
|
+
result[:pfm] = PFMGenerator.generate(@font, to_hash)
|
|
155
|
+
|
|
156
|
+
# Generate PFB or PFA based on format option
|
|
157
|
+
format = @options.format || :pfb
|
|
158
|
+
if format == :pfa
|
|
159
|
+
result[:pfa] = PFAGenerator.generate(@font, to_hash)
|
|
160
|
+
else
|
|
161
|
+
result[:pfb] = PFBGenerator.generate(@font, to_hash)
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# Generate INF for Windows installation
|
|
165
|
+
result[:inf] = INFGenerator.generate(@font, to_hash)
|
|
166
|
+
|
|
167
|
+
result
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
private
|
|
171
|
+
|
|
172
|
+
# Convert options to hash
|
|
173
|
+
#
|
|
174
|
+
# @return [Hash] Options as hash
|
|
175
|
+
def to_hash
|
|
176
|
+
{
|
|
177
|
+
upm_scale: @options.upm_scale || 1000,
|
|
178
|
+
encoding: @options.encoding || Encodings::AdobeStandard,
|
|
179
|
+
decompose_composites: @options.decompose_composites || false,
|
|
180
|
+
convert_curves: @options.convert_curves || true,
|
|
181
|
+
autohint: @options.autohint || false,
|
|
182
|
+
preserve_hinting: @options.preserve_hinting || false,
|
|
183
|
+
format: @options.format || :pfb,
|
|
184
|
+
}
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
# Normalize options to ConversionOptions
|
|
188
|
+
#
|
|
189
|
+
# @param options [ConversionOptions, Hash] Options to normalize
|
|
190
|
+
# @return [ConversionOptions] Normalized options
|
|
191
|
+
def self.normalize_options(options)
|
|
192
|
+
return options if options.is_a?(ConversionOptions)
|
|
193
|
+
|
|
194
|
+
ConversionOptions.new(options)
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# Instance method version of normalize_options
|
|
198
|
+
#
|
|
199
|
+
# @param options [ConversionOptions, Hash] Options to normalize
|
|
200
|
+
# @return [ConversionOptions] Normalized options
|
|
201
|
+
def normalize_options_value(options)
|
|
202
|
+
self.class.normalize_options(options)
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
# Extract base filename from font
|
|
206
|
+
#
|
|
207
|
+
# @param font [Fontisan::TrueTypeFont, Fontisan::OpenTypeFont] Source font
|
|
208
|
+
# @return [String] Base filename
|
|
209
|
+
def self.extract_base_name(font)
|
|
210
|
+
name_table = font.table(Constants::NAME_TAG)
|
|
211
|
+
if name_table.respond_to?(:postscript_name)
|
|
212
|
+
name = name_table.postscript_name(1) || name_table.postscript_name(3)
|
|
213
|
+
return name if name
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
font.post_script_name || "font"
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
end
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Fontisan
|
|
4
|
+
module Type1
|
|
5
|
+
# INF (Font Information) Generator
|
|
6
|
+
#
|
|
7
|
+
# [`INFGenerator`](lib/fontisan/type1/inf_generator.rb) generates INF files
|
|
8
|
+
# for Windows Type 1 font installation.
|
|
9
|
+
#
|
|
10
|
+
# INF files contain metadata for installing Type 1 fonts on Windows systems.
|
|
11
|
+
# They reference the PFB, PFM, and AFM files that make up a Windows Type 1 font.
|
|
12
|
+
#
|
|
13
|
+
# @example Generate INF from TTF
|
|
14
|
+
# font = Fontisan::FontLoader.load("font.ttf")
|
|
15
|
+
# inf_data = Fontisan::Type1::INFGenerator.generate(font)
|
|
16
|
+
# File.write("font.inf", inf_data)
|
|
17
|
+
#
|
|
18
|
+
# @example Generate INF with custom file names
|
|
19
|
+
# inf_data = Fontisan::Type1::INFGenerator.generate(font,
|
|
20
|
+
# pfb_file: "myfont.pfb",
|
|
21
|
+
# afm_file: "myfont.afm",
|
|
22
|
+
# pfm_file: "myfont.pfm"
|
|
23
|
+
# )
|
|
24
|
+
#
|
|
25
|
+
# @see https://www.adobe.com/devnet/font/pdfs/5005.PFM_Spec.pdf
|
|
26
|
+
class INFGenerator
|
|
27
|
+
# Generate INF file content from a font
|
|
28
|
+
#
|
|
29
|
+
# @param font [Fontisan::TrueTypeFont, Fontisan::OpenTypeFont] The font to generate INF from
|
|
30
|
+
# @param options [Hash] Generation options
|
|
31
|
+
# @option options [String] :pfb_file PFB filename (default: based on font name)
|
|
32
|
+
# @option options [String] :afm_file AFM filename (default: based on font name)
|
|
33
|
+
# @option options [String] :pfm_file PFM filename (default: based on font name)
|
|
34
|
+
# @option options [String] :inf_file INF filename (default: based on font name)
|
|
35
|
+
# @option options [String] :otf_file OTF filename (for OpenType fonts)
|
|
36
|
+
# @return [String] INF file content
|
|
37
|
+
def self.generate(font, options = {})
|
|
38
|
+
new(font, options).generate
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Generate INF file from a font and write to file
|
|
42
|
+
#
|
|
43
|
+
# @param font [Fontisan::TrueTypeFont, Fontisan::OpenTypeFont] The font to generate INF from
|
|
44
|
+
# @param path [String] Path to write INF file
|
|
45
|
+
# @param options [Hash] Generation options
|
|
46
|
+
# @return [void]
|
|
47
|
+
def self.generate_to_file(font, path, options = {})
|
|
48
|
+
inf_content = generate(font, options)
|
|
49
|
+
File.write(path, inf_content, encoding: "ISO-8859-1")
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Initialize a new INFGenerator
|
|
53
|
+
#
|
|
54
|
+
# @param font [Fontisan::TrueTypeFont, Fontisan::OpenTypeFont] The font to generate INF from
|
|
55
|
+
# @param options [Hash] Generation options
|
|
56
|
+
def initialize(font, options = {})
|
|
57
|
+
@font = font
|
|
58
|
+
@options = options
|
|
59
|
+
@metrics = MetricsCalculator.new(font)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Generate INF file content
|
|
63
|
+
#
|
|
64
|
+
# @return [String] INF file content
|
|
65
|
+
def generate
|
|
66
|
+
lines = []
|
|
67
|
+
|
|
68
|
+
# Font description section
|
|
69
|
+
lines << "[Font Description]"
|
|
70
|
+
lines << build_font_description
|
|
71
|
+
lines << ""
|
|
72
|
+
|
|
73
|
+
# Files section
|
|
74
|
+
lines << "[Files]"
|
|
75
|
+
lines << build_file_list
|
|
76
|
+
lines << ""
|
|
77
|
+
|
|
78
|
+
# Other section
|
|
79
|
+
lines << "[Other]"
|
|
80
|
+
lines << build_other_section
|
|
81
|
+
|
|
82
|
+
lines.join("\n")
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
private
|
|
86
|
+
|
|
87
|
+
# Build font description section
|
|
88
|
+
#
|
|
89
|
+
# @return [String] Font description lines
|
|
90
|
+
def build_font_description
|
|
91
|
+
lines = []
|
|
92
|
+
|
|
93
|
+
# Font name (required)
|
|
94
|
+
font_name = extract_font_name
|
|
95
|
+
lines << "FontName=#{font_name}"
|
|
96
|
+
|
|
97
|
+
# Font files
|
|
98
|
+
pfb_file = @options[:pfb_file] || default_pfb_file
|
|
99
|
+
lines << "FontFile=#{pfb_file}"
|
|
100
|
+
|
|
101
|
+
afm_file = @options[:afm_file] || default_afm_file
|
|
102
|
+
lines << "MetricsFile=#{afm_file}"
|
|
103
|
+
|
|
104
|
+
pfm_file = @options[:pfm_file] || default_pfm_file
|
|
105
|
+
lines << "WinMetricsFile=#{pfm_file}"
|
|
106
|
+
|
|
107
|
+
# Font family
|
|
108
|
+
family_name = extract_family_name
|
|
109
|
+
lines << "FamilyName=#{family_name}" if family_name
|
|
110
|
+
|
|
111
|
+
# Font weight
|
|
112
|
+
weight = extract_weight
|
|
113
|
+
lines << "Weight=#{weight}" if weight
|
|
114
|
+
|
|
115
|
+
# Italic angle
|
|
116
|
+
italic_angle = extract_italic_angle
|
|
117
|
+
lines << "ItalicAngle=#{italic_angle}" if italic_angle && italic_angle != 0
|
|
118
|
+
|
|
119
|
+
# Version
|
|
120
|
+
version = extract_version
|
|
121
|
+
lines << "Version=#{version}" if version
|
|
122
|
+
|
|
123
|
+
# Copyright
|
|
124
|
+
copyright = extract_copyright
|
|
125
|
+
lines << "Copyright=#{copyright}" if copyright
|
|
126
|
+
|
|
127
|
+
# Font type
|
|
128
|
+
lines << "FontType=Type 1"
|
|
129
|
+
|
|
130
|
+
lines.join("\n")
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# Build file list section
|
|
134
|
+
#
|
|
135
|
+
# @return [String] File list lines
|
|
136
|
+
def build_file_list
|
|
137
|
+
lines = []
|
|
138
|
+
|
|
139
|
+
# PFB file (required)
|
|
140
|
+
pfb_file = @options[:pfb_file] || default_pfb_file
|
|
141
|
+
lines << "#{pfb_file}=PFB"
|
|
142
|
+
|
|
143
|
+
# AFM file (required)
|
|
144
|
+
afm_file = @options[:afm_file] || default_afm_file
|
|
145
|
+
lines << "#{afm_file}=AFM"
|
|
146
|
+
|
|
147
|
+
# PFM file (required for Windows)
|
|
148
|
+
pfm_file = @options[:pfm_file] || default_pfm_file
|
|
149
|
+
lines << "#{pfm_file}=PFM"
|
|
150
|
+
|
|
151
|
+
# OTF file (if converting from OTF)
|
|
152
|
+
if @options[:otf_file]
|
|
153
|
+
lines << "#{@options[:otf_file]}=OTF"
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
lines.join("\n")
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# Build other section
|
|
160
|
+
#
|
|
161
|
+
# @return [String] Other section lines
|
|
162
|
+
def build_other_section
|
|
163
|
+
lines = []
|
|
164
|
+
|
|
165
|
+
# Installation notes
|
|
166
|
+
lines << "Notes=This font is generated from #{@font.post_script_name} by Fontisan"
|
|
167
|
+
|
|
168
|
+
# Vendor
|
|
169
|
+
vendor = extract_vendor
|
|
170
|
+
lines << "Vendor=#{vendor}" if vendor
|
|
171
|
+
|
|
172
|
+
# License
|
|
173
|
+
license = extract_license
|
|
174
|
+
lines << "License=#{license}" if license
|
|
175
|
+
|
|
176
|
+
lines.join("\n")
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
# Extract font name
|
|
180
|
+
#
|
|
181
|
+
# @return [String] Font name
|
|
182
|
+
def extract_font_name
|
|
183
|
+
name_table = @font.table(Constants::NAME_TAG)
|
|
184
|
+
return "" unless name_table
|
|
185
|
+
|
|
186
|
+
# Try full font name first, then postscript name
|
|
187
|
+
if name_table.respond_to?(:full_font_name)
|
|
188
|
+
name_table.full_font_name(1) || name_table.full_font_name(3) ||
|
|
189
|
+
extract_postscript_name
|
|
190
|
+
else
|
|
191
|
+
extract_postscript_name
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
# Extract PostScript name
|
|
196
|
+
#
|
|
197
|
+
# @return [String] PostScript name
|
|
198
|
+
def extract_postscript_name
|
|
199
|
+
name_table = @font.table(Constants::NAME_TAG)
|
|
200
|
+
return @font.post_script_name || "Unknown" unless name_table
|
|
201
|
+
|
|
202
|
+
if name_table.respond_to?(:postscript_name)
|
|
203
|
+
name_table.postscript_name(1) || name_table.postscript_name(3) ||
|
|
204
|
+
@font.post_script_name || "Unknown"
|
|
205
|
+
else
|
|
206
|
+
@font.post_script_name || "Unknown"
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
# Extract family name
|
|
211
|
+
#
|
|
212
|
+
# @return [String, nil] Family name
|
|
213
|
+
def extract_family_name
|
|
214
|
+
name_table = @font.table(Constants::NAME_TAG)
|
|
215
|
+
return nil unless name_table
|
|
216
|
+
|
|
217
|
+
if name_table.respond_to?(:font_family)
|
|
218
|
+
name_table.font_family(1) || name_table.font_family(3)
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
# Extract weight
|
|
223
|
+
#
|
|
224
|
+
# @return [String, nil] Weight
|
|
225
|
+
def extract_weight
|
|
226
|
+
os2 = @font.table(Constants::OS2_TAG)
|
|
227
|
+
return nil unless os2
|
|
228
|
+
|
|
229
|
+
weight_class = if os2.respond_to?(:us_weight_class)
|
|
230
|
+
os2.us_weight_class
|
|
231
|
+
elsif os2.respond_to?(:weight_class)
|
|
232
|
+
os2.weight_class
|
|
233
|
+
end
|
|
234
|
+
return nil unless weight_class
|
|
235
|
+
|
|
236
|
+
case weight_class
|
|
237
|
+
when 100..200 then "Thin"
|
|
238
|
+
when 200..300 then "ExtraLight"
|
|
239
|
+
when 300..400 then "Light"
|
|
240
|
+
when 400..500 then "Regular"
|
|
241
|
+
when 500..600 then "Medium"
|
|
242
|
+
when 600..700 then "SemiBold"
|
|
243
|
+
when 700..800 then "Bold"
|
|
244
|
+
when 800..900 then "ExtraBold"
|
|
245
|
+
when 900..1000 then "Black"
|
|
246
|
+
else "Regular"
|
|
247
|
+
end
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
# Extract italic angle
|
|
251
|
+
#
|
|
252
|
+
# @return [Float, nil] Italic angle
|
|
253
|
+
def extract_italic_angle
|
|
254
|
+
post = @font.table(Constants::POST_TAG)
|
|
255
|
+
return nil unless post
|
|
256
|
+
|
|
257
|
+
if post.respond_to?(:italic_angle)
|
|
258
|
+
post.italic_angle
|
|
259
|
+
end
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
# Extract version
|
|
263
|
+
#
|
|
264
|
+
# @return [String, nil] Version string
|
|
265
|
+
def extract_version
|
|
266
|
+
name_table = @font.table(Constants::NAME_TAG)
|
|
267
|
+
return nil unless name_table
|
|
268
|
+
|
|
269
|
+
if name_table.respond_to?(:version_string)
|
|
270
|
+
name_table.version_string(1) || name_table.version_string(3)
|
|
271
|
+
end
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
# Extract copyright
|
|
275
|
+
#
|
|
276
|
+
# @return [String, nil] Copyright notice
|
|
277
|
+
def extract_copyright
|
|
278
|
+
name_table = @font.table(Constants::NAME_TAG)
|
|
279
|
+
return nil unless name_table
|
|
280
|
+
|
|
281
|
+
if name_table.respond_to?(:copyright)
|
|
282
|
+
name_table.copyright(1) || name_table.copyright(3)
|
|
283
|
+
end
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
# Extract vendor/manufacturer
|
|
287
|
+
#
|
|
288
|
+
# @return [String, nil] Vendor name
|
|
289
|
+
def extract_vendor
|
|
290
|
+
name_table = @font.table(Constants::NAME_TAG)
|
|
291
|
+
return nil unless name_table
|
|
292
|
+
|
|
293
|
+
if name_table.respond_to?(:manufacturer)
|
|
294
|
+
name_table.manufacturer(1) || name_table.manufacturer(3)
|
|
295
|
+
end
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
# Extract license information
|
|
299
|
+
#
|
|
300
|
+
# @return [String, nil] License information
|
|
301
|
+
def extract_license
|
|
302
|
+
name_table = @font.table(Constants::NAME_TAG)
|
|
303
|
+
return nil unless name_table
|
|
304
|
+
|
|
305
|
+
if name_table.respond_to?(:license)
|
|
306
|
+
name_table.license(1) || name_table.license(3)
|
|
307
|
+
end
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
# Get default PFB filename
|
|
311
|
+
#
|
|
312
|
+
# @return [String] PFB filename
|
|
313
|
+
def default_pfb_file
|
|
314
|
+
"#{extract_postscript_name}.pfb"
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
# Get default AFM filename
|
|
318
|
+
#
|
|
319
|
+
# @return [String] AFM filename
|
|
320
|
+
def default_afm_file
|
|
321
|
+
"#{extract_postscript_name}.afm"
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
# Get default PFM filename
|
|
325
|
+
#
|
|
326
|
+
# @return [String] PFM filename
|
|
327
|
+
def default_pfm_file
|
|
328
|
+
"#{extract_postscript_name}.pfm"
|
|
329
|
+
end
|
|
330
|
+
end
|
|
331
|
+
end
|
|
332
|
+
end
|