fontina 0.1.0
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 +7 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +21 -0
- data/README.md +42 -0
- data/fontina.gemspec +29 -0
- data/lib/fontina/fetcher.rb +34 -0
- data/lib/fontina/fetchers/http.rb +43 -0
- data/lib/fontina/fetchers/local_file_path.rb +15 -0
- data/lib/fontina/fetchers/local_file_uri.rb +21 -0
- data/lib/fontina/fetchers/preprocessor.rb +18 -0
- data/lib/fontina/format.rb +39 -0
- data/lib/fontina/formats/fon/constants.rb +12 -0
- data/lib/fontina/formats/fon/container.rb +24 -0
- data/lib/fontina/formats/fon/dos_header.rb +14 -0
- data/lib/fontina/formats/fon/ne_exported_name_table.rb +24 -0
- data/lib/fontina/formats/fon/ne_font_dir.rb +50 -0
- data/lib/fontina/formats/fon/ne_header.rb +25 -0
- data/lib/fontina/formats/fon/ne_resource.rb +14 -0
- data/lib/fontina/formats/fon/ne_resource_name_info.rb +18 -0
- data/lib/fontina/formats/fon/ne_resource_table.rb +12 -0
- data/lib/fontina/formats/fon/ne_resource_type_info.rb +21 -0
- data/lib/fontina/formats/fon/ne_vs_version_info.rb +70 -0
- data/lib/fontina/formats/fon.rb +79 -0
- data/lib/fontina/formats/open_type/constants.rb +79 -0
- data/lib/fontina/formats/open_type/container.rb +10 -0
- data/lib/fontina/formats/open_type/head_table.rb +17 -0
- data/lib/fontina/formats/open_type/name_table.rb +34 -0
- data/lib/fontina/formats/open_type/offset_table.rb +15 -0
- data/lib/fontina/formats/open_type/os2_table.rb +14 -0
- data/lib/fontina/formats/open_type/table.rb +15 -0
- data/lib/fontina/formats/open_type/table_directory.rb +26 -0
- data/lib/fontina/formats/open_type.rb +77 -0
- data/lib/fontina/formats/shared/language_codes/mac.rb +125 -0
- data/lib/fontina/formats/shared/language_codes/windows.rb +211 -0
- data/lib/fontina/formats/shared/p_string.rb +16 -0
- data/lib/fontina/meta_package.rb +32 -0
- data/lib/fontina/package.rb +18 -0
- data/lib/fontina/version.rb +3 -0
- data/lib/fontina.rb +29 -0
- metadata +194 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 1709e3c73ce8a624a911a47e6eb00f0648a690e3
|
4
|
+
data.tar.gz: 9d743b1f8a9fc918afccfca1c42cf21aaef3f561
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 839971dac95f57ffb1a0ba3883ebbab127c03276f2d2bd2bebc78660ab63e8891dbb67f76e81418c1ee42b41c1b42727d2d2204a78583f11aea58d2116656850
|
7
|
+
data.tar.gz: 1dbab3c0e87be89f91a8a3adb403195100ec2b014af31507dbb49cdf273b4e11cec9e65b6fcd40d6b11187430e4a3c072f645a002f2c9e16769d29f9c4cbd21d
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2017 Michael Petter
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
# Fontina
|
2
|
+
|
3
|
+
[ ](https://rubygems.org/gems/fontina)
|
4
|
+
[](https://travis-ci.org/michaeljpetter/fontina)
|
5
|
+
|
6
|
+
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/fontina`. To experiment with that code, run `bin/console` for an interactive prompt.
|
7
|
+
|
8
|
+
TODO: Delete this and the text above, and describe your gem
|
9
|
+
|
10
|
+
## Installation
|
11
|
+
|
12
|
+
Add this line to your application's Gemfile:
|
13
|
+
|
14
|
+
```ruby
|
15
|
+
gem 'fontina'
|
16
|
+
```
|
17
|
+
|
18
|
+
And then execute:
|
19
|
+
|
20
|
+
$ bundle
|
21
|
+
|
22
|
+
Or install it yourself as:
|
23
|
+
|
24
|
+
$ gem install fontina
|
25
|
+
|
26
|
+
## Usage
|
27
|
+
|
28
|
+
TODO: Write usage instructions here
|
29
|
+
|
30
|
+
## Development
|
31
|
+
|
32
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/console` for an interactive prompt that will allow you to experiment.
|
33
|
+
|
34
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release` to create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
35
|
+
|
36
|
+
## Contributing
|
37
|
+
|
38
|
+
1. Fork it ( https://github.com/[my-github-username]/fontina/fork )
|
39
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
40
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
41
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
42
|
+
5. Create a new Pull Request
|
data/fontina.gemspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require_relative 'lib/fontina/version'
|
2
|
+
|
3
|
+
Gem::Specification.new do |gem|
|
4
|
+
gem.name = 'fontina'
|
5
|
+
gem.version = Fontina::VERSION
|
6
|
+
gem.author = 'Michael Petter'
|
7
|
+
gem.email = 'michaeljpetter@gmail.com'
|
8
|
+
|
9
|
+
gem.summary = 'A cheesy font utility'
|
10
|
+
gem.description = <<-END
|
11
|
+
Fetches, parses, and describes font files.
|
12
|
+
END
|
13
|
+
gem.homepage = 'http://github.com/michaeljpetter/fontina'
|
14
|
+
gem.license = 'MIT'
|
15
|
+
|
16
|
+
gem.platform = Gem::Platform::RUBY
|
17
|
+
gem.files = Dir.glob %w(lib/**/* Gemfile *.gemspec LICENSE* README*)
|
18
|
+
gem.require_paths = ['lib']
|
19
|
+
|
20
|
+
gem.add_dependency 'bindata', '>=2.3.5'
|
21
|
+
gem.add_dependency 'faraday', '< 1.0'
|
22
|
+
gem.add_dependency 'faraday_middleware', '~> 0.10.0'
|
23
|
+
gem.add_dependency 'mores', '>=0.1.5'
|
24
|
+
|
25
|
+
gem.add_development_dependency 'bundler', '~> 1.7'
|
26
|
+
gem.add_development_dependency 'rake', '~> 10.0'
|
27
|
+
gem.add_development_dependency 'rspec', '~> 3.4'
|
28
|
+
gem.add_development_dependency 'rspec-its', '~> 1.2'
|
29
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Fontina
|
2
|
+
|
3
|
+
module Fetcher
|
4
|
+
Result = Mores::ImmutableStruct.new(:content, :filename, :mime_type)
|
5
|
+
|
6
|
+
class << self
|
7
|
+
extend Forwardable
|
8
|
+
|
9
|
+
delegate :fetch => :succession
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def succession
|
14
|
+
@succession ||= Mores::Succession.new do
|
15
|
+
line :fetch do |location|
|
16
|
+
raise "Location '#{location}' is not supported"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def included(base)
|
22
|
+
succession >> base.new
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
%w[
|
30
|
+
preprocessor
|
31
|
+
http
|
32
|
+
local_file_uri
|
33
|
+
local_file_path
|
34
|
+
].each { |fetcher| require_relative "fetchers/#{fetcher}" }
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
3
|
+
module Fontina
|
4
|
+
|
5
|
+
class Fetchers::HTTP
|
6
|
+
include Fetcher
|
7
|
+
|
8
|
+
def fetch(location)
|
9
|
+
return super unless location.is_a? URI::HTTP
|
10
|
+
|
11
|
+
response = connection.get(location)
|
12
|
+
|
13
|
+
Result[response.body, get_filename(response), get_mime_type(response)]
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def connection
|
19
|
+
@connection ||= Faraday.new do |c|
|
20
|
+
c.use FaradayMiddleware::FollowRedirects, limit: 2
|
21
|
+
c.use Faraday::Response::RaiseError
|
22
|
+
c.adapter Faraday.default_adapter
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def get_filename(response)
|
27
|
+
get_attachment_filename(response) || File.basename(response.env.url.path)
|
28
|
+
end
|
29
|
+
|
30
|
+
def get_attachment_filename(response)
|
31
|
+
disposition = response.headers[:content_disposition] and
|
32
|
+
match = disposition.match(/^attachment; *filename=(?<q>"?)(?<filename>(\w| |\.|\\")*)\k<q>$/) and
|
33
|
+
match[:filename].gsub('\"', '"')
|
34
|
+
end
|
35
|
+
|
36
|
+
def get_mime_type(response)
|
37
|
+
type = response.headers[:content_type] and
|
38
|
+
match = type.match(/^(?<type>[\w-]+\/[\w-]+)(;.*)?$/) and
|
39
|
+
match[:type]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
|
3
|
+
module Fontina
|
4
|
+
|
5
|
+
class Fetchers::LocalFilePath
|
6
|
+
include Fetcher
|
7
|
+
|
8
|
+
def fetch(location)
|
9
|
+
return super unless location.is_a? String
|
10
|
+
|
11
|
+
Result[Pathname.new(location).binread, File.basename(location)]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'pathname'
|
3
|
+
|
4
|
+
module Fontina
|
5
|
+
|
6
|
+
class Fetchers::LocalFileURI
|
7
|
+
include Fetcher
|
8
|
+
|
9
|
+
def fetch(location)
|
10
|
+
return super unless
|
11
|
+
location.is_a?(URI::Generic) &&
|
12
|
+
location.scheme == 'file' &&
|
13
|
+
[nil, 'localhost'].include?(location.host)
|
14
|
+
|
15
|
+
path = URI.decode location.path
|
16
|
+
|
17
|
+
Result[Pathname.new(path).binread, File.basename(path)]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
3
|
+
module Fontina
|
4
|
+
|
5
|
+
class Fetchers::Preprocessor
|
6
|
+
include Fetcher
|
7
|
+
|
8
|
+
def fetch(location)
|
9
|
+
begin
|
10
|
+
uri = URI.parse location
|
11
|
+
location = uri if uri.scheme && !uri.opaque
|
12
|
+
rescue URI::Error; end
|
13
|
+
|
14
|
+
super
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Fontina
|
2
|
+
|
3
|
+
module Format
|
4
|
+
class << self
|
5
|
+
def for(criteria)
|
6
|
+
criteria.lazy
|
7
|
+
.map { |key, value| registry[key][value] }
|
8
|
+
.find(&:itself) or fail "no format matches #{criteria}"
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def register(format, criteria)
|
14
|
+
criteria.each do |key, args|
|
15
|
+
args.each { |arg| registry[key][arg] = format }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def registry
|
20
|
+
@registry ||= %i[extension mime_type]
|
21
|
+
.reduce(Hash.new({}.freeze)) { |h, k| h[k] = {}; h }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
registry.each_key do |key|
|
28
|
+
define_method key do |*args|
|
29
|
+
Format.send :register, self, key => args
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
%w[
|
37
|
+
fon
|
38
|
+
open_type
|
39
|
+
].each { |format| require_relative "formats/#{format}" }
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Fontina::Formats::FON
|
2
|
+
|
3
|
+
class FON_Container < BinData::Record
|
4
|
+
search_prefix :fon
|
5
|
+
|
6
|
+
dos_header :dos
|
7
|
+
|
8
|
+
skip to_abs_offset: -> { dos.new_addr }
|
9
|
+
ne_header :ne
|
10
|
+
|
11
|
+
skip to_abs_offset: -> { ne.abs_offset + ne.restab_offs }
|
12
|
+
ne_exported_name_table :restab
|
13
|
+
virtual :restab_count, assert: -> { restab.count == 2 }
|
14
|
+
|
15
|
+
skip to_abs_offset: -> { ne.nrestab_addr }
|
16
|
+
ne_exported_name_table :nrestab
|
17
|
+
virtual :nrestab_num_bytes, assert: -> { nrestab.num_bytes == ne.nrestab_len }
|
18
|
+
virtual :nrestab_count, assert: -> { nrestab.count == 2 }
|
19
|
+
virtual :nrestab_first, assert: -> { nrestab.first.start_with?('FONTRES ') }
|
20
|
+
|
21
|
+
delayed_io :rsrctab, type: :ne_resource_table, read_abs_offset: -> { ne.abs_offset + ne.rsrctab_offs }
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Fontina::Formats::FON
|
2
|
+
|
3
|
+
class FON_DOSHeader < BinData::Record
|
4
|
+
endian :little
|
5
|
+
hide :magic, :reloc_addr
|
6
|
+
|
7
|
+
uint16 :magic, assert: IMAGE_DOS_SIGNATURE
|
8
|
+
skip length: 22
|
9
|
+
uint16 :reloc_addr, assert: 0x40
|
10
|
+
skip length: 34
|
11
|
+
int32 :new_addr
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Fontina::Formats::FON
|
2
|
+
|
3
|
+
class FON_NEExportedName < BinData::Primitive
|
4
|
+
p_string :name
|
5
|
+
uint8 :enttab_idx, assert: 0
|
6
|
+
|
7
|
+
def get
|
8
|
+
name
|
9
|
+
end
|
10
|
+
|
11
|
+
def set(value)
|
12
|
+
name = value
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class FON_NEExportedNameTable < BinData::Array
|
17
|
+
search_prefix :fon
|
18
|
+
|
19
|
+
default_parameter read_until: -> { element.empty? }
|
20
|
+
|
21
|
+
ne_exported_name
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Fontina::Formats::FON
|
2
|
+
|
3
|
+
class FON_NEFontDirEntry < BinData::Record
|
4
|
+
endian :little
|
5
|
+
|
6
|
+
uint16 :version
|
7
|
+
uint32 :len
|
8
|
+
string :copyright, length: 60, trim_padding: true
|
9
|
+
uint16 :font_type
|
10
|
+
uint16 :points
|
11
|
+
uint16 :vert_dpi
|
12
|
+
uint16 :horiz_dpi
|
13
|
+
uint16 :ascent
|
14
|
+
uint16 :internal_leading
|
15
|
+
uint16 :external_leading
|
16
|
+
uint8 :italic
|
17
|
+
uint8 :underline
|
18
|
+
uint8 :strikeout
|
19
|
+
uint16 :weight
|
20
|
+
uint8 :char_set
|
21
|
+
uint16 :pix_width
|
22
|
+
uint16 :pix_height
|
23
|
+
uint8 :pitch_and_family
|
24
|
+
uint16 :avg_width
|
25
|
+
uint16 :max_width
|
26
|
+
uint8 :first_char
|
27
|
+
uint8 :last_char
|
28
|
+
uint8 :default_char
|
29
|
+
uint8 :break_char
|
30
|
+
uint16 :width_len
|
31
|
+
uint32 :device_addr
|
32
|
+
uint32 :face_addr
|
33
|
+
skip length: 4
|
34
|
+
stringz :device_name
|
35
|
+
stringz :face_name
|
36
|
+
end
|
37
|
+
|
38
|
+
class FON_NEFontDir < BinData::Record
|
39
|
+
endian :little
|
40
|
+
search_prefix :fon
|
41
|
+
hide :dir_count
|
42
|
+
|
43
|
+
uint16 :dir_count
|
44
|
+
array :dir, initial_length: :dir_count do
|
45
|
+
uint16 :ordinal
|
46
|
+
ne_font_dir_entry :entry
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Fontina::Formats::FON
|
2
|
+
|
3
|
+
class FON_NEHeader < BinData::Record
|
4
|
+
FLAGS_MASK = 0b1000100000000011
|
5
|
+
VALID_FLAGS = 0b1000000000000000
|
6
|
+
|
7
|
+
endian :little
|
8
|
+
hide :magic, :enttab_len, :flags
|
9
|
+
|
10
|
+
uint16 :magic, assert: IMAGE_OS2_SIGNATURE
|
11
|
+
skip length: 4
|
12
|
+
uint16 :enttab_len, assert: -> { value.between?(1, 2) }
|
13
|
+
skip length: 4
|
14
|
+
uint16 :flags, assert: -> { value & FLAGS_MASK == VALID_FLAGS }
|
15
|
+
skip length: 18
|
16
|
+
uint16 :nrestab_len
|
17
|
+
skip length: 2
|
18
|
+
uint16 :rsrctab_offs
|
19
|
+
uint16 :restab_offs
|
20
|
+
skip length: 4
|
21
|
+
int32 :nrestab_addr
|
22
|
+
skip length: 16
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Fontina::Formats::FON
|
2
|
+
|
3
|
+
class FON_NEResource < BinData::Choice
|
4
|
+
search_prefix :fon
|
5
|
+
|
6
|
+
default_parameter selection: -> { type_id & ~WORD_HIGHBIT }
|
7
|
+
|
8
|
+
ne_font_dir RT_FONTDIR
|
9
|
+
ne_vs_version_info RT_VERSION
|
10
|
+
|
11
|
+
virtual :default
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Fontina::Formats::FON
|
2
|
+
|
3
|
+
class FON_NEResourceNameInfo < BinData::Record
|
4
|
+
endian :little
|
5
|
+
search_prefix :fon
|
6
|
+
|
7
|
+
uint16 :data_blkaddr
|
8
|
+
uint16 :data_blklen
|
9
|
+
skip length: 2
|
10
|
+
uint16 :id
|
11
|
+
skip length: 4
|
12
|
+
|
13
|
+
delayed_io :data, read_abs_offset: -> { data_blkaddr << align_shift } do
|
14
|
+
buffer type: :ne_resource, length: -> { data_blklen << align_shift }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Fontina::Formats::FON
|
2
|
+
|
3
|
+
class FON_NEResourceTable < BinData::Record
|
4
|
+
endian :little
|
5
|
+
search_prefix :fon
|
6
|
+
|
7
|
+
uint16 :align_shift, assert: -> { value > 1 }
|
8
|
+
array :types, type: :ne_resource_type_info, read_until: -> { element.type_id.zero? }
|
9
|
+
array :rsrc_names, type: :p_string, read_until: -> { element.empty? }
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Fontina::Formats::FON
|
2
|
+
|
3
|
+
class FON_NEResourceTypeInfo < BinData::Record
|
4
|
+
VALID_TYPE_IDS = ([0] + [
|
5
|
+
RT_FONTDIR,
|
6
|
+
RT_FONT,
|
7
|
+
RT_VERSION,
|
8
|
+
15 # unknown; occurs in some system fonts
|
9
|
+
].map!(&WORD_HIGHBIT.method(:|))).freeze
|
10
|
+
|
11
|
+
endian :little
|
12
|
+
search_prefix :fon
|
13
|
+
hide :rsrc_count
|
14
|
+
|
15
|
+
uint16 :type_id, assert: -> { VALID_TYPE_IDS.include? value }
|
16
|
+
uint16 :rsrc_count, onlyif: -> { type_id.nonzero? }
|
17
|
+
skip length: 4, onlyif: -> { type_id.nonzero? }
|
18
|
+
array :name_info, type: :ne_resource_name_info, initial_length: :rsrc_count, onlyif: -> { type_id.nonzero? }
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module Fontina::Formats::FON
|
2
|
+
|
3
|
+
class FON_NEVersionData < BinData::Choice
|
4
|
+
endian :little
|
5
|
+
|
6
|
+
default_parameter selection: -> {
|
7
|
+
return STRINGZ if path[1...-2] == ['StringFileInfo']
|
8
|
+
return TRANSLATION if path[1..-1] == ['VarFileInfo', 'Translation']
|
9
|
+
:default
|
10
|
+
}
|
11
|
+
|
12
|
+
stringz STRINGZ = 1
|
13
|
+
|
14
|
+
array TRANSLATION = 2, read_until: :eof do
|
15
|
+
uint16 :locale
|
16
|
+
uint16 :codepage
|
17
|
+
end
|
18
|
+
|
19
|
+
virtual :default
|
20
|
+
end
|
21
|
+
|
22
|
+
class FON_NEVersionNode < BinData::Record
|
23
|
+
endian :little
|
24
|
+
search_prefix :fon
|
25
|
+
hide :node_len, :data_len, :children_len
|
26
|
+
|
27
|
+
uint16 :node_len
|
28
|
+
uint16 :data_len
|
29
|
+
stringz :name
|
30
|
+
virtual byte_align: 4
|
31
|
+
|
32
|
+
buffer :data, length: :data_len, type: :ne_version_data, onlyif: -> { data_len.nonzero? }
|
33
|
+
virtual :data_end
|
34
|
+
virtual byte_align: 4
|
35
|
+
|
36
|
+
virtual :children_len, initial_value: -> { node_len - data_end.rel_offset }
|
37
|
+
|
38
|
+
buffer :children, length: :children_len, onlyif: -> { children_len.nonzero? } do
|
39
|
+
array type: :ne_version_node, read_until: :eof
|
40
|
+
end
|
41
|
+
virtual byte_align: 4
|
42
|
+
|
43
|
+
def path
|
44
|
+
@path ||= (parent_node.path + [name]).freeze
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def parent_node
|
50
|
+
@parent_node ||= begin
|
51
|
+
value = self
|
52
|
+
loop { value = value.parent; break if value.is_a? self.class }
|
53
|
+
value
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
class FON_NEVsVersionInfo < FON_NEVersionNode
|
59
|
+
virtual :root_name, assert: -> { name == 'VS_VERSION_INFO' }
|
60
|
+
|
61
|
+
virtual :root_num_bytes,
|
62
|
+
byte_align: -> { 1 << align_shift },
|
63
|
+
assert: -> { num_bytes == data_blklen << align_shift }
|
64
|
+
|
65
|
+
def path
|
66
|
+
@path ||= [name].freeze
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module Fontina
|
2
|
+
|
3
|
+
module Formats::FON
|
4
|
+
extend Format
|
5
|
+
extension '.fon'
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def read(io)
|
9
|
+
package container.read io
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def container
|
15
|
+
@container ||= begin
|
16
|
+
%w[
|
17
|
+
shared/language_codes/windows
|
18
|
+
shared/p_string
|
19
|
+
fon/constants
|
20
|
+
fon/dos_header
|
21
|
+
fon/ne_header
|
22
|
+
fon/ne_exported_name_table
|
23
|
+
fon/ne_font_dir
|
24
|
+
fon/ne_vs_version_info
|
25
|
+
fon/ne_resource
|
26
|
+
fon/ne_resource_name_info
|
27
|
+
fon/ne_resource_type_info
|
28
|
+
fon/ne_resource_table
|
29
|
+
fon/container
|
30
|
+
].each { |file| require_relative file }
|
31
|
+
|
32
|
+
Class.new(FON_Container) { auto_call_delayed_io }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def package(fon)
|
37
|
+
language = get_language(fon)
|
38
|
+
|
39
|
+
Package[
|
40
|
+
[QualifiedName[
|
41
|
+
fon.nrestab.first.name.sub(/^.*:\s*/, ''), :windows, language
|
42
|
+
]],
|
43
|
+
|
44
|
+
get_rsrc_data(fon, RT_FONTDIR)
|
45
|
+
.flat_map(&:dir).map(&:entry)
|
46
|
+
.map do |e|
|
47
|
+
Font[
|
48
|
+
[QualifiedName[e.face_name, :windows, language]],
|
49
|
+
e.font_type[0] == 1 ? :vector : :raster,
|
50
|
+
e.points,
|
51
|
+
e.weight,
|
52
|
+
e.italic[0] == 1,
|
53
|
+
e.underline[0] == 1,
|
54
|
+
e.strikeout[0] == 1,
|
55
|
+
]
|
56
|
+
end
|
57
|
+
]
|
58
|
+
end
|
59
|
+
|
60
|
+
def get_language(fon)
|
61
|
+
language_id = get_rsrc_data(fon, RT_VERSION)
|
62
|
+
.flat_map(&:children).find_all { |c| c.name == 'VarFileInfo' }
|
63
|
+
.flat_map(&:children).find_all { |c| c.name == 'Translation' }
|
64
|
+
.flat_map(&:data).map(&:locale)
|
65
|
+
.first
|
66
|
+
|
67
|
+
Formats::Shared::LanguageCodes::WINDOWS[language_id]
|
68
|
+
end
|
69
|
+
|
70
|
+
def get_rsrc_data(fon, type)
|
71
|
+
fon.rsrctab
|
72
|
+
.types.find_all { |t| t.type_id == WORD_HIGHBIT | type }
|
73
|
+
.flat_map(&:name_info)
|
74
|
+
.map(&:data)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|