distorted 0.5.7 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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