distorted-floor 0.7.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/LICENSE +661 -0
- data/README.md +32 -0
- data/bin/distorted-floor +16 -0
- data/bin/repl +14 -0
- data/bin/setup +8 -0
- data/font/1252/LICENSE/MoreLessPerfectDOSVGA437/img/Less_Perfect_DOS_VGA.png +0 -0
- data/font/1252/LICENSE/MoreLessPerfectDOSVGA437/img/More_Perfect_DOS_VGA.png +0 -0
- data/font/1252/LICENSE/MoreLessPerfectDOSVGA437/img/Perfect_DOS_VGA.png +0 -0
- data/font/1252/LICENSE/MoreLessPerfectDOSVGA437/less_more_perfect_dos_vga_437.html +52 -0
- data/font/1252/LICENSE/PerfectDOSVGA437/font-comment.php@file=perfect_dos_vga_437.html +5 -0
- data/font/1252/LessPerfectDOSVGA.ttf +0 -0
- data/font/1252/MorePerfectDOSVGA.ttf +0 -0
- data/font/1252/Perfect DOS VGA 437 Win.ttf +0 -0
- data/font/437/Perfect DOS VGA 437.ttf +0 -0
- data/font/437/dos437.txt +72 -0
- data/font/65001/Anonymous Pro B.ttf +0 -0
- data/font/65001/Anonymous Pro BI.ttf +0 -0
- data/font/65001/Anonymous Pro I.ttf +0 -0
- data/font/65001/Anonymous Pro.ttf +0 -0
- data/font/65001/LICENSE/AnonymousPro/FONTLOG.txt +45 -0
- data/font/65001/LICENSE/AnonymousPro/OFL-FAQ.txt +235 -0
- data/font/65001/LICENSE/AnonymousPro/OFL.txt +94 -0
- data/font/65001/LICENSE/AnonymousPro/README.txt +55 -0
- data/font/850/ProFont-Bold-01/LICENSE +22 -0
- data/font/850/ProFont-Bold-01/readme.txt +28 -0
- data/font/850/ProFontWindows-Bold.ttf +0 -0
- data/font/850/ProFontWindows.ttf +0 -0
- data/font/850/Profont/LICENSE +22 -0
- data/font/850/Profont/readme.txt +31 -0
- data/font/932/LICENSE/README-ttf.txt +213 -0
- data/font/932/mona.ttf +0 -0
- data/lib/distorted-floor/checking_you_out.rb +78 -0
- data/lib/distorted-floor/click_again.rb +406 -0
- data/lib/distorted-floor/element_of_media/change.rb +114 -0
- data/lib/distorted-floor/element_of_media/compound.rb +120 -0
- data/lib/distorted-floor/element_of_media.rb +2 -0
- data/lib/distorted-floor/error_code.rb +55 -0
- data/lib/distorted-floor/floor.rb +17 -0
- data/lib/distorted-floor/invoker.rb +100 -0
- data/lib/distorted-floor/media_molecule/font.rb +200 -0
- data/lib/distorted-floor/media_molecule/image.rb +33 -0
- data/lib/distorted-floor/media_molecule/pdf.rb +45 -0
- data/lib/distorted-floor/media_molecule/svg.rb +46 -0
- data/lib/distorted-floor/media_molecule/text.rb +247 -0
- data/lib/distorted-floor/media_molecule/video.rb +21 -0
- data/lib/distorted-floor/media_molecule.rb +58 -0
- data/lib/distorted-floor/modular_technology/gstreamer.rb +175 -0
- data/lib/distorted-floor/modular_technology/pango.rb +90 -0
- data/lib/distorted-floor/modular_technology/ttfunk.rb +48 -0
- data/lib/distorted-floor/modular_technology/vips/ffi.rb +66 -0
- data/lib/distorted-floor/modular_technology/vips/load.rb +174 -0
- data/lib/distorted-floor/modular_technology/vips/operatio$.rb +268 -0
- data/lib/distorted-floor/modular_technology/vips/save.rb +135 -0
- data/lib/distorted-floor/modular_technology/vips.rb +17 -0
- data/lib/distorted-floor/monkey_business/encoding.rb +374 -0
- data/lib/distorted-floor/monkey_business/hash.rb +18 -0
- data/lib/distorted-floor/monkey_business/set.rb +15 -0
- data/lib/distorted-floor/monkey_business/string.rb +6 -0
- data/lib/distorted-floor.rb +2 -0
- metadata +215 -0
@@ -0,0 +1,55 @@
|
|
1
|
+
# https://ruby-doc.org/core/Exception.html sez:
|
2
|
+
# "It is recommended that a library should have one subclass of StandardError
|
3
|
+
# or RuntimeError and have specific exception types inherit from it.
|
4
|
+
# This allows the user to rescue a generic exception type to catch
|
5
|
+
# all exceptions the library may raise even if future versions of
|
6
|
+
# the library add new exception subclasses."
|
7
|
+
class DistorteDError < StandardError; end
|
8
|
+
|
9
|
+
# Normal "File not found" errors are platform-specific, in the Errno module,
|
10
|
+
# so define our own generic one for DD:
|
11
|
+
# https://ruby-doc.org/core/IOError.html
|
12
|
+
# https://ruby-doc.org/core/Errno.html
|
13
|
+
class DistorteDFileNotFoundError < DistorteDError; end
|
14
|
+
|
15
|
+
# The built-in NotImplementedError is for "when a feature is not implemented
|
16
|
+
# on the current platform", so make our own more appropriate ones.
|
17
|
+
class MediaTypeNotImplementedError < DistorteDError
|
18
|
+
attr_reader :name
|
19
|
+
def initialize(name)
|
20
|
+
super
|
21
|
+
@name = name
|
22
|
+
end
|
23
|
+
|
24
|
+
def message
|
25
|
+
"No supported media type for #{name}"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class MediaTypeOutputNotImplementedError < MediaTypeNotImplementedError
|
30
|
+
attr_reader :type, :context
|
31
|
+
def initialize(name, type, context)
|
32
|
+
super(name)
|
33
|
+
@type = type
|
34
|
+
@context = context
|
35
|
+
end
|
36
|
+
|
37
|
+
def message
|
38
|
+
"Unable to save #{name} as #{type.to_s} from #{context}"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class MediaTypeNotFoundError < DistorteDError
|
43
|
+
attr_reader :name
|
44
|
+
def initialize(name)
|
45
|
+
super
|
46
|
+
@name = name
|
47
|
+
end
|
48
|
+
|
49
|
+
def message
|
50
|
+
"Failed to detect media type for #{name}"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
class OutOfDateLibraryError < LoadError; end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
|
2
|
+
require 'set'
|
3
|
+
require 'distorted-floor/monkey_business/set'
|
4
|
+
|
5
|
+
require 'distorted-floor/invoker'
|
6
|
+
require 'distorted-floor/click_again'
|
7
|
+
require 'distorted-floor/checking_you_out'
|
8
|
+
|
9
|
+
|
10
|
+
module Cooltrainer; end
|
11
|
+
module Cooltrainer::DistorteD; end
|
12
|
+
|
13
|
+
class Cooltrainer::DistorteD::Floor
|
14
|
+
def initialize(src, dest, type: nil)
|
15
|
+
#TODO: Library-use entry-point
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
|
2
|
+
# Our custom Exceptions
|
3
|
+
require 'distorted-floor/error_code'
|
4
|
+
|
5
|
+
# File Typer
|
6
|
+
require 'distorted-floor/checking_you_out'
|
7
|
+
using ::DistorteD::CHECKING::YOU::OUT
|
8
|
+
require 'distorted-floor/media_molecule'
|
9
|
+
|
10
|
+
# Set.to_hash
|
11
|
+
require 'distorted-floor/monkey_business/set'
|
12
|
+
require 'set'
|
13
|
+
|
14
|
+
Cooltrainer::DistorteD::GEM_ROOT = File.expand_path(File.join(__dir__, '..'.freeze, '..'.freeze))
|
15
|
+
|
16
|
+
module Cooltrainer::DistorteD::Invoker
|
17
|
+
# Returns a Hash[CHECKING::YOU::OUT] => Hash[MediaMolecule] => Hash[param_alias] => Compound
|
18
|
+
def lower_world
|
19
|
+
Cooltrainer::DistorteD::IMPLANTATION(:LOWER_WORLD).each_with_object(
|
20
|
+
Hash.new { |pile, type| pile[type] = Hash[] }
|
21
|
+
) { |(key, types), pile|
|
22
|
+
types.each { |type, elements| pile.update(type => {key.molecule => elements}) { |k,o,n| o.merge(n) }}
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
# Returns a Hash[MediaMolecule] => Hash[CHECKING::YOU::OUT] => Hash[param_alias] => Compound
|
27
|
+
def outer_limits(all: false)
|
28
|
+
Cooltrainer::DistorteD::IMPLANTATION(
|
29
|
+
:OUTER_LIMITS,
|
30
|
+
(all || type_mars.empty?) ? Cooltrainer::DistorteD::media_molecules : type_mars.each_with_object(Set[]) { |type, molecules|
|
31
|
+
molecules.merge(lower_world[type].keys)
|
32
|
+
},
|
33
|
+
).each_with_object(Hash.new { |pile, type| pile[type] = Hash[] }) { |(key, types), pile|
|
34
|
+
types.each { |type, elements| pile.update(key.molecule => {type => elements}) { |k,o,n| o.merge(n) }}
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
# Filename without the dot-and-extension.
|
39
|
+
def basename
|
40
|
+
File.basename(@name, '.*')
|
41
|
+
end
|
42
|
+
|
43
|
+
# Returns a `::Set` of `::CHECKING::YOU::OUT` objects common to the source file and our supported MediaMolecules.
|
44
|
+
# Each of these Molecules will be plugged to the current instance.
|
45
|
+
def type_mars
|
46
|
+
# TODO: Get rid of the redundant `Set[…].flatten` here once I stabilize CYO API.
|
47
|
+
@type_mars ||= Set[::CHECKING::YOU::OUT(path)].flatten & lower_world.keys.to_set
|
48
|
+
raise MediaTypeNotImplementedError.new(@name) if @type_mars.empty?
|
49
|
+
@type_mars
|
50
|
+
end
|
51
|
+
|
52
|
+
# MediaMolecule file-type plugger.
|
53
|
+
# Any call to a ::CHECKING::YOU::OUT's distorted_method will end up here unless
|
54
|
+
# the Molecule that defines it has been `prepend`ed to our instance.
|
55
|
+
def method_missing(meth, *args, **kwargs, &block)
|
56
|
+
# Only consider method names with our prefixes.
|
57
|
+
if ::CHECKING::YOU::OUT::distorted_method_prefixes.values.map(&:to_s).include?(meth.to_s.split(::CHECKING::YOU::OUT::type_separators)[0])
|
58
|
+
# TODO: Might need to handle cases here where the Set[Molecule]
|
59
|
+
# exists but none of them defined our method.
|
60
|
+
unless self.singleton_class.instance_variable_get(:@media_molecules)
|
61
|
+
unless outer_limits.empty?
|
62
|
+
self.singleton_class.instance_variable_set(
|
63
|
+
:@media_molecules,
|
64
|
+
outer_limits.keys.each_with_object(Set[]) { |molecule, molecules|
|
65
|
+
self.singleton_class.prepend(molecule)
|
66
|
+
molecules.add(molecule)
|
67
|
+
}
|
68
|
+
)
|
69
|
+
# `return` to ensure we don't fall through to #method_missing:super
|
70
|
+
# if we are going to do any work, otherwise a NoMethodError will
|
71
|
+
# still be raised despite the distorted_method :sends suceeding.
|
72
|
+
#
|
73
|
+
# Use :__send__ in case a Molecule defines a `:send` method.
|
74
|
+
# https://ruby-doc.org/core/Object.html#method-i-send
|
75
|
+
return self.send(meth, *args, **kwargs, &block)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
# …and I still haven't found it! — What I'm looking for, that is.
|
80
|
+
# https://www.youtube.com/watch?v=xqse3vYcnaU
|
81
|
+
super
|
82
|
+
end
|
83
|
+
|
84
|
+
# Make sure :respond_to? works for yet-unplugged distorted_methods.
|
85
|
+
# http://blog.marc-andre.ca/2010/11/15/methodmissing-politely/
|
86
|
+
def respond_to_missing?(meth, *a)
|
87
|
+
# We can tell if a method looks like one of ours if it has at least 3 (maybe more!)
|
88
|
+
# underscore-separated components with a valid prefix as the first component
|
89
|
+
# and the media-type and sub-type as the rest, e.g.
|
90
|
+
#
|
91
|
+
# irb(main)> 'to_application_pdf'.split('_')
|
92
|
+
# => ["to", "application", "pdf"]
|
93
|
+
#
|
94
|
+
# irb(main)> ::CHECKING::YOU::OUT('.docx').first.distorted_file_method.to_s.split('_')
|
95
|
+
# => ["write", "application", "vnd", "openxmlformats", "officedocument", "wordprocessingml", "document"]
|
96
|
+
parts = meth.to_s.split(::CHECKING::YOU::OUT::type_separators)
|
97
|
+
::CHECKING::YOU::OUT::distorted_method_prefixes.values.map(&:to_s).include?(parts[0]) && parts.length > 2 || super(meth, *a)
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
@@ -0,0 +1,200 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
# Font metadata extraction
|
4
|
+
require 'ttfunk'
|
5
|
+
|
6
|
+
require 'distorted-floor/modular_technology/pango'
|
7
|
+
require 'distorted-floor/modular_technology/ttfunk'
|
8
|
+
require 'distorted-floor/modular_technology/vips/save'
|
9
|
+
require 'distorted-floor/checking_you_out'
|
10
|
+
using ::DistorteD::CHECKING::YOU::OUT
|
11
|
+
|
12
|
+
|
13
|
+
module Cooltrainer; end
|
14
|
+
module Cooltrainer::DistorteD; end
|
15
|
+
module Cooltrainer::DistorteD::Molecule; end
|
16
|
+
module Cooltrainer::DistorteD::Molecule::Font
|
17
|
+
|
18
|
+
|
19
|
+
# TODO: Test OTF, OTB, and others.
|
20
|
+
# NOTE: Traditional bitmap fonts won't be supported due to Pango 1.44
|
21
|
+
# and later switching to Harfbuzz from Freetype:
|
22
|
+
# https://gitlab.gnome.org/GNOME/pango/-/issues/386
|
23
|
+
# https://blogs.gnome.org/mclasen/2019/05/25/pango-future-directions/
|
24
|
+
LOWER_WORLD = {
|
25
|
+
::CHECKING::YOU::OUT::from_ietf_media_type('font/ttf') => nil,
|
26
|
+
}
|
27
|
+
OUTER_LIMITS = {
|
28
|
+
::CHECKING::YOU::OUT::from_ietf_media_type('font/ttf') => nil,
|
29
|
+
}
|
30
|
+
|
31
|
+
ATTRIBUTES = Set[
|
32
|
+
:alt,
|
33
|
+
]
|
34
|
+
ATTRIBUTES_VALUES = {
|
35
|
+
}
|
36
|
+
ATTRIBUTES_DEFAULT = {
|
37
|
+
}
|
38
|
+
|
39
|
+
|
40
|
+
# Maybe T0DO: Process output with TTFunk instead of only using it
|
41
|
+
# to generate images and metadata.
|
42
|
+
self::LOWER_WORLD.keys.each { |t|
|
43
|
+
define_method(t.distorted_file_method) { |dest_root, change|
|
44
|
+
copy_file(change.paths(dest_root).first)
|
45
|
+
}
|
46
|
+
}
|
47
|
+
|
48
|
+
include Cooltrainer::DistorteD::Technology::TTFunk
|
49
|
+
include Cooltrainer::DistorteD::Technology::Pango
|
50
|
+
include Cooltrainer::DistorteD::Technology::Vips::Save
|
51
|
+
|
52
|
+
|
53
|
+
# irb(main):089:0> chars.take(5)
|
54
|
+
# => [[1, 255], [2, 1], [3, 2], [4, 3], [5, 4]]
|
55
|
+
# irb(main):090:0> chars.values.take(5)
|
56
|
+
# => [255, 1, 2, 3, 4]
|
57
|
+
# irb(main):091:0> chars.values.map(&:chr).take(5)
|
58
|
+
# => ["\xFF", "\x01", "\x02", "\x03", "\x04"]
|
59
|
+
def to_pango(change)
|
60
|
+
output = '' << cr << '<span>' << cr
|
61
|
+
|
62
|
+
output << "<span size='35387'> #{font_name}</span>" << cr << cr
|
63
|
+
|
64
|
+
output << "<span size='24576'> #{font_description}</span>" << cr
|
65
|
+
output << "<span size='24576'> #{font_copyright}</span>" << cr
|
66
|
+
output << "<span size='24576'> #{font_version}</span>" << cr << cr
|
67
|
+
|
68
|
+
# Print a preview String in using the loaded font. Or don't.
|
69
|
+
if change.title
|
70
|
+
output << cr << cr << "<span size='24576' foreground='grey'> #{g_markup_escape_text(abstract(:title))}</span>" << cr << cr << cr
|
71
|
+
end
|
72
|
+
|
73
|
+
# /!\ MANDATORY READING /!\
|
74
|
+
# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6cmap.html
|
75
|
+
#
|
76
|
+
# "The 'cmap' table maps character codes to glyph indices.
|
77
|
+
# The choice of encoding for a particular font is dependent upon the conventions
|
78
|
+
# used by the intended platform. A font intended to run on multiple platforms
|
79
|
+
# with different encoding conventions will require multiple encoding tables.
|
80
|
+
# As a result, the 'cmap' table may contain multiple subtables,
|
81
|
+
# one for each supported encoding scheme."
|
82
|
+
#
|
83
|
+
# Cmap#unicode is a convenient shortcut to sorting the subtables
|
84
|
+
# and removing any unusable ones:
|
85
|
+
# https://github.com/prawnpdf/ttfunk/blob/master/lib/ttfunk/table/cmap.rb
|
86
|
+
#
|
87
|
+
# irb(main):174:0> font_meta.cmap.tables.count
|
88
|
+
# => 3
|
89
|
+
# irb(main):175:0> font_meta.cmap.unicode.count
|
90
|
+
# => 2
|
91
|
+
to_ttfunk.cmap.tables.each do |table|
|
92
|
+
next if !table.unicode?
|
93
|
+
# Each subtable's `code_map` is a Hash map of character codes (the Hash keys)
|
94
|
+
# to the glyph IDs from the original font (the Hash's values).
|
95
|
+
#
|
96
|
+
# Subtable::encode takes:
|
97
|
+
# - a Hash mapping character codes to original font glyph IDs.
|
98
|
+
# - the desired output encoding — Set[:mac_roman, :unicode, :unicode_ucs4]
|
99
|
+
# https://github.com/prawnpdf/ttfunk/blob/master/lib/ttfunk/table/cmap/subtable.rb
|
100
|
+
# …and returns a Hash with keys:
|
101
|
+
# - :charmap — Hash mapping the characters in the input charmap
|
102
|
+
# to a another hash containing both the `:old`
|
103
|
+
# and `:new` glyph ids for each character code.
|
104
|
+
# - :subtable — String encoded subtable for the given encoding.
|
105
|
+
encoded = TTFunk::Table::Cmap::Subtable::encode(table&.code_map, :unicode).dig(:charmap)
|
106
|
+
|
107
|
+
output << "<span size='49152'>"
|
108
|
+
|
109
|
+
i = 0
|
110
|
+
encoded.each_pair { |c, (old, new)|
|
111
|
+
|
112
|
+
begin
|
113
|
+
if glyph = to_ttfunk.glyph_outlines.for(c)
|
114
|
+
# Add a space on either side of the character so they aren't
|
115
|
+
# all smooshed up against each other and unreadable.
|
116
|
+
output << ' ' << g_markup_escape_char(c) << ' '
|
117
|
+
if i >= 15
|
118
|
+
output << cr
|
119
|
+
i = 0
|
120
|
+
else
|
121
|
+
i = i + 1
|
122
|
+
end
|
123
|
+
else
|
124
|
+
end
|
125
|
+
rescue NoMethodError => nme
|
126
|
+
# TTFunk's `glyph_outlines.for()` will raise this if we call it
|
127
|
+
# for a codepoint that does not exist in the font, which we will
|
128
|
+
# not do because we are enumerating the codepoints in the font,
|
129
|
+
# but we should still handle the possibility.
|
130
|
+
# irb(main):060:0> font.glyph_outlines.for(555555)
|
131
|
+
#
|
132
|
+
# Traceback (most recent call last):
|
133
|
+
# 6: from /usr/bin/irb:23:in `<main>'
|
134
|
+
# 5: from /usr/bin/irb:23:in `load'
|
135
|
+
# 4: from /home/okeeblow/.gems/gems/irb-1.2.4/exe/irb:11:in `<top (required)>'
|
136
|
+
# 3: from (irb):60
|
137
|
+
# 2: from /home/okeeblow/.gems/gems/ttfunk-1.6.2.1/lib/ttfunk/table/glyf.rb:35:in `for'
|
138
|
+
# 1: from /home/okeeblow/.gems/gems/ttfunk-1.6.2.1/lib/ttfunk/table/loca.rb:35:in `size_of'
|
139
|
+
# NoMethodError (undefined method `-' for nil:NilClass)
|
140
|
+
end
|
141
|
+
}
|
142
|
+
|
143
|
+
output << '</span>' << cr
|
144
|
+
end
|
145
|
+
|
146
|
+
output << '</span>'
|
147
|
+
output
|
148
|
+
end
|
149
|
+
|
150
|
+
# Return the `src` as the font_path since we aren't using
|
151
|
+
# any of the built-in fonts.
|
152
|
+
def font_path
|
153
|
+
path
|
154
|
+
end
|
155
|
+
|
156
|
+
def to_vips_image(change)
|
157
|
+
# https://libvips.github.io/libvips/API/current/libvips-create.html#vips-text
|
158
|
+
Vips::Image.text(
|
159
|
+
# This string must be well-escaped Pango Markup:
|
160
|
+
# https://developer.gnome.org/pango/stable/pango-Markup.html
|
161
|
+
# However the official function for escaping text is
|
162
|
+
# not implemented in Ruby GLib, so we have to do it ourselves.
|
163
|
+
to_pango(change),
|
164
|
+
**{
|
165
|
+
# String absolute path to TTF
|
166
|
+
:fontfile => font_path,
|
167
|
+
# It's not enough to just specify the TTF path;
|
168
|
+
# we must also specify a font family, subfamily, and size.
|
169
|
+
:font => "#{font_name}",
|
170
|
+
# Space between lines (in Points).
|
171
|
+
:spacing => to_ttfunk.line_gap,
|
172
|
+
# Requires libvips 8.8
|
173
|
+
:justify => false,
|
174
|
+
:dpi => change.dpi&.to_i,
|
175
|
+
},
|
176
|
+
)
|
177
|
+
end
|
178
|
+
|
179
|
+
end # Font
|
180
|
+
|
181
|
+
|
182
|
+
# Notes on file-format specifics and software-library-specifics
|
183
|
+
#
|
184
|
+
# # TTF (via TTFunk)
|
185
|
+
#
|
186
|
+
# ## Cmap
|
187
|
+
#
|
188
|
+
# Each TTFunk::Table::Cmap::Format<whatever> class responds to `:supported?`
|
189
|
+
# with its own internal boolean telling us if that Format is usable in TTFunk.
|
190
|
+
# This has nothing to do with any font file itself, just the library code.
|
191
|
+
# irb(main)> font.cmap.tables.map{|t| t.supported?}
|
192
|
+
# => [true, true, true]
|
193
|
+
#
|
194
|
+
# Any subclass of TTFunk::Table::Cmap::Subtable responds to `:unicode?`
|
195
|
+
# with a boolean calculated from the instance `@platform_id` and `@encoding_id`,
|
196
|
+
# and those numeric IDs are assigned to the symbolic (e.g. `:macroman`) names in:
|
197
|
+
# https://github.com/prawnpdf/ttfunk/blob/master/lib/ttfunk/table/cmap/subtable.rb
|
198
|
+
# irb(main)> font.cmap.tables.map{|t| t.unicode?}
|
199
|
+
# => [true, false, true]
|
200
|
+
#
|
@@ -0,0 +1,33 @@
|
|
1
|
+
|
2
|
+
require 'set'
|
3
|
+
|
4
|
+
require 'distorted-floor/checking_you_out'
|
5
|
+
require 'distorted-floor/modular_technology/vips'
|
6
|
+
|
7
|
+
|
8
|
+
module Cooltrainer; end
|
9
|
+
module Cooltrainer::DistorteD; end
|
10
|
+
module Cooltrainer::DistorteD::Molecule; end
|
11
|
+
module Cooltrainer::DistorteD::Molecule::Image
|
12
|
+
|
13
|
+
|
14
|
+
# Attributes for our <picture>/<img>.
|
15
|
+
# Automatically enabled as attrs for DD Liquid Tag.
|
16
|
+
# https://developer.mozilla.org/en-US/docs/Web/HTML/Element/picture#Attributes
|
17
|
+
# https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#Attributes
|
18
|
+
# https://developer.mozilla.org/en-US/docs/Web/Performance/Lazy_loading
|
19
|
+
ATTRIBUTES = Set[:alt, :caption, :href, :loading]
|
20
|
+
|
21
|
+
# Defaults for HTML Element attributes.
|
22
|
+
# Not every attr has to be listed here.
|
23
|
+
# Many need no default and just won't render.
|
24
|
+
ATTRIBUTES_DEFAULT = {
|
25
|
+
:loading => :eager,
|
26
|
+
}
|
27
|
+
ATTRIBUTES_VALUES = {
|
28
|
+
:loading => Set[:eager, :lazy],
|
29
|
+
}
|
30
|
+
|
31
|
+
include Cooltrainer::DistorteD::Technology::Vips
|
32
|
+
|
33
|
+
end # Image
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
require 'hexapdf'
|
4
|
+
|
5
|
+
require 'distorted-floor/checking_you_out'
|
6
|
+
using ::DistorteD::CHECKING::YOU::OUT
|
7
|
+
|
8
|
+
|
9
|
+
module Cooltrainer; end
|
10
|
+
module Cooltrainer::DistorteD; end
|
11
|
+
module Cooltrainer::DistorteD::Molecule; end
|
12
|
+
module Cooltrainer::DistorteD::Molecule::PDF
|
13
|
+
|
14
|
+
|
15
|
+
# https://hexapdf.gettalong.org/documentation/reference/api/HexaPDF/Document/index.html#method-c-new
|
16
|
+
# https://hexapdf.gettalong.org/documentation/reference/api/HexaPDF/index.html#DefaultDocumentConfiguration
|
17
|
+
# https://hexapdf.gettalong.org/documentation/reference/api/HexaPDF/Task/Optimize.html
|
18
|
+
PDF_TYPE = ::CHECKING::YOU::OUT::from_ietf_media_type('application/pdf')
|
19
|
+
LOWER_WORLD = Hash[
|
20
|
+
PDF_TYPE => nil,
|
21
|
+
]
|
22
|
+
OUTER_LIMITS = Hash[
|
23
|
+
PDF_TYPE => nil,
|
24
|
+
]
|
25
|
+
|
26
|
+
# TODO: Use MuPDF instead of libvips magick-based PDF loader.
|
27
|
+
|
28
|
+
def self.optimize(src, dest)
|
29
|
+
HexaPDF::Document.open(src) do |doc|
|
30
|
+
doc.task(
|
31
|
+
:optimize,
|
32
|
+
compact: true,
|
33
|
+
object_streams: :generate,
|
34
|
+
xref_streams: :generate,
|
35
|
+
compress_pages: false,
|
36
|
+
)
|
37
|
+
doc.write(dest)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
define_method(PDF_TYPE.distorted_file_method) { |dest_root, change|
|
42
|
+
copy_file(change.paths(dest_root).first)
|
43
|
+
}
|
44
|
+
|
45
|
+
end # PDF
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
require 'svg_optimizer' # https://github.com/fnando/svg_optimizer
|
4
|
+
|
5
|
+
require 'distorted-floor/checking_you_out'
|
6
|
+
using ::DistorteD::CHECKING::YOU::OUT
|
7
|
+
require 'distorted-floor/modular_technology/vips/save'
|
8
|
+
|
9
|
+
|
10
|
+
module Cooltrainer; end
|
11
|
+
module Cooltrainer::DistorteD; end
|
12
|
+
module Cooltrainer::DistorteD::Molecule; end
|
13
|
+
module Cooltrainer::DistorteD::Molecule::SVG
|
14
|
+
|
15
|
+
include Cooltrainer::DistorteD::Technology::Vips::Save
|
16
|
+
|
17
|
+
SVG_TYPE = ::CHECKING::YOU::OUT::from_ietf_media_type('image/svg+xml')
|
18
|
+
|
19
|
+
LOWER_WORLD = Hash[
|
20
|
+
SVG_TYPE => Cooltrainer::DistorteD::Technology::Vips::VipsType::loader_for(SVG_TYPE).map(&:options).reduce(&:merge)
|
21
|
+
].merge(Hash[
|
22
|
+
:optimize => Cooltrainer::Compound.new(:optimize, valid: Cooltrainer::BOOLEAN_VALUES, default: false, blurb: 'SvgOptimizer'),
|
23
|
+
])
|
24
|
+
|
25
|
+
# WISHLIST: Support VML for old IE compatibility.
|
26
|
+
# Example: RaphaëlJS — https://en.wikipedia.org/wiki/Rapha%C3%ABl_(JavaScript_library)
|
27
|
+
OUTER_LIMITS = Hash[
|
28
|
+
SVG_TYPE => nil,
|
29
|
+
]
|
30
|
+
|
31
|
+
def to_vips_image(change = nil)
|
32
|
+
# NOTE: libvips 8.9 added the `unlimited` argument to svgload.
|
33
|
+
# Loading SVGs >= 10MiB in size will fail on older libvips.
|
34
|
+
# https://github.com/libvips/libvips/commit/55e49831b801e05ddd974b1e2102fda7956c53f5
|
35
|
+
@vips_image ||= Vips::Image.new_from_file(path)
|
36
|
+
end
|
37
|
+
|
38
|
+
define_method(SVG_TYPE.distorted_file_method) { |dest_root, change|
|
39
|
+
if change.optimize
|
40
|
+
SvgOptimizer.optimize_file(path, change.paths(dest_root).first, SvgOptimizer::DEFAULT_PLUGINS)
|
41
|
+
else
|
42
|
+
copy_file(change.paths(dest_root).first)
|
43
|
+
end
|
44
|
+
}
|
45
|
+
|
46
|
+
end
|