distorted 0.5.7 → 0.6.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.
@@ -0,0 +1,36 @@
1
+
2
+ require 'set'
3
+
4
+ require 'distorted/checking_you_out'
5
+ require 'distorted/modular_technology/vips'
6
+ require 'distorted/injection_of_love'
7
+
8
+
9
+ module Cooltrainer
10
+ module DistorteD
11
+ module 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
+ include Cooltrainer::DistorteD::InjectionOfLove
33
+
34
+ end # Image
35
+ end # DistorteD
36
+ end # Cooltrainer
@@ -1,17 +1,20 @@
1
1
  require 'set'
2
2
 
3
3
  require 'hexapdf'
4
- require 'mime/types'
4
+
5
+ require 'distorted/checking_you_out'
6
+ require 'distorted/injection_of_love'
7
+ require 'distorted/molecule/C18H27NO3'
5
8
 
6
9
 
7
10
  module Cooltrainer
8
11
  module DistorteD
9
- class PDF
12
+ module PDF
13
+
10
14
 
11
- MEDIA_TYPE = 'application'.freeze
12
- SUB_TYPE = 'pdf'.freeze
15
+ include Cooltrainer::DistorteD::Molecule::C18H27NO3
13
16
 
14
- MIME_TYPES = MIME::Types["#{MEDIA_TYPE}/#{SUB_TYPE}"].to_set
17
+ LOWER_WORLD = CHECKING::YOU::IN("application/pdf")
15
18
 
16
19
  # https://developer.mozilla.org/en-US/docs/Web/HTML/Element/object#Attributes
17
20
  # https://www.adobe.com/content/dam/acom/en/devnet/acrobat/pdfs/pdf_open_parameters.pdf
@@ -37,7 +40,7 @@ module Cooltrainer
37
40
  :highlight,
38
41
  :fdf,
39
42
  ]
