skrift-x11 0.2.2 → 0.3.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 +4 -4
- data/lib/skrift/x11/glyphs.rb +122 -194
- data/lib/skrift/x11/version.rb +1 -1
- data/skrift-x11.gemspec +2 -1
- metadata +18 -5
- data/lib/skrift/x11/boxdrawing.rb +0 -198
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ed8b7aa32d9e1eb6984384f6f22488d3d6f5be7f5eef1c671c0226cf7ff431c5
|
|
4
|
+
data.tar.gz: cee3cb6866a431e8f8c2d0e4543a1eed56f85883098b1561902dd52e0641386e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: be22707412fa343bb25f8a9ddbd754801090ccf1458041aeb91c18513d8a963d2121eeae293a65d34d6c782d9d7d166f57b8448bb30dd755ae6452a0a3c9bc27
|
|
7
|
+
data.tar.gz: 84b80afb3b159bfbdc5733a397c670305b7c6dfc4d37a8e9cfac57bdb12b775703bf65ddbf62ef5a0eeab32ccad3a8ce270b9eec7cd15b99708f196b2454f2ea
|
data/lib/skrift/x11/glyphs.rb
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
|
+
require 'skrift/boxdrawing'
|
|
1
2
|
|
|
2
|
-
|
|
3
|
+
# Colour-glyph support is optional: only needed when a `color:` font is given.
|
|
4
|
+
begin
|
|
5
|
+
require 'skrift/color'
|
|
6
|
+
rescue LoadError
|
|
7
|
+
# skrift-color not installed; colour rendering will be unavailable.
|
|
8
|
+
end
|
|
3
9
|
|
|
4
10
|
module X11
|
|
5
11
|
# XRender constants currently not defined in pure-x11.
|
|
6
|
-
# FIXME.
|
|
12
|
+
# FIXME: these are generic XRender enumerants and belong in pure-x11.
|
|
7
13
|
module Form
|
|
8
14
|
PictOpSrc=1
|
|
9
15
|
PictOpOver=3
|
|
@@ -13,70 +19,49 @@ end
|
|
|
13
19
|
|
|
14
20
|
module Skrift
|
|
15
21
|
module X11
|
|
16
|
-
|
|
22
|
+
# Thin XRender adapter over Skrift::GlyphCache. Monochrome glyphs go into an
|
|
23
|
+
# A8 glyph set (coloured at composite time by a solid fill); colour glyphs
|
|
24
|
+
# (emoji, via the skrift-color delegate) go into an ARGB32 glyph set and are
|
|
25
|
+
# composited with a neutral white source so their own colours show. A string
|
|
26
|
+
# is drawn as runs of like-typed glyphs, one composite per run. All the
|
|
27
|
+
# platform-independent work (fallback, rasterisation, layout, caching, box
|
|
28
|
+
# drawing, colour delegation) lives in skrift core + the plugins.
|
|
17
29
|
class Glyphs
|
|
18
30
|
attr_reader :fixed_width
|
|
19
|
-
attr_accessor :maxheight
|
|
20
|
-
|
|
21
|
-
def load_font(index)
|
|
22
|
-
return @fonts[index] if @fonts[index]
|
|
23
|
-
f = @fontset[index]
|
|
24
|
-
return nil if !f
|
|
25
|
-
return @fonts[index] = Font.load(f) if File.exist?(f)
|
|
26
|
-
|
|
27
|
-
# FIXME: Do proper resolution of XDG dirs
|
|
28
|
-
fn = File.expand_path("~/.local/share/fonts/#{f}")
|
|
29
|
-
return @fonts[index] = Font.load(fn) if File.exist?(fn)
|
|
30
|
-
|
|
31
|
-
fn = `fc-match --format='%{file}\n' #{fn}`.split("\n").first
|
|
32
|
-
return @fonts[index] = Font.load(fn) if File.exist?(fn)
|
|
33
|
-
return nil
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
def inspect
|
|
37
|
-
"<Glyphs #{object_id}"
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
def initialize dpy, font=nil, fontset: nil, x_scale:, y_scale:, pic: nil, fixed: nil, maxheight: nil, fit: false
|
|
41
|
-
@fontset = fontset
|
|
42
|
-
@fonts = @fontset ? [] : [font]
|
|
43
|
-
@font = font || load_font(0)
|
|
44
|
-
@maxheight = maxheight
|
|
31
|
+
attr_accessor :maxheight, :lm
|
|
45
32
|
|
|
33
|
+
# +color+ (optional) is a font (path, name or Skrift::Font) providing
|
|
34
|
+
# colour glyphs, e.g. an emoji font; requires the skrift-color gem.
|
|
35
|
+
def initialize(dpy, font=nil, fontset: nil, x_scale:, y_scale:, pic: nil, fixed: nil, maxheight: nil, fit: false, color: nil)
|
|
46
36
|
@dpy = dpy
|
|
47
|
-
@sft = SFT.new(@font)
|
|
48
|
-
@sft.x_scale = x_scale
|
|
49
|
-
@sft.y_scale = y_scale
|
|
50
37
|
@pic = pic
|
|
51
|
-
@
|
|
52
|
-
# When set (with a fixed cell), glyphs whose natural size exceeds the
|
|
53
|
-
# cell are scaled down to fit and centred instead of being clamped
|
|
54
|
-
# (and distorted) at the cell edge by the rasteriser.
|
|
55
|
-
@fit = fit
|
|
56
|
-
|
|
57
|
-
g = @sft.gmetrics(@sft.lookup("M".ord))
|
|
58
|
-
if !g
|
|
59
|
-
g = @sft.gmetrics(0)
|
|
60
|
-
end
|
|
61
|
-
if g
|
|
62
|
-
@fixed_width = g.advance_width.ceil
|
|
63
|
-
else
|
|
64
|
-
@fixed_width = x_scale
|
|
65
|
-
end
|
|
38
|
+
@maxheight = maxheight
|
|
66
39
|
|
|
67
|
-
|
|
68
|
-
@
|
|
69
|
-
|
|
70
|
-
|
|
40
|
+
color_renderer = build_color_renderer(color, x_scale, y_scale)
|
|
41
|
+
@cache = Skrift::GlyphCache.new(fontset || font,
|
|
42
|
+
x_scale: x_scale, y_scale: y_scale,
|
|
43
|
+
fixed: fixed, maxheight: maxheight, fit: fit,
|
|
44
|
+
special: ->(cp) { @boxes.glyph(cp) },
|
|
45
|
+
color: color_renderer)
|
|
46
|
+
@boxes = Skrift::BoxDrawing.new(@cache.cell_width, @cache.cell_height)
|
|
47
|
+
@fixed_width = @cache.cell_width
|
|
48
|
+
@lm = Skrift::LMetrics.new(@cache.ascent, @cache.descent, @cache.line_gap)
|
|
71
49
|
|
|
72
|
-
@
|
|
50
|
+
@colcache = {}
|
|
51
|
+
@gids = {} # codepoint -> [:mono|:color, glyph-set id]
|
|
52
|
+
@nextgid = 1
|
|
73
53
|
|
|
74
|
-
# The glyph set
|
|
75
54
|
@gfmt = @dpy.render_find_standard_format(:a8).id
|
|
76
55
|
@glyphset = @dpy.render_create_glyph_set(@gfmt)
|
|
56
|
+
|
|
57
|
+
if color_renderer
|
|
58
|
+
@cfmt = @dpy.render_find_standard_format(:argb24).id
|
|
59
|
+
@cglyphset = @dpy.render_create_glyph_set(@cfmt)
|
|
60
|
+
@white = @dpy.render_create_solid_fill(0xffff, 0xffff, 0xffff, 0xffff)
|
|
61
|
+
end
|
|
77
62
|
end
|
|
78
63
|
|
|
79
|
-
|
|
64
|
+
def inspect = "<Glyphs #{object_id}>"
|
|
80
65
|
|
|
81
66
|
def fill_for_col(col)
|
|
82
67
|
return @colcache[col] if @colcache[col]
|
|
@@ -86,166 +71,109 @@ module Skrift
|
|
|
86
71
|
g |= g << 8
|
|
87
72
|
b = col & 0xff
|
|
88
73
|
b |= b << 8
|
|
89
|
-
|
|
90
|
-
|
|
74
|
+
@colcache[col] = @dpy.render_create_solid_fill(r, g, b, 0xffff)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def text_width(str) = @cache.text_width(str)
|
|
78
|
+
|
|
79
|
+
# Draw +str+ as one composite per run of same-typed glyphs (mono vs
|
|
80
|
+
# colour). Each rendered glyph advances the pen by one cell, so a pure-text
|
|
81
|
+
# string is a single A8 composite exactly as before; emoji break the run.
|
|
82
|
+
def render_str(pic, col, x, y, str)
|
|
83
|
+
glyphs = str.to_s.each_char.map { |ch| gsgid_for(ch.ord) }
|
|
84
|
+
rendered = 0
|
|
85
|
+
i = 0
|
|
86
|
+
while i < glyphs.length
|
|
87
|
+
(i += 1; next) if glyphs[i].nil? # unmapped: skip, no advance
|
|
88
|
+
type = glyphs[i].first
|
|
89
|
+
run_x = x + rendered * @fixed_width
|
|
90
|
+
ids = []
|
|
91
|
+
while i < glyphs.length && glyphs[i] && glyphs[i].first == type
|
|
92
|
+
ids << glyphs[i].last
|
|
93
|
+
rendered += 1
|
|
94
|
+
i += 1
|
|
95
|
+
end
|
|
96
|
+
composite_run(type, pic, col, run_x, y, ids)
|
|
97
|
+
end
|
|
91
98
|
end
|
|
92
99
|
|
|
93
100
|
private
|
|
94
101
|
|
|
95
|
-
def
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
102
|
+
def build_color_renderer(color, x_scale, y_scale)
|
|
103
|
+
return nil unless color
|
|
104
|
+
# A caller can pass its own delegate (e.g. one that gates which
|
|
105
|
+
# codepoints render in colour); we use it as-is. Otherwise treat the
|
|
106
|
+
# argument as a font and build an ungated Color::Renderer from it.
|
|
107
|
+
return color if color.respond_to?(:render)
|
|
108
|
+
unless defined?(Skrift::Color::Renderer)
|
|
109
|
+
raise "colour glyphs require the skrift-color gem"
|
|
110
|
+
end
|
|
111
|
+
font = Skrift::FontSet.new(color)[0]
|
|
112
|
+
Skrift::Color::Renderer.new(font, x_scale: x_scale, y_scale: y_scale)
|
|
99
113
|
end
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
def cache_special(ch)
|
|
103
|
-
# Box drawing
|
|
104
|
-
return nil if !((0x2500 .. 0x257f) === ch.ord)
|
|
105
114
|
|
|
115
|
+
def composite_run(type, pic, col, x, y, ids)
|
|
116
|
+
if type == :color
|
|
117
|
+
@dpy.render_composite_glyphs32(:PictOpOver, @white, pic, @cfmt, @cglyphset, 0, 0, [x, y, ids])
|
|
118
|
+
else
|
|
119
|
+
@dpy.render_composite_glyphs32(:PictOpOver, fill_for_col(col), pic, @gfmt, @glyphset, 0, 0, [x, y, ids])
|
|
120
|
+
end
|
|
121
|
+
end
|
|
106
122
|
|
|
107
|
-
|
|
123
|
+
# [:mono|:color, glyph-set id] for a codepoint, uploading on first use, or
|
|
124
|
+
# nil for a codepoint no font maps (and which isn't box drawing/colour).
|
|
125
|
+
def gsgid_for(cp)
|
|
126
|
+
return @gids[cp] if @gids.key?(cp)
|
|
127
|
+
@gids[cp] = upload(cp)
|
|
128
|
+
end
|
|
108
129
|
|
|
130
|
+
def upload(cp)
|
|
131
|
+
# NUL doubles as the double-width spacer (the trailing cell of a wide
|
|
132
|
+
# glyph): a blank cell that only advances, letting the wide glyph in the
|
|
133
|
+
# previous cell overflow into it.
|
|
134
|
+
return upload_blank if cp.zero?
|
|
109
135
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
img.width, # w
|
|
113
|
-
img.height, # h
|
|
114
|
-
0, # x
|
|
115
|
-
0, # y
|
|
116
|
-
fixed_width,
|
|
117
|
-
0
|
|
118
|
-
)
|
|
136
|
+
g = @cache.glyph(cp)
|
|
137
|
+
return nil unless g
|
|
119
138
|
|
|
120
139
|
gsgid = @nextgid
|
|
121
140
|
@nextgid += 1
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
return if gid.nil?
|
|
129
|
-
mtx = @sft.gmetrics(gid)
|
|
130
|
-
scaled = false
|
|
131
|
-
saved = nil
|
|
132
|
-
|
|
133
|
-
# Oversized glyph in a fixed cell: re-render it at a reduced scale so
|
|
134
|
-
# it fits the cell, then position it through the normal
|
|
135
|
-
# baseline-relative path below (no vertical shift). Otherwise the
|
|
136
|
-
# rasteriser clamps the overflow at the cell edge and distorts the
|
|
137
|
-
# shape. Only glyphs clearly wider/taller than the cell are scaled -
|
|
138
|
-
# a marginal overshoot (e.g. a 'W' a pixel past the cell) is left to
|
|
139
|
-
# the clamp, so ordinary text is untouched.
|
|
140
|
-
if @fit && @fixed && mtx
|
|
141
|
-
sx = (mtx.min_width || 0) > boxw * 1.1 ? boxw.to_f / mtx.min_width : 1.0
|
|
142
|
-
sy = (mtx.min_height || 0) > boxh * 1.1 ? boxh.to_f / mtx.min_height : 1.0
|
|
143
|
-
s = [sx, sy].min
|
|
144
|
-
if s < 1.0
|
|
145
|
-
saved = [@sft.x_scale, @sft.y_scale]
|
|
146
|
-
@sft.x_scale = @sft.x_scale * s
|
|
147
|
-
@sft.y_scale = @sft.y_scale * s
|
|
148
|
-
mtx = @sft.gmetrics(gid)
|
|
149
|
-
scaled = true
|
|
150
|
-
end
|
|
151
|
-
end
|
|
152
|
-
|
|
153
|
-
# FIXME: Not sure what to do if mtx.nil? here.
|
|
154
|
-
# Maybe use x/y scale?
|
|
155
|
-
if @fixed
|
|
156
|
-
w = fixed_width
|
|
141
|
+
if g.color?
|
|
142
|
+
@dpy.render_add_glyphs(@cglyphset, gsgid, glyph_info(g), argb_data(g.rgba))
|
|
143
|
+
[:color, gsgid]
|
|
144
|
+
elsif g.alpha
|
|
145
|
+
@dpy.render_add_glyphs(@glyphset, gsgid, glyph_info(g), g.alpha.pack("C*"))
|
|
146
|
+
[:mono, gsgid]
|
|
157
147
|
else
|
|
158
|
-
|
|
148
|
+
upload_blank # no outline (e.g. space)
|
|
159
149
|
end
|
|
160
|
-
h = [mtx&.min_height || 1, @maxheight].compact.min
|
|
161
|
-
#p [w,h]
|
|
162
|
-
img = Image.new((w+3)&~3, h)
|
|
163
|
-
if !@sft.render(gid, img)
|
|
164
|
-
#STDERR.puts "Unable to render #{gid}\n"
|
|
165
|
-
data = "\0"*(w*h)
|
|
166
|
-
else
|
|
167
|
-
data = img.pixels.pack("C*")
|
|
168
|
-
end
|
|
169
|
-
|
|
170
|
-
yoff = mtx.y_offset || baseline
|
|
171
|
-
|
|
172
|
-
info = ::X11::Form::GlyphInfo.new(
|
|
173
|
-
img.width, # w
|
|
174
|
-
img.height, # h
|
|
175
|
-
-mtx.left_side_bearing, # x
|
|
176
|
-
yoff-baseline, # y
|
|
177
|
-
fixed_width || mtx.advance_width, # || mtx.advance_width, # x_off
|
|
178
|
-
0
|
|
179
|
-
)
|
|
180
|
-
|
|
181
|
-
@dpy.render_add_glyphs(@glyphset, gsgid, info, data)
|
|
182
|
-
@glyphcache[gsgid] = scaled ? (fixed_width || mtx.advance_width) : mtx.advance_width
|
|
183
|
-
ensure
|
|
184
|
-
@sft.x_scale, @sft.y_scale = saved if saved
|
|
185
150
|
end
|
|
186
151
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
@glyphcache[gl].to_i + sum
|
|
195
|
-
}
|
|
196
|
-
end
|
|
197
|
-
|
|
198
|
-
private
|
|
199
|
-
def each_font
|
|
200
|
-
Array(@fontset).each_index do |i|
|
|
201
|
-
yield load_font(i)
|
|
202
|
-
end
|
|
152
|
+
# A blank glyph that only advances one cell.
|
|
153
|
+
def upload_blank
|
|
154
|
+
gsgid = @nextgid
|
|
155
|
+
@nextgid += 1
|
|
156
|
+
info = ::X11::Form::GlyphInfo.new(0, 0, 0, 0, @fixed_width, 0)
|
|
157
|
+
@dpy.render_add_glyphs(@glyphset, gsgid, info, "".b)
|
|
158
|
+
[:mono, gsgid]
|
|
203
159
|
end
|
|
204
|
-
|
|
205
|
-
def cache_glyphs(str)
|
|
206
|
-
gl = str.to_s.each_char.map do |ch|
|
|
207
|
-
|
|
208
|
-
cache = @chcache[ch]
|
|
209
|
-
|
|
210
|
-
if cache.nil?
|
|
211
|
-
cache = cache_special(ch)
|
|
212
|
-
end
|
|
213
|
-
|
|
214
|
-
if cache.nil?
|
|
215
|
-
each_font do |font|
|
|
216
|
-
# FIXME: This is stupid and must be untangled
|
|
217
|
-
@sft.font = font
|
|
218
|
-
gid = @sft.lookup(ch.ord)
|
|
219
|
-
if gid
|
|
220
|
-
cache = @chcache[ch] = {font: font, gid: gid, gsgid: @nextgid}
|
|
221
|
-
@nextgid += 1
|
|
222
|
-
break
|
|
223
|
-
end
|
|
224
|
-
end
|
|
225
|
-
end
|
|
226
|
-
|
|
227
|
-
if cache
|
|
228
|
-
data = @glyphcache[cache[:gsgid]]
|
|
229
|
-
if !data
|
|
230
|
-
@sft.font = cache[:font]
|
|
231
|
-
data = cache_glyph(cache[:gsgid], cache[:gid], @lm.ascender)
|
|
232
|
-
end
|
|
233
160
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
end
|
|
161
|
+
def glyph_info(g)
|
|
162
|
+
::X11::Form::GlyphInfo.new(g.width, g.height,
|
|
163
|
+
-g.left_side_bearing, g.y_offset - @cache.baseline,
|
|
164
|
+
@fixed_width, 0)
|
|
239
165
|
end
|
|
240
166
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
167
|
+
# Pack straight-alpha 0xRRGGBBAA pixels as premultiplied ARGB32 in the
|
|
168
|
+
# little-endian byte order XRender's argb format expects (B, G, R, A).
|
|
169
|
+
def argb_data(rgba)
|
|
170
|
+
rgba.map do |px|
|
|
171
|
+
a = px & 0xff
|
|
172
|
+
r = (((px >> 24) & 0xff) * a) / 255
|
|
173
|
+
g = (((px >> 16) & 0xff) * a) / 255
|
|
174
|
+
b = (((px >> 8) & 0xff) * a) / 255
|
|
175
|
+
(a << 24) | (r << 16) | (g << 8) | b
|
|
176
|
+
end.pack("L<*")
|
|
249
177
|
end
|
|
250
178
|
end
|
|
251
179
|
end
|
data/lib/skrift/x11/version.rb
CHANGED
data/skrift-x11.gemspec
CHANGED
|
@@ -34,7 +34,8 @@ Gem::Specification.new do |spec|
|
|
|
34
34
|
|
|
35
35
|
# Uncomment to register a new dependency of your gem
|
|
36
36
|
# spec.add_dependency "example-gem", "~> 1.0"
|
|
37
|
-
spec.add_dependency "skrift"
|
|
37
|
+
spec.add_dependency "skrift", ">= 0.3.0"
|
|
38
|
+
spec.add_dependency "skrift-boxdrawing", ">= 0.2.0"
|
|
38
39
|
spec.add_dependency "pure-x11"
|
|
39
40
|
|
|
40
41
|
# For more information and examples about making a new gem, check out our
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: skrift-x11
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Vidar Hokstad
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-06-
|
|
11
|
+
date: 2026-06-22 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: skrift
|
|
@@ -16,14 +16,28 @@ dependencies:
|
|
|
16
16
|
requirements:
|
|
17
17
|
- - ">="
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version:
|
|
19
|
+
version: 0.3.0
|
|
20
20
|
type: :runtime
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
24
|
- - ">="
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
|
-
version:
|
|
26
|
+
version: 0.3.0
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: skrift-boxdrawing
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - ">="
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: 0.2.0
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - ">="
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: 0.2.0
|
|
27
41
|
- !ruby/object:Gem::Dependency
|
|
28
42
|
name: pure-x11
|
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -51,7 +65,6 @@ files:
|
|
|
51
65
|
- example.png
|
|
52
66
|
- example.rb
|
|
53
67
|
- lib/skrift/x11.rb
|
|
54
|
-
- lib/skrift/x11/boxdrawing.rb
|
|
55
68
|
- lib/skrift/x11/glyphs.rb
|
|
56
69
|
- lib/skrift/x11/version.rb
|
|
57
70
|
- resources/FiraGO-Regular_extended_with_NotoSansEgyptianHieroglyphs-Regular.ttf
|
|
@@ -1,198 +0,0 @@
|
|
|
1
|
-
module Skrift
|
|
2
|
-
module X11
|
|
3
|
-
class Glyphs
|
|
4
|
-
|
|
5
|
-
def empty_box_image
|
|
6
|
-
img = Image.new(stride, boxh)
|
|
7
|
-
img.pixels = Array.new(img.width*img.height,0x00)
|
|
8
|
-
img
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
def cache_box(ch)
|
|
13
|
-
@boxcache ||= {}
|
|
14
|
-
c = @boxcache[ch] if @boxcache[ch]
|
|
15
|
-
|
|
16
|
-
hx = (boxw+1)/2
|
|
17
|
-
hy = (boxh+1)/2
|
|
18
|
-
yoff = hy*stride
|
|
19
|
-
img = nil
|
|
20
|
-
h = 1 # 1/3 width of "heavy" line
|
|
21
|
-
|
|
22
|
-
lh = light_h = [-255, 0,255, 0, 255]; hh = heavy_h = [-255, -h,255, h, 255]
|
|
23
|
-
lv = light_v = [ 0,-255, 0,255, 255]; hv = heavy_v = [ -h,-255, h,255, 255]
|
|
24
|
-
|
|
25
|
-
ll = light_l = [-255, 0, 0, 0, 255]; hl = heavy_l = [-255, -h, 0, h, 255]
|
|
26
|
-
lu = light_u = [ 0,-255, 0, 0, 255]; hu = heavy_u = [ -h,-255, h, 0, 255]
|
|
27
|
-
lr = light_r = [ 0, 0,255, 0, 255]; hr = heavy_r = [ 0, -h,255, h, 255]
|
|
28
|
-
ld = light_d = [ 0, 0, 0,255, 255]; hd = heavy_d = [ -h, 0, h,255, 255]
|
|
29
|
-
|
|
30
|
-
hc = [ -h, -h, h, h, 255] # Heavy centre
|
|
31
|
-
|
|
32
|
-
d = 2
|
|
33
|
-
dblc = [-d+1, -d+1, d-1, d-1, 0] # Gap in centre of double lines.
|
|
34
|
-
light_vc = [0, -d, 0, d, 255] # Light vertical crossing centre of double line.
|
|
35
|
-
|
|
36
|
-
dh = double_h = [-255,-d,255,-d, 255] + [-255, d,255, d, 255]
|
|
37
|
-
dv = double_v = [-d,-255,-d,255, 255] + [d,-255,d,255, 255]
|
|
38
|
-
|
|
39
|
-
mask_vbar = [-d+1,-255,d-1,255,0]
|
|
40
|
-
mask_hbar = [-255,-d+1,255,d-1,0]
|
|
41
|
-
|
|
42
|
-
mt= masktop = [-255,-255, 255, -1, 0]; maskdtop = [-255,-255, 255,-d-1, 0]
|
|
43
|
-
ml=maskleft = [-255,-255, -1, 255, 0]; maskdleft = [-255,-255,-d-1, 255, 0]
|
|
44
|
-
mb=maskbottom = [-255, 1, 255, 255, 0]; maskdbottom= [-255, d+1, 255, 255, 0]
|
|
45
|
-
mr=maskright = [ 1,-255, 255, 255, 0]; maskdright = [ d+1,-255, 255, 255, 0]
|
|
46
|
-
|
|
47
|
-
mask_lbar = [-255,-d+1,d-1,d-1, 0]; dlbar=double_lbar = [-255,-d,d,d, 255]
|
|
48
|
-
mask_tbar = [-d+1,-255,d-1,d-1, 0]; dtbar=double_tbar = [-d,-255,d,d, 255]
|
|
49
|
-
mask_rbar = [-d+1,-d+1,255,d-1, 0]; drbar=double_rbar = [-d,-d,255,d, 255]
|
|
50
|
-
mask_dbar = [-d+1,-d+1,d-1,255, 0]; ddbar=double_dbar = [-d,-d,d,255, 255]
|
|
51
|
-
|
|
52
|
-
mask_c = [0,0,0,0,0]
|
|
53
|
-
|
|
54
|
-
hdl = heavy_dl = [heavy_d, heavy_l, hc]
|
|
55
|
-
hdr = heavy_dr = [heavy_d, heavy_r, hc]
|
|
56
|
-
hlu = heavy_lu = [heavy_l, heavy_u, hc]
|
|
57
|
-
hru = heavy_ru = [heavy_r, heavy_u, hc]
|
|
58
|
-
ldl = light_dl = [light_d, light_l]
|
|
59
|
-
ldr = light_dr = [light_d, light_r]
|
|
60
|
-
llu = light_lu = [light_l, light_u]
|
|
61
|
-
lru = light_ru = [light_r, light_u]
|
|
62
|
-
|
|
63
|
-
# FIXME Makes stipples wider when tx/ty etc. are big enough
|
|
64
|
-
tx = hx - boxw / 3
|
|
65
|
-
mv2 = mask_v2 = [[-tx, -255, -tx, 255, 0], [tx, -255, tx, 255,0]]
|
|
66
|
-
|
|
67
|
-
ty = hy - boxh / 3
|
|
68
|
-
mh2 = mask_h2 = [[-255,-ty, 255,-ty,0], [-255,ty,255,ty,0]]
|
|
69
|
-
|
|
70
|
-
rects = {
|
|
71
|
-
0x2500 => lh, 0x2501 => hh, 0x2502 => lv, 0x2503 => hv,
|
|
72
|
-
0x2504 => lh + mv2, 0x2505 => hh + mv2, 0x2506 => lv + mh2, 0x2507 => hv + mv2,
|
|
73
|
-
0x2508 => lh, # FIXME: Stippled
|
|
74
|
-
0x2509 => hh, # FIXME: Stippled
|
|
75
|
-
0x250A => lv, # FIXME: Stippled
|
|
76
|
-
0x250B => hv, # FIXME: Stippled
|
|
77
|
-
|
|
78
|
-
0x250C => ldr, 0x250D => hr + ld, 0x250E => hd + lr, 0x250F => hdr,
|
|
79
|
-
0x2510 => ldl, 0x2511 => hl + ld, 0x2512 => [hd, ll], 0x2513 => hdl,
|
|
80
|
-
0x2514 => lru, 0x2515 => lu + hr, 0x2516 => lr + hu, 0x2517 => hru,
|
|
81
|
-
0x2518 => llu, 0x2519 => [hl, lu], 0x251a => [ll, hu], 0x251b => hlu,
|
|
82
|
-
0x251c => lv+lr, 0x251d => [lv, hr], 0x251e => ldr + hu, 0x251f => [lru, hd],
|
|
83
|
-
|
|
84
|
-
0x2520 => hv + lr, 0x2521 => hru + ld, 0x2522 => hdr + lu, 0x2523 => hv + hr,
|
|
85
|
-
0x2524 => ll + lv, 0x2525 => hl + lv, 0x2526 => hu + ldl, 0x2527 => hd + lu,
|
|
86
|
-
0x2528 => hv + ll, 0x2529 => hlu + ld, 0x252a => hdl+lu, 0x252b => hl + hv,
|
|
87
|
-
0x252c => lh + ld, 0x252d => [hl, ldr], 0x252e => [hr, ldr], 0x252f => [hh, ld],
|
|
88
|
-
|
|
89
|
-
0x2530 => [lh, hd], 0x2531 => [hdl, lr], 0x2532 => [ll, hdr], 0x2533 => [hh, hd],
|
|
90
|
-
0x2534 => [lh, lu], 0x2535 => [lru, hl], 0x2536 => [llu, hr], 0x2537 => [hh, lu],
|
|
91
|
-
0x2538 => [lh, hu], 0x2539 => [hlu, lr], 0x253a => [hru, ll], 0x253b => [hh, hu],
|
|
92
|
-
0x253c => [lv, lh], 0x253d => lv+lr+hl, 0x253e => ll+lv+hr, 0x253f => [lv, hh],
|
|
93
|
-
|
|
94
|
-
0x2540 => [lh, ld, hu], 0x2541 => [light_h, light_u, heavy_d], 0x2542 => [heavy_v, light_h], 0x2543 => [heavy_l, heavy_u, hc, light_d, light_r],
|
|
95
|
-
0x2544 => [ll, ld, hru], 0x2545 => [heavy_dl, light_ru], 0x2546 => [light_lu, heavy_dr], 0x2547 => [heavy_h, heavy_u, light_d],
|
|
96
|
-
0x2548 => [hh, hd, light_u], 0x2549 => [heavy_v, heavy_l, light_r], 0x254a => [heavy_v, light_l, heavy_r], 0x254b => [heavy_v, heavy_h],
|
|
97
|
-
0x254c => [lh, dblc], 0x254d => [heavy_h, dblc], 0x254e => [light_v, dblc], 0x254f => [heavy_v, dblc],
|
|
98
|
-
|
|
99
|
-
0x2550 => dh, 0x2551 => dv, 0x2552 => [dh, lv, ml, maskdtop], 0x2553 => [dv, lh, mt, maskdleft],
|
|
100
|
-
0x2554 => [ddbar, drbar, mask_dbar, mask_rbar], 0x2555 => [dh, mr, lv, maskdtop],
|
|
101
|
-
0x2556 => [dv, lh,maskdright,mt], 0x2557 => [double_dbar, double_lbar, mask_dbar, mask_lbar],
|
|
102
|
-
0x2558 => [lv, dh, ml, maskdbottom], 0x2559 => [lh, dv, maskdleft, mb],
|
|
103
|
-
0x255A => [dtbar, double_rbar, mask_tbar, mask_rbar], 0x255B => [dh, lv, maskdbottom, mr],
|
|
104
|
-
0x255C => [dv, lh, mb, maskdright], 0x255D => [dtbar, dlbar, mask_tbar, mask_lbar],
|
|
105
|
-
0x255e => [lv, dh, maskleft], 0x255f => [lh, dv, dblc, maskdleft],
|
|
106
|
-
0x2560 => [dv, drbar, mask_rbar, mask_vbar], 0x2561 => [lv, dh, maskright],
|
|
107
|
-
0x2562 => [dv, ll, dblc], 0x2563 => [dlbar, dv, mask_lbar, mask_vbar],
|
|
108
|
-
0x2564 => [dh, ld, dblc], 0x2565 => [lh, dv, mt],
|
|
109
|
-
0x2566 => [dh, ddbar,mask_hbar, mask_dbar], 0x2567 => [dh, light_u,dblc],
|
|
110
|
-
0x2568 => [lh, dv, mb], 0x2569 => [dh, double_tbar, mask_hbar, mask_tbar],
|
|
111
|
-
0x256A => dh+lv, 0x256b => dv+lh, 0x256c => [dh, dv, mask_hbar, mask_vbar],
|
|
112
|
-
|
|
113
|
-
# 256d, 256e, 256f, 2570 => curves
|
|
114
|
-
# FIXME: Current is just very slightly rounded.
|
|
115
|
-
0x256d => [ldr, mask_c], 0x256e => [ldl, mask_c], 0x256f => [llu, mask_c], 0x2570 => [lru, mask_c],
|
|
116
|
-
|
|
117
|
-
# 2571, 2572. 2573 => diagonals, handled below.
|
|
118
|
-
0x2571 => nil, 0x2572 => nil, 0x2573 => nil,
|
|
119
|
-
|
|
120
|
-
0x2574 => ll, 0x2575 => lu, 0x2576 => lr, 0x2577 => ld,
|
|
121
|
-
0x2578 => hl, 0x2579 => hu, 0x257a => hr, 0x257b => hd,
|
|
122
|
-
0x257c => ll + hr, 0x257d => lu + hd, 0x257e => hl + lr, 0x257f => hu + ld
|
|
123
|
-
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
r = rects[ch.ord]
|
|
127
|
-
if r
|
|
128
|
-
img = empty_box_image
|
|
129
|
-
r.flatten.each_slice(5) do |rect|
|
|
130
|
-
p rect
|
|
131
|
-
x1,y1,x2,y2, col = *rect
|
|
132
|
-
x1 = (x1+hx).clamp(0,boxw-1)
|
|
133
|
-
x2 = (x2+hx).clamp(0,boxw-1)
|
|
134
|
-
y1 = (y1+hy).clamp(0,boxh-1)
|
|
135
|
-
y2 = (y2+hy).clamp(0,boxh-1)
|
|
136
|
-
col ||= 255
|
|
137
|
-
|
|
138
|
-
a = Array.new(x2-x1+1,col)
|
|
139
|
-
|
|
140
|
-
(y1..y2).each do |y|
|
|
141
|
-
img.pixels[y*stride + x1 .. y*stride + x2] = a
|
|
142
|
-
end
|
|
143
|
-
end
|
|
144
|
-
return img
|
|
145
|
-
end
|
|
146
|
-
|
|
147
|
-
if ch.ord == 0x2571
|
|
148
|
-
img = empty_box_image
|
|
149
|
-
slope = boxw.to_f / boxh
|
|
150
|
-
x = boxw.to_f-1
|
|
151
|
-
(0...boxh).each do |y|
|
|
152
|
-
err = x - x.to_i
|
|
153
|
-
img.pixels[y*stride+x.ceil] = 255 #(255*(1-err)).floor
|
|
154
|
-
img.pixels[y*stride+x.ceil-1] = (255*err).floor if x > 0
|
|
155
|
-
x-= slope
|
|
156
|
-
end
|
|
157
|
-
return img
|
|
158
|
-
end
|
|
159
|
-
|
|
160
|
-
# FIXME: If size is *at* certain levels level, this, and the next one fails?
|
|
161
|
-
|
|
162
|
-
if ch.ord == 0x2572
|
|
163
|
-
img = empty_box_image
|
|
164
|
-
slope = boxw.to_f / boxh
|
|
165
|
-
x = boxw.to_f-1
|
|
166
|
-
(0...boxh).each do |y|
|
|
167
|
-
err = x - x.to_i
|
|
168
|
-
img.pixels[y*stride+boxw-x.ceil-1] = 255
|
|
169
|
-
img.pixels[y*stride+boxw-x.ceil] = (255*err).floor if x > 0
|
|
170
|
-
x-= slope
|
|
171
|
-
end
|
|
172
|
-
return img
|
|
173
|
-
end
|
|
174
|
-
|
|
175
|
-
if ch.ord == 0x2573
|
|
176
|
-
img = empty_box_image
|
|
177
|
-
slope = boxw.to_f / boxh
|
|
178
|
-
x = boxw.to_f-1
|
|
179
|
-
(0...boxh).each do |y|
|
|
180
|
-
err = x - x.to_i
|
|
181
|
-
img.pixels[y*stride+boxw-x.ceil-1] = 255
|
|
182
|
-
img.pixels[y*stride+boxw-x.ceil] = (255*err).floor if x > 0
|
|
183
|
-
img.pixels[y*stride+x.ceil-1] = (255*err).floor if x > 0
|
|
184
|
-
img.pixels[y*stride+x.ceil] = 255 #(255*(1-err)).floor
|
|
185
|
-
x-= slope
|
|
186
|
-
end
|
|
187
|
-
return img
|
|
188
|
-
end
|
|
189
|
-
|
|
190
|
-
if img
|
|
191
|
-
img = img.dup
|
|
192
|
-
img.pixels = img.pixels.dup
|
|
193
|
-
end
|
|
194
|
-
img || empty_box_image
|
|
195
|
-
end
|
|
196
|
-
end
|
|
197
|
-
end
|
|
198
|
-
end
|