dragonfly_fonts 0.0.3

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 (58) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +23 -0
  3. data/Dockerfile +15 -0
  4. data/Gemfile +4 -0
  5. data/Guardfile +8 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +196 -0
  8. data/Rakefile +9 -0
  9. data/circle.yml +23 -0
  10. data/docker-compose.yml +15 -0
  11. data/dragonfly_fonts.gemspec +28 -0
  12. data/lib/dragonfly_fonts.rb +8 -0
  13. data/lib/dragonfly_fonts/analysers/bbox.rb +35 -0
  14. data/lib/dragonfly_fonts/analysers/font_info.rb +15 -0
  15. data/lib/dragonfly_fonts/analysers/glyphs.rb +14 -0
  16. data/lib/dragonfly_fonts/analysers/gsub_tables.rb +14 -0
  17. data/lib/dragonfly_fonts/plugin.rb +43 -0
  18. data/lib/dragonfly_fonts/processors/correct_metrics.rb +21 -0
  19. data/lib/dragonfly_fonts/processors/encode.rb +57 -0
  20. data/lib/dragonfly_fonts/processors/extract_glyph.rb +19 -0
  21. data/lib/dragonfly_fonts/processors/normalize_names.rb +17 -0
  22. data/lib/dragonfly_fonts/processors/set_dimensions.rb +24 -0
  23. data/lib/dragonfly_fonts/processors/set_ttf_names.rb +52 -0
  24. data/lib/dragonfly_fonts/processors/set_underline.rb +26 -0
  25. data/lib/dragonfly_fonts/processors/set_width.rb +25 -0
  26. data/lib/dragonfly_fonts/processors/set_woff_metadata.rb +23 -0
  27. data/lib/dragonfly_fonts/processors/ttf_autohint.rb +21 -0
  28. data/lib/dragonfly_fonts/processors/web_friendly.rb +17 -0
  29. data/lib/dragonfly_fonts/unicode_ranges.rb +89 -0
  30. data/lib/dragonfly_fonts/version.rb +3 -0
  31. data/samples/Arial.ttf +0 -0
  32. data/samples/Inconsolata.otf +0 -0
  33. data/script/dimensions.py +28 -0
  34. data/script/font_info.py +49 -0
  35. data/script/glyphs.py +25 -0
  36. data/script/gsub_tables.py +43 -0
  37. data/script/normalize_names.sh +13 -0
  38. data/script/underline.py +20 -0
  39. data/script/webfonts.pe +49 -0
  40. data/script/woff_meta.py +81 -0
  41. data/test/dragonfly_fonts/analysers/bbox_test.rb +48 -0
  42. data/test/dragonfly_fonts/analysers/font_info_test.rb +65 -0
  43. data/test/dragonfly_fonts/analysers/glyphs_test.rb +27 -0
  44. data/test/dragonfly_fonts/analysers/gsub_tables_test.rb +42 -0
  45. data/test/dragonfly_fonts/plugin_test.rb +76 -0
  46. data/test/dragonfly_fonts/processors/correct_metrics_test.rb +22 -0
  47. data/test/dragonfly_fonts/processors/encode_test.rb +36 -0
  48. data/test/dragonfly_fonts/processors/extract_glyph_test.rb +22 -0
  49. data/test/dragonfly_fonts/processors/normalize_names_test.rb +18 -0
  50. data/test/dragonfly_fonts/processors/set_dimensions_test.rb +26 -0
  51. data/test/dragonfly_fonts/processors/set_ttf_names_test.rb +23 -0
  52. data/test/dragonfly_fonts/processors/set_underline_test.rb +22 -0
  53. data/test/dragonfly_fonts/processors/set_width_test.rb +48 -0
  54. data/test/dragonfly_fonts/processors/set_woff_metadata_test.rb +28 -0
  55. data/test/dragonfly_fonts/processors/ttf_autohint_test.rb +18 -0
  56. data/test/dragonfly_fonts/processors/web_friendly_test.rb +18 -0
  57. data/test/test_helper.rb +21 -0
  58. metadata +215 -0