40
- ATTRS = Set[
43
+ ATTRIBUTES = Set[
41
44
  :alt,
42
45
  :caption,
43
46
  :height, #<object> viewer container height.
@@ -50,9 +53,8 @@ module Cooltrainer
50
53
 
51
54
  FLOAT_INT_FRAGMENT = '[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)'.freeze
52
55
  ZERO_TO_ONE_HUNDRED = /^(([1-9]\d?|1\d{1})([.,]\d{0,1})?|100([.,]0{1})?)$/
53
- BOOLEAN_SET = Set[0, 1, false, true, '0'.freeze, '1'.freeze, 'false'.freeze, 'true'.freeze]
54
56
 
55
- ATTRS_DEFAULT = {
57
+ ATTRIBUTES_DEFAULT = {
56
58
  :height => '100%'.freeze,
57
59
  :width => '100%'.freeze,
58
60
  # BEGIN PDF Open Parameters
@@ -73,7 +75,7 @@ module Cooltrainer
73
75
  # …but then goes on to show some examples (like `comment`)
74
76
  # that are clearly longer than 32 characters.
75
77
  # Dunno. I'll err on the side of giving you a footgun.
76
- ATTRS_VALUES = {
78
+ ATTRIBUTES_VALUES = {
77
79
  :nameddest => /^#{RESERVED_CHARACTERS_FRAGMENT}$/,
78
80
  :page => /\d/,
79
81
  :comment => /^#{RESERVED_CHARACTERS_FRAGMENT}$/,
@@ -82,15 +84,18 @@ module Cooltrainer
82
84
  :view => /^Fit(H|V|B|BH|BV(,#{FLOAT_INT_FRAGMENT})?)?$/,
83
85
  :viewrect => /^#{FLOAT_INT_FRAGMENT},#{FLOAT_INT_FRAGMENT},#{FLOAT_INT_FRAGMENT},#{FLOAT_INT_FRAGMENT}$/,
84
86
  :pagemode => Set[:none, :thumbs, :bookmarks],
85
- :scrollbar => BOOLEAN_SET,
87
+ :scrollbar => BOOLEAN_ATTR_VALUES,
86
88
  :search => /^#{RESERVED_CHARACTERS_FRAGMENT}(,\s#{RESERVED_CHARACTERS_FRAGMENT})*$/,
87
- :toolbar => BOOLEAN_SET,
88
- :statusbar => BOOLEAN_SET,
89
- :messages => BOOLEAN_SET,
90
- :navpanes => BOOLEAN_SET,
89
+ :toolbar => BOOLEAN_ATTR_VALUES,
90
+ :statusbar => BOOLEAN_ATTR_VALUES,
91
+ :messages => BOOLEAN_ATTR_VALUES,
92
+ :navpanes => BOOLEAN_ATTR_VALUES,
91
93
  :fdf => /^#{RESERVED_CHARACTERS_FRAGMENT}$/,
92
94
  }
93
95
 
96
+ include Cooltrainer::DistorteD::InjectionOfLove
97
+
98
+ # TODO: Use MuPDF instead of libvips magick-based PDF loader.
94
99
 
95
100
  def self.optimize(src, dest)
96
101
  HexaPDF::Document.open(src) do |doc|
@@ -105,6 +110,10 @@ module Cooltrainer
105
110
  end
106
111
  end
107
112
 
113
+ def to_application_pdf(*a, **k, &b)
114
+ copy_file(*a, **k, &b)
115
+ end
116
+
108
117
  end # PDF
109
118
  end # DistorteD
110
119
  end # Cooltrainer
@@ -0,0 +1,60 @@
1
+ require 'set'
2
+
3
+ require 'svg_optimizer'
4
+
5
+ require 'distorted/checking_you_out'
6
+ require 'distorted/injection_of_love'
7
+ require 'distorted/molecule/C18H27NO3'
8
+
9
+
10
+ module Cooltrainer
11
+ module DistorteD
12
+ module SVG
13
+
14
+ SUB_TYPE = 'svg'.freeze
15
+ include Cooltrainer::DistorteD::Molecule::C18H27NO3
16
+
17
+ #WISHLIST: Support VML for old IE compatibility.
18
+ # Example: RaphaëlJS — https://en.wikipedia.org/wiki/Rapha%C3%ABl_(JavaScript_library)
19
+ LOWER_WORLD = CHECKING::YOU::IN(/^image\/svg/)
20
+
21
+ ATTRIBUTES = Set[
22
+ :alt,
23
+ :caption,
24
+ :href,
25
+ :loading,
26
+ :optimize,
27
+ ]
28
+ ATTRIBUTES_VALUES = {
29
+ :optimize => BOOLEAN_ATTR_VALUES,
30
+ }
31
+ ATTRIBUTES_DEFAULT = {
32
+ :optimize => false,
33
+ }
34
+
35
+ include Cooltrainer::DistorteD::Technology::VipsSave
36
+ include Cooltrainer::DistorteD::InjectionOfLove
37
+
38
+ def to_vips_image
39
+ # TODO: Load-time options for various formats, like SVG's `unlimited`:
40
+ # "SVGs larger than 10MB are normally blocked for security. Set unlimited to allow SVGs of any size."
41
+ # https://libvips.github.io/libvips/API/current/VipsForeignSave.html#vips-svgload
42
+ @vips_image ||= Vips::Image.new_from_file(path)
43
+ end
44
+
45
+ def to_image_svg_xml(dest, *a, **k, &b)
46
+ if abstract(:optimize)
47
+ SvgOptimizer.optimize_file(path, dest, SvgOptimizer::DEFAULT_PLUGINS)
48
+ else
49
+ copy_file(dest, *a, **k, &b)
50
+ end
51
+ end
52
+
53
+ def self.optimize(src, dest)
54
+ # TODO: Make optimizations/plugins configurable
55
+ SvgOptimizer.optimize_file(src, dest, SvgOptimizer::DEFAULT_PLUGINS)
56
+ end
57
+
58
+ end
59
+ end
60
+ end
@@ -1,45 +1,24 @@
1
1
  require 'set'
2
2
 
3
- require 'ttfunk' # Font metadata extraction
4
3
  require 'charlock_holmes' # Text file charset detection
5
4
 
6
5
  require 'distorted/monkey_business/string' # String#map
7
6
  require 'distorted/modular_technology/pango'
7
+ require 'distorted/modular_technology/ttfunk'
8
+ require 'distorted/modular_technology/vips_save'
8
9
 
9
- require 'distorted/image'
10
-
11
- require 'mime/types'
12
-
13
- # No need to do all the fancy library versioning in a subclass.
14
- require 'vips'
10
+ require 'distorted/checking_you_out'
11
+ require 'distorted/injection_of_love'
12
+ require 'distorted/molecule/image'
15
13
 
16
14
 
17
15
  module Cooltrainer
18
16
  module DistorteD
19
- class Text < Image
20
-
21
- include Cooltrainer::DistorteD::Tech::Pango;
22
-
17
+ module Text
23
18
 
24
- MEDIA_TYPE = 'text'.freeze
25
19
 
26
- MIME_TYPES = MIME::Types[/^#{self::MEDIA_TYPE}\/(plain|x-nfo)/, :complete => true].to_set
27
-
28
- ATTRS = Set[
29
- :alt,
30
- :crop,
31
- :font,
32
- :encoding,
33
- :spacing,
34
- :dpi,
35
- ]
36
- ATTRS_VALUES = {
37
- :spacing => Set[:monospace, :proportional],
38
- }
39
- ATTRS_DEFAULT = {
40
- :crop => :none,
41
- :dpi => 144,
42
- }
20
+ LOWER_WORLD = CHECKING::YOU::IN(/^text\/(plain|x-nfo)/)
21
+ OUTER_LIMITS = CHECKING::YOU::IN(/^text\/(plain|x-nfo)/)
43
22
 
44
23
  # Track supported fonts by codepage.
45
24
  # Avoid renaming these from the original archives / websites.
@@ -91,17 +70,45 @@ module Cooltrainer
91
70
  ],
92
71
  }
93
72
  # …as well as the inverse, the numeric codepage for each font:
94
- FONT_CODEPAGE = CODEPAGE_FONT.reduce(Hash.new([])) { |memo, (key, values)|
73
+ FONT_CODEPAGE = self::CODEPAGE_FONT.reduce(Hash.new([])) { |memo, (key, values)|
95
74
  values.each { |value| memo[value] = key }
96
75
  memo
97
76
  }
98
77
 
78
+ self::OUTER_LIMITS.each { |t|
79
+ define_method(t.distorted_method) { |*a, **k, &b|
80
+ copy_file(*a, **k, &b)
81
+ }
82
+ }
83
+
84
+ ATTRIBUTES = Set[
85
+ :alt,
86
+ :crop,
87
+ :font,
88
+ :encoding,
89
+ :spacing,
90
+ :dpi,
91
+ ]
92
+ ATTRIBUTES_VALUES = {
93
+ :spacing => Set[:monospace, :proportional],
94
+ :font => self::FONT_FILENAME.keys.to_set,
95
+ }
96
+ ATTRIBUTES_DEFAULT = {
97
+ :crop => :none,
98
+ :dpi => 144,
99
+ :encoding => 'UTF-8'.freeze
100
+ }
101
+
102
+ include Cooltrainer::DistorteD::Technology::TTFunk
103
+ include Cooltrainer::DistorteD::Technology::Pango
104
+ include Cooltrainer::DistorteD::Technology::VipsSave
105
+ include Cooltrainer::DistorteD::InjectionOfLove
99
106
 
100
107
  # Using a numeric key for things for simplicity.
101
108
  # TODO: Replace this with Ruby's built-in Encoding class after I have
102
109
  # a better idea what I want to do.
103
110
  def codepage
104
- case @encoding
111
+ case text_file_encoding
105
112
  when 'UTF-8'.freeze then 65001
106
113
  when 'Shift_JIS'.freeze then 932
107
114
  when 'IBM437'.freeze then 437
@@ -112,35 +119,48 @@ module Cooltrainer
112
119
  # Return a Pango Markup escaped version of the document.
113
120
  def to_pango
114
121
  # https://developer.gnome.org/glib/stable/glib-Simple-XML-Subset-Parser.html#g-markup-escape-text
115
- escaped = @contents.map{ |c|
122
+ escaped = text_file_utf8_content.map{ |c|
116
123
  g_markup_escape_char(c)
117
124
  }
118
- if spacing == :monospace
125
+ if font_spacing == :monospace
119
126
  "<tt>" << escaped << "</tt>"
120
127
  else
121
128
  escaped
122
129
  end
123
130
  end
124
131
 
125
- def initialize(src, encoding: nil, font: nil, spacing: nil, dpi: ATTRS_DEFAULT[:dpi])
126
- @src = src
127
- @liquid_spacing = spacing
132
+ protected
128
133
 
134
+ def text_file_content
129
135
  # VIPS makes us provide the text content as a single variable,
130
136
  # so we may as well just one-shot File.read() it into memory.
131
137
  # https://kunststube.net/encoding/
132
- contents = File.read(@src)
138
+ @text_file_content ||= File.read(path)
139
+ end
140
+
141
+ def text_file_utf8_content
142
+ CharlockHolmes::Converter.convert(text_file_content, text_file_encoding, 'UTF-8'.freeze)
143
+ end
133
144
 
145
+ def text_file_encoding
134
146
  # It's not easy or even possible in some cases to tell the "true" codepage
135
147
  # we should use for any given text document, but using character detection
136
148
  # is worth a shot if the user gave us nothing.
137
- detected = CharlockHolmes::EncodingDetector.detect(contents)
138
- @encoding = (encoding || detected[:encoding] || 'UTF-8'.freeze).to_s
139
- @contents = CharlockHolmes::Converter.convert(contents, @encoding, 'UTF-8'.freeze)
149
+ #
150
+ # TODO: Figure out if/how we can get IBM437 files to not be detected as ISO-8859-1
151
+ @text_file_encoding ||= (
152
+ abstract(:encoding).to_s ||
153
+ CharlockHolmes::EncodingDetector.detect(text_file_content)[:encoding] ||
154
+ 'UTF-8'.freeze
155
+ ).to_s
156
+ end
140
157
 
141
- # Set the shorthand symbol key for our chosen font.
142
- @font = font&.to_sym || self.singleton_class.const_get(:CODEPAGE_FONT)[codepage].first
158
+ def vips_font
159
+ # Set the shorthand Symbol key for our chosen font.
160
+ return abstract(:font)&.to_sym || CODEPAGE_FONT[codepage].first
161
+ end
143
162
 
163
+ def to_vips_image
144
164
  # Load font metadata directly from the file so we don't have to
145
165
  # duplicate it here to feed to Vips/Pango.
146
166
  #
@@ -154,10 +174,9 @@ module Cooltrainer
154
174
  # => "PerfectDOSVGA437"
155
175
  # irb(main)> font_meta.line_gap
156
176
  # => 0
157
- @font_meta = TTFunk::File.open(font_path)
158
177
 
159
178
  # https://libvips.github.io/libvips/API/current/libvips-create.html#vips-text
160
- @image = Vips::Image.text(
179
+ Vips::Image.text(
161
180
  # This string must be well-escaped Pango Markup:
162
181
  # https://developer.gnome.org/pango/stable/pango-Markup.html
163
182
  # However the official function for escaping text is
@@ -170,23 +189,22 @@ module Cooltrainer
170
189
  # we must also specify a font family, subfamily, and size.
171
190
  :font => "#{font_name} 16",
172
191
  # Space between lines (in Points).
173
- :spacing => @font_meta.line_gap,
192
+ :spacing => to_ttfunk.line_gap,
174
193
  :justify => true, # Requires libvips 8.8
175
- :dpi => dpi.to_i,
194
+ :dpi => abstract(:dpi)&.to_i,
176
195
  },
177
196
  )
178
197
  end
179
198
 
180
- protected
181
-
182
199
  # Return the String absolute path to the TTF file
183
200
  def font_path
184
201
  File.join(
185
- File.dirname(__FILE__), # distorted
202
+ File.dirname(__FILE__), # molecule
203
+ '..'.freeze, # distorted
186
204
  '..'.freeze, # lib
187
205
  '..'.freeze, # DistorteD-Ruby
188
206
  'font'.freeze,
189
- font_codepage,
207
+ font_codepage.to_s,
190
208
  font_filename,
191
209
  )
192
210
  end
@@ -194,46 +212,12 @@ module Cooltrainer
194
212
  # Returns the numeric representation of the codepage
195
213
  # covered by our font.
196
214
  def font_codepage
197
- self.singleton_class.const_get(:FONT_CODEPAGE)&.dig(@font).to_s
215
+ FONT_CODEPAGE.dig(vips_font).to_s
198
216
  end
199
217
 
200
218
  # Returns the basename (with file extension) of our font.
201
219
  def font_filename
202
- self.singleton_class.const_get(:FONT_FILENAME)&.dig(@font)
203
- end
204
-
205
- # Returns a boolean for whether or not this font is monospaced.
206
- # true == monospace
207
- # false == proportional
208
- def spacing
209
- # Monospace fonts will (read: should) have the same width
210
- # for every glyph, so we can tell a monospace font by
211
- # checking if a deduplicated widths table has size == 1:
212
- # irb(main)> font.horizontal_metrics.widths.count
213
- # => 256
214
- # irb(main)> font.horizontal_metrics.widths.uniq.compact.length
215
- # => 1
216
- @font_meta.horizontal_metrics.widths.uniq.compact.length == 1 ? :monospace : :proportional
217
- end
218
-
219
- # Returns the Family and Subfamily as one string suitable for libvips
220
- def font_name
221
- "#{@font_meta.name.font_family.first.encode('UTF-8')} #{@font_meta.name.font_subfamily.first.encode('UTF-8')}"
222
- end
223
-
224
- # Returns the Pango-Markup-encoded UTF-8 String version + revision of the font
225
- def font_version
226
- g_markup_escape_text(@font_meta.name&.version&.first&.encode('UTF-8').to_s)
227
- end
228
-
229
- # Returns the Pango-Markup-encoded UTF-8 String font file description
230
- def font_description
231
- g_markup_escape_text(@font_meta.name&.description&.first&.encode('UTF-8').to_s)
232
- end
233
-
234
- # Returns the Pango-Markup-encoded UTF-8 String copyright information of the font
235
- def font_copyright
236
- g_markup_escape_text(@font_meta.name&.copyright&.first&.encode('UTF-8').to_s)
220
+ FONT_FILENAME.dig(vips_font)
237
221
  end
238
222
 
239
223
  end # Text
@@ -17,25 +17,27 @@ end
17
17
 
18
18
  require 'set'
19
19
 
20
- require 'mime/types'
20
+ require 'distorted/checking_you_out'
21
+ require 'distorted/injection_of_love'
22
+
21
23
 
22
24
  module Cooltrainer
23
25
  module DistorteD
24
- class Video
26
+ module Video
25
27
 
26
- MEDIA_TYPE = 'video'.freeze
27
- MIME_TYPES = MIME::Types[/^#{MEDIA_TYPE}/, :complete => true].to_set
28
+ LOWER_WORLD = CHECKING::YOU::IN(/^video\/mp4/)
28
29
 
29
30
  # Attributes for our <video>.
30
31
  # Automatically enabled as attrs for DD Liquid Tag.
31
32
  # https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video#Attributes
32
- ATTRS = Set[:caption]
33
+ ATTRIBUTES = Set[:caption]
33
34
 
34
35
  # Defaults for HTML Element attributes.
35
36
  # Not every attr has to be listed here.
36
37
  # Many need no default and just won't render.
37
- ATTRS_DEFAULT = {}
38
- ATTRS_VALUES = {}
38
+ ATTRIBUTES_DEFAULT = {}
39
+ ATTRIBUTES_VALUES = {}
40
+ include Cooltrainer::DistorteD::InjectionOfLove
39
41
 
40
42
  attr_accessor :dest
41
43