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.
Files changed (61) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +661 -0
  3. data/README.md +32 -0
  4. data/bin/distorted-floor +16 -0
  5. data/bin/repl +14 -0
  6. data/bin/setup +8 -0
  7. data/font/1252/LICENSE/MoreLessPerfectDOSVGA437/img/Less_Perfect_DOS_VGA.png +0 -0
  8. data/font/1252/LICENSE/MoreLessPerfectDOSVGA437/img/More_Perfect_DOS_VGA.png +0 -0
  9. data/font/1252/LICENSE/MoreLessPerfectDOSVGA437/img/Perfect_DOS_VGA.png +0 -0
  10. data/font/1252/LICENSE/MoreLessPerfectDOSVGA437/less_more_perfect_dos_vga_437.html +52 -0
  11. data/font/1252/LICENSE/PerfectDOSVGA437/font-comment.php@file=perfect_dos_vga_437.html +5 -0
  12. data/font/1252/LessPerfectDOSVGA.ttf +0 -0
  13. data/font/1252/MorePerfectDOSVGA.ttf +0 -0
  14. data/font/1252/Perfect DOS VGA 437 Win.ttf +0 -0
  15. data/font/437/Perfect DOS VGA 437.ttf +0 -0
  16. data/font/437/dos437.txt +72 -0
  17. data/font/65001/Anonymous Pro B.ttf +0 -0
  18. data/font/65001/Anonymous Pro BI.ttf +0 -0
  19. data/font/65001/Anonymous Pro I.ttf +0 -0
  20. data/font/65001/Anonymous Pro.ttf +0 -0
  21. data/font/65001/LICENSE/AnonymousPro/FONTLOG.txt +45 -0
  22. data/font/65001/LICENSE/AnonymousPro/OFL-FAQ.txt +235 -0
  23. data/font/65001/LICENSE/AnonymousPro/OFL.txt +94 -0
  24. data/font/65001/LICENSE/AnonymousPro/README.txt +55 -0
  25. data/font/850/ProFont-Bold-01/LICENSE +22 -0
  26. data/font/850/ProFont-Bold-01/readme.txt +28 -0
  27. data/font/850/ProFontWindows-Bold.ttf +0 -0
  28. data/font/850/ProFontWindows.ttf +0 -0
  29. data/font/850/Profont/LICENSE +22 -0
  30. data/font/850/Profont/readme.txt +31 -0
  31. data/font/932/LICENSE/README-ttf.txt +213 -0
  32. data/font/932/mona.ttf +0 -0
  33. data/lib/distorted-floor/checking_you_out.rb +78 -0
  34. data/lib/distorted-floor/click_again.rb +406 -0
  35. data/lib/distorted-floor/element_of_media/change.rb +114 -0
  36. data/lib/distorted-floor/element_of_media/compound.rb +120 -0
  37. data/lib/distorted-floor/element_of_media.rb +2 -0
  38. data/lib/distorted-floor/error_code.rb +55 -0
  39. data/lib/distorted-floor/floor.rb +17 -0
  40. data/lib/distorted-floor/invoker.rb +100 -0
  41. data/lib/distorted-floor/media_molecule/font.rb +200 -0
  42. data/lib/distorted-floor/media_molecule/image.rb +33 -0
  43. data/lib/distorted-floor/media_molecule/pdf.rb +45 -0
  44. data/lib/distorted-floor/media_molecule/svg.rb +46 -0
  45. data/lib/distorted-floor/media_molecule/text.rb +247 -0
  46. data/lib/distorted-floor/media_molecule/video.rb +21 -0
  47. data/lib/distorted-floor/media_molecule.rb +58 -0
  48. data/lib/distorted-floor/modular_technology/gstreamer.rb +175 -0
  49. data/lib/distorted-floor/modular_technology/pango.rb +90 -0
  50. data/lib/distorted-floor/modular_technology/ttfunk.rb +48 -0
  51. data/lib/distorted-floor/modular_technology/vips/ffi.rb +66 -0
  52. data/lib/distorted-floor/modular_technology/vips/load.rb +174 -0
  53. data/lib/distorted-floor/modular_technology/vips/operatio$.rb +268 -0
  54. data/lib/distorted-floor/modular_technology/vips/save.rb +135 -0
  55. data/lib/distorted-floor/modular_technology/vips.rb +17 -0
  56. data/lib/distorted-floor/monkey_business/encoding.rb +374 -0
  57. data/lib/distorted-floor/monkey_business/hash.rb +18 -0
  58. data/lib/distorted-floor/monkey_business/set.rb +15 -0
  59. data/lib/distorted-floor/monkey_business/string.rb +6 -0
  60. data/lib/distorted-floor.rb +2 -0
  61. 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