@@ -0,0 +1,43 @@
1
+ require 'dragonfly_fonts/analysers/bbox'
2
+ require 'dragonfly_fonts/analysers/font_info'
3
+ require 'dragonfly_fonts/analysers/glyphs'
4
+ require 'dragonfly_fonts/analysers/gsub_tables'
5
+
6
+ require 'dragonfly_fonts/processors/correct_metrics'
7
+ require 'dragonfly_fonts/processors/encode'
8
+ require 'dragonfly_fonts/processors/extract_glyph'
9
+ require 'dragonfly_fonts/processors/normalize_names'
10
+ require 'dragonfly_fonts/processors/set_dimensions'
11
+ require 'dragonfly_fonts/processors/set_ttf_names'
12
+ require 'dragonfly_fonts/processors/set_underline'
13
+ require 'dragonfly_fonts/processors/set_width'
14
+ require 'dragonfly_fonts/processors/set_woff_metadata'
15
+ require 'dragonfly_fonts/processors/ttf_autohint'
16
+ require 'dragonfly_fonts/processors/web_friendly'
17
+
18
+ module DragonflyFonts
19
+ class Plugin
20
+ def call(app, _opts = {})
21
+ app.add_analyser :bbox, Analysers::Bbox.new
22
+ app.add_analyser :font_info, Analysers::FontInfo.new
23
+ app.add_analyser :glyphs, Analysers::Glyphs.new
24
+ app.add_analyser :gsub_tables, Analysers::GsubTables.new
25
+
26
+ # ---------------------------------------------------------------------
27
+
28
+ app.add_processor :correct_metrics, Processors::CorrectMetrics.new
29
+ app.add_processor :encode, Processors::Encode.new
30
+ app.add_processor :extract_glyph, Processors::ExtractGlyph.new
31
+ app.add_processor :normalize_names, Processors::NormalizeNames.new
32
+ app.add_processor :set_dimensions, Processors::SetDimensions.new
33
+ app.add_processor :set_ttf_names, Processors::SetTtfNames.new
34
+ app.add_processor :set_underline, Processors::SetUnderline.new
35
+ app.add_processor :set_width, Processors::SetWidth.new
36
+ app.add_processor :set_woff_metadata, Processors::SetWoffMetadata.new
37
+ app.add_processor :ttf_autohint, Processors::TtfAutohint.new
38
+ app.add_processor :web_friendly, Processors::WebFriendly.new
39
+ end
40
+ end
41
+ end
42
+
43
+ Dragonfly::App.register_plugin(:fonts) { DragonflyFonts::Plugin.new }
@@ -0,0 +1,21 @@
1
+ module DragonflyFonts
2
+ module Processors
3
+ class CorrectMetrics
4
+ def call(font)
5
+ font.shell_update do |old_path, new_path|
6
+ "#{fontforge_command} -lang=ff -c 'Open($1); SetOS2Value(\"HHeadAscent\",$ascent); SetOS2Value(\"HHeadAscentIsOffset\",0); SetOS2Value(\"HHeadDescent\",-$descent); SetOS2Value(\"HHeadDescentIsOffset\",0); SetOS2Value(\"TypoLineGap\",0); Generate($2);' #{old_path} #{new_path}"
7
+ end
8
+ end
9
+
10
+ def update_url(attrs, *_args)
11
+ attrs.style = 'corrmetr'
12
+ end
13
+
14
+ private # =============================================================
15
+
16
+ def fontforge_command
17
+ 'fontforge'
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,57 @@
1
+ require 'shellwords'
2
+
3
+ module DragonflyFonts
4
+ module Processors
5
+ class Encode
6
+ class UnsupportedFormat < RuntimeError; end
7
+
8
+ def call(content, format)
9
+ content.shell_update(ext: format, escape: false) do |old_path, new_path|
10
+ case format.to_sym
11
+ when :eot then ttf2eot(old_path, new_path)
12
+ when :otf, :svg, :ttf, :woff then fontforge(old_path, new_path)
13
+ when :woff2 then woff2(old_path, new_path)
14
+ else fail UnsupportedFormat
15
+ end
16
+ end
17
+
18
+ content.meta['format'] = format.to_s
19
+ content.ext = format
20
+ end
21
+
22
+ def update_url(attrs, format, _args = '')
23
+ attrs.ext = format.to_s
24
+ end
25
+
26
+ private # =============================================================
27
+
28
+ def fontforge(old_path, new_path)
29
+ "#{fontforge_command} -lang=ff -c 'Open($1); Generate($2)' #{old_path} #{new_path}"
30
+ end
31
+
32
+ def fontforge_command
33
+ 'fontforge'
34
+ end
35
+
36
+ # ---------------------------------------------------------------------
37
+
38
+ def ttf2eot(old_path, new_path)
39
+ "#{ttf2eot_command} < #{old_path} > #{new_path}"
40
+ end
41
+
42
+ def ttf2eot_command
43
+ 'ttf2eot'
44
+ end
45
+
46
+ # ---------------------------------------------------------------------
47
+
48
+ def woff2(old_path, new_path)
49
+ "#{woff2_command} #{old_path} && mv #{old_path.gsub(/\.\w{3,5}\z/, '.woff2')} #{new_path}"
50
+ end
51
+
52
+ def woff2_command
53
+ 'woff2_compress'
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,19 @@
1
+ module DragonflyFonts
2
+ module Processors
3
+ class ExtractGlyph
4
+ def call(font, glyph, opts = {})
5
+ format = opts.fetch(:format, :svg)
6
+
7
+ font.shell_update(ext: format) do |old_path, new_path|
8
+ "#{fontforge_command} -lang=ff -c 'Open($1); Select(\"#{glyph}\"); Export(\"#{new_path}\");' #{old_path}"
9
+ end
10
+ end
11
+
12
+ private # =============================================================
13
+
14
+ def fontforge_command
15
+ 'fontforge'
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,17 @@
1
+ module DragonflyFonts
2
+ module Processors
3
+ class NormalizeNames
4
+ def call(font, opts = {})
5
+ font.shell_update(ext: :ttf) do |old_path, new_path|
6
+ "#{fontforge_command} -script #{DragonflyFonts::SCRIPT_DIR.join('normalize_names.sh')} #{old_path} #{new_path}"
7
+ end
8
+ end
9
+
10
+ private # =============================================================
11
+
12
+ def fontforge_command
13
+ 'fontforge'
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,24 @@
1
+ module DragonflyFonts
2
+ module Processors
3
+ class SetDimensions
4
+ def call(content, opts = {})
5
+ ascent = opts.fetch(:ascent, '')
6
+ descent = opts.fetch(:descent, '')
7
+
8
+ content.shell_update do |old_path, new_path|
9
+ "#{dimensions_script} #{old_path} #{new_path} #{Shellwords.escape(ascent)} #{Shellwords.escape(descent)}"
10
+ end
11
+ end
12
+
13
+ def update_url(attrs, *_args)
14
+ attrs.style = 'adjdimensions'
15
+ end
16
+
17
+ private # =============================================================
18
+
19
+ def dimensions_script
20
+ DragonflyFonts::SCRIPT_DIR.join('dimensions.py')
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,52 @@
1
+ require 'shellwords'
2
+
3
+ module DragonflyFonts
4
+ module Processors
5
+ class SetTtfNames
6
+ # http://partners.adobe.com/public/developer/opentype/index_name.html#enc4
7
+ NAME_IDS = {
8
+ compatible_full: 18,
9
+ copyright: 0,
10
+ description: 10,
11
+ designer: 9,
12
+ designer_url: 12,
13
+ fontname: 1,
14
+ fullname: 4,
15
+ license: 13,
16
+ license_url: 14,
17
+ manufacturer: 8,
18
+ postscript_cid: 20,
19
+ postscript_name: 6,
20
+ preferred_family: 16,
21
+ preferred_subfamily: 17,
22
+ sample_text: 19,
23
+ trademark: 7,
24
+ uid: 3,
25
+ vendor_url: 11,
26
+ version: 5,
27
+ weight: 2
28
+ }
29
+
30
+ def call(font, values = {})
31
+ font.shell_update do |old_path, new_path|
32
+ "#{fontforge_command} -lang=ff -c 'Open($1); #{command_string(values)} Generate($2);' #{old_path} #{new_path}"
33
+ end
34
+ end
35
+
36
+ private # =============================================================
37
+
38
+ def fontforge_command
39
+ 'fontforge'
40
+ end
41
+
42
+ def command_string(values)
43
+ res = []
44
+ values.each do |k, v|
45
+ next unless NAME_IDS.keys.include?(k.to_sym)
46
+ res << "SetTTFName(0x409, #{NAME_IDS[k]}, \"#{Shellwords.escape(v)}\");"
47
+ end
48
+ res.join
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,26 @@
1
+ require 'shellwords'
2
+
3
+ module DragonflyFonts
4
+ module Processors
5
+ class SetUnderline
6
+ def call(content, opts = {})
7
+ upos = opts.fetch(:upos, '')
8
+ uwidth = opts.fetch(:uwidth, '')
9
+
10
+ content.shell_update do |old_path, new_path|
11
+ "#{underline_script} #{old_path} #{new_path} #{Shellwords.escape(upos)} #{Shellwords.escape(uwidth)}"
12
+ end
13
+ end
14
+
15
+ def update_url(attrs, *_args)
16
+ attrs.style = 'adjunderline'
17
+ end
18
+
19
+ private # =============================================================
20
+
21
+ def underline_script
22
+ DragonflyFonts::SCRIPT_DIR.join('underline.py')
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,25 @@
1
+ module DragonflyFonts
2
+ module Processors
3
+ class SetWidth
4
+ # relative:
5
+ # when absent or 0 the vertical width will be set to width
6
+ # when 1 then the vertical width will be incremented by the first
7
+ # when 2 then the vertical width will be scaled by <first argument>/100.0.
8
+ def call(content, width, relative = 1)
9
+ content.shell_update do |old_path, new_path|
10
+ "#{fontforge_command} -lang=ff -c 'Open($1); SelectWorthOutputting(); SetWidth(#{width},#{relative}); Generate($2);' #{old_path} #{new_path}"
11
+ end
12
+ end
13
+
14
+ def update_url(attrs, *_args)
15
+ attrs.style = 'adjwidth'
16
+ end
17
+
18
+ private # =============================================================
19
+
20
+ def fontforge_command
21
+ 'fontforge'
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,23 @@
1
+ require 'shellwords'
2
+
3
+ module DragonflyFonts
4
+ module Processors
5
+ class SetWoffMetadata
6
+ def call(font, uniqueid, licensee_name = '')
7
+ font.shell_update(ext: :woff) do |old_path, new_path|
8
+ "#{woff_meta_script} #{old_path} #{new_path} #{Shellwords.escape(uniqueid)} #{Shellwords.escape(licensee_name)}"
9
+ end
10
+ end
11
+
12
+ def update_url(attrs, *_args)
13
+ attrs.style = 'woffmeta'
14
+ end
15
+
16
+ private # =============================================================
17
+
18
+ def woff_meta_script
19
+ DragonflyFonts::SCRIPT_DIR.join('woff_meta.py')
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,21 @@
1
+ module DragonflyFonts
2
+ module Processors
3
+ class TtfAutohint
4
+ def call(font)
5
+ font.shell_update do |old_path, new_path|
6
+ "#{ttfautohint_command} --strong-stem-width='' --windows-compatibility --composites #{old_path} #{new_path}"
7
+ end
8
+ end
9
+
10
+ def update_url(attrs, *_args)
11
+ attrs.style = 'ttfautohint'
12
+ end
13
+
14
+ private # =============================================================
15
+
16
+ def ttfautohint_command
17
+ 'ttfautohint'
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,17 @@
1
+ module DragonflyFonts
2
+ module Processors
3
+ class WebFriendly
4
+ def call(font, opts = {})
5
+ font.shell_update(ext: :ttf) do |old_path, new_path|
6
+ "#{fontforge_command} -script #{DragonflyFonts::SCRIPT_DIR.join('webfonts.pe')} #{old_path} #{new_path}"
7
+ end
8
+ end
9
+
10
+ private # =============================================================
11
+
12
+ def fontforge_command
13
+ 'fontforge'
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,89 @@
1
+ DragonflyFonts::UNICODE_RANGES = [
2
+ ['Basic Latin', ['0x0000-0x007F', '0-127']],
3
+ ['Latin-1 Supplement', ['0x0080-0x00FF', '128-255']],
4
+ ['Latin Extended-A', ['0x0100-0x017F', '256-383']],
5
+ ['Latin Extended-B', ['0x0180-0x024F', '384-591']],
6
+ ['IPA Extensions', ['0x0250-0x02AF', '592-687']],
7
+ ['Spacing Modifier Letters', ['0x02B0-0x02FF', '688-767']],
8
+ ['Combining Diacritical Marks', ['0x0300-0x036F', '768-879']],
9
+ ['Greek', ['0x0370-0x03FF', '880-1023']],
10
+ ['Cyrillic', ['0x0400-0x04FF', '1024-1279']],
11
+ ['Armenian', ['0x0530-0x058F', '1328-1423']],
12
+ ['Hebrew', ['0x0590-0x05FF', '1424-1535']],
13
+ ['Arabic', ['0x0600-0x06FF', '1536-1791']],
14
+ ['Syriac', ['0x0700-0x074F', '1792-1871']],
15
+ ['Thaana', ['0x0780-0x07BF', '1920-1983']],
16
+ ['Devanagari', ['0x0900-0x097F', '2304-2431']],
17
+ ['Bengali', ['0x0980-0x09FF', '2432-2559']],
18
+ ['Gurmukhi', ['0x0A00-0x0A7F', '2560-2687']],
19
+ ['Gujarati', ['0x0A80-0x0AFF', '2688-2815']],
20
+ ['Oriya', ['0x0B00-0x0B7F', '2816-2943']],
21
+ ['Tamil', ['0x0B80-0x0BFF', '2944-3071']],
22
+ ['Telugu', ['0x0C00-0x0C7F', '3072-3199']],
23
+ ['Kannada', ['0x0C80-0x0CFF', '3200-3327']],
24
+ ['Malayalam', ['0x0D00-0x0D7F', '3328-3455']],
25
+ ['Sinhala', ['0x0D80-0x0DFF', '3456-3583']],
26
+ ['Thai', ['0x0E00-0x0E7F', '3584-3711']],
27
+ ['Lao', ['0x0E80-0x0EFF', '3712-3839']],
28
+ ['Tibetan', ['0x0F00-0x0FFF', '3840-4095']],
29
+ ['Myanmar', ['0x1000-0x109F', '4096-4255']],
30
+ ['Georgian', ['0x10A0-0x10FF', '4256-4351']],
31
+ ['Hangul Jamo', ['0x1100-0x11FF', '4352-4607']],
32
+ ['Ethiopic', ['0x1200-0x137F', '4608-4991']],
33
+ ['Cherokee', ['0x13A0-0x13FF', '5024-5119']],
34
+ ['Unified Canadian Aboriginal Syllabics', ['0x1400-0x167F', '5120-5759']],
35
+ ['Ogham', ['0x1680-0x169F', '5760-5791']],
36
+ ['Runic', ['0x16A0-0x16FF', '5792-5887']],
37
+ ['Khmer', ['0x1780-0x17FF', '6016-6143']],
38
+ ['Mongolian', ['0x1800-0x18AF', '6144-6319']],
39
+ ['Latin Extended Additional', ['0x1E00-0x1EFF', '7680-7935']],
40
+ ['Greek Extended', ['0x1F00-0x1FFF', '7936-8191']],
41
+ ['General Punctuation', ['0x2000-0x206F', '8192-8303']],
42
+ ['Superscripts and Subscripts', ['0x2070-0x209F', '8304-8351']],
43
+ ['Currency Symbols', ['0x20A0-0x20CF', '8352-8399']],
44
+ ['Combining Marks for Symbols', ['0x20D0-0x20FF', '8400-8447']],
45
+ ['Letterlike Symbols', ['0x2100-0x214F', '8448-8527']],
46
+ ['Number Forms', ['0x2150-0x218F', '8528-8591']],
47
+ ['Arrows', ['0x2190-0x21FF', '8592-8703']],
48
+ ['Mathematical Operators', ['0x2200-0x22FF', '8704-8959']],
49
+ ['Miscellaneous Technical', ['0x2300-0x23FF', '8960-9215']],
50
+ ['Control Pictures', ['0x2400-0x243F', '9216-9279']],
51
+ ['Optical Character Recognition', ['0x2440-0x245F', '9280-9311']],
52
+ ['Enclosed Alphanumerics', ['0x2460-0x24FF', '9312-9471']],
53
+ ['Box Drawing', ['0x2500-0x257F', '9472-9599']],
54
+ ['Block Elements', ['0x2580-0x259F', '9600-9631']],
55
+ ['Geometric Shapes', ['0x25A0-0x25FF', '9632-9727']],
56
+ ['Miscellaneous Symbols', ['0x2600-0x26FF', '9728-9983']],
57
+ ['Dingbats', ['0x2700-0x27BF', '9984-10175']],
58
+ ['Braille Patterns', ['0x2800-0x28FF', '10240-10495']],
59
+ ['CJK Radicals Supplement', ['0x2E80-0x2EFF', '11904-12031']],
60
+ ['Kangxi Radicals', ['0x2F00-0x2FDF', '12032-12255']],
61
+ ['Ideographic Description Characters', ['0x2FF0-0x2FFF', '12272-12287']],
62
+ ['CJK Symbols and Punctuation', ['0x3000-0x303F', '12288-12351']],
63
+ ['Hiragana', ['0x3040-0x309F', '12352-12447']],
64
+ ['Katakana', ['0x30A0-0x30FF', '12448-12543']],
65
+ ['Bopomofo', ['0x3100-0x312F', '12544-12591']],
66
+ ['Hangul Compatibility Jamo', ['0x3130-0x318F', '12592-12687']],
67
+ ['Kanbun', ['0x3190-0x319F', '12688-12703']],
68
+ ['Bopomofo Extended', ['0x31A0-0x31BF', '12704-12735']],
69
+ ['Enclosed CJK Letters and Months', ['0x3200-0x32FF', '12800-13055']],
70
+ ['CJK Compatibility', ['0x3300-0x33FF', '13056-13311']],
71
+ ['CJK Unified Ideographs Extension A', ['0x3400-0x4DB5', '13312-19893']],
72
+ ['CJK Unified Ideographs', ['0x4E00-0x9FFF', '19968-40959']],
73
+ ['Yi Syllables', ['0xA000-0xA48F', '40960-42127']],
74
+ ['Yi Radicals', ['0xA490-0xA4CF', '42128-42191']],
75
+ ['Hangul Syllables', ['0xAC00-0xD7A3', '44032-55203']],
76
+ ['High Surrogates', ['0xD800-0xDB7F', '55296-56191']],
77
+ ['High Private Use Surrogates', ['0xDB80-0xDBFF', '56192-56319']],
78
+ ['Low Surrogates', ['0xDC00-0xDFFF', '56320-57343']],
79
+ ['Private Use', ['0xE000-0xF8FF', '57344-63743']],
80
+ ['CJK Compatibility Ideographs', ['0xF900-0xFAFF', '63744-64255']],
81
+ ['Alphabetic Presentation Forms', ['0xFB00-0xFB4F', '64256-64335']],
82
+ ['Arabic Presentation Forms-A', ['0xFB50-0xFDFF', '64336-65023']],
83
+ ['Combining Half Marks', ['0xFE20-0xFE2F', '65056-65071']],
84
+ ['CJK Compatibility Forms', ['0xFE30-0xFE4F', '65072-65103']],
85
+ ['Small Form Variants', ['0xFE50-0xFE6F', '65104-65135']],
86
+ ['Arabic Presentation Forms-B', ['0xFE70-0xFEFE', '65136-65278']],
87
+ ['Specials', ['0xFFF0-0xFFFD', '65520-65533']],
88
+ ['Halfwidth and Fullwidth Forms', ['0xFF00-0xFFEF', '65280-65519']]
89
+ ]