skrift-x11 0.2.1 → 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/example.rb +1 -1
- data/lib/skrift/x11/glyphs.rb +139 -92
- data/lib/skrift/x11/version.rb +1 -1
- data/skrift-x11.gemspec +43 -0
- metadata +19 -4
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/example.rb
CHANGED
|
@@ -52,7 +52,7 @@ f = Font.load("resources/FiraGO-Regular_extended_with_NotoSansEgyptianHieroglyph
|
|
|
52
52
|
$skrift = Skrift::X11::Glyphs.new(dpy, f, x_scale: 40, y_scale: 40)
|
|
53
53
|
|
|
54
54
|
def redraw(dpy, wid, gc)
|
|
55
|
-
dpy.poly_fill_rectangle(wid, gc, [
|
|
55
|
+
dpy.poly_fill_rectangle(wid, gc, [20,45, 400, 400])
|
|
56
56
|
$skrift.render_str($pic, 0xffffff, 50,90, 'Pure Ruby w/Skrift!')
|
|
57
57
|
$skrift.render_str($pic, 0xffffff, 50,140, "And unicode:")
|
|
58
58
|
$skrift.render_str($pic, 0xff00ff, 50,200, "Μπορώ να φάω σπασμένα γυαλιά χωρίς να πάθω τίποτα.")
|
data/lib/skrift/x11/glyphs.rb
CHANGED
|
@@ -1,40 +1,67 @@
|
|
|
1
|
+
require 'skrift/boxdrawing'
|
|
1
2
|
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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
|
|
9
|
+
|
|
10
|
+
module X11
|
|
11
|
+
# XRender constants currently not defined in pure-x11.
|
|
12
|
+
# FIXME: these are generic XRender enumerants and belong in pure-x11.
|
|
13
|
+
module Form
|
|
7
14
|
PictOpSrc=1
|
|
8
15
|
PictOpOver=3
|
|
9
16
|
CPRepeat = 1
|
|
17
|
+
end
|
|
18
|
+
end
|
|
10
19
|
|
|
20
|
+
module Skrift
|
|
21
|
+
module X11
|
|
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.
|
|
11
29
|
class Glyphs
|
|
30
|
+
attr_reader :fixed_width
|
|
31
|
+
attr_accessor :maxheight, :lm
|
|
12
32
|
|
|
13
|
-
|
|
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)
|
|
14
36
|
@dpy = dpy
|
|
15
|
-
@sft = SFT.new(font)
|
|
16
|
-
@sft.x_scale = x_scale
|
|
17
|
-
@sft.y_scale = y_scale
|
|
18
37
|
@pic = pic
|
|
19
|
-
@
|
|
38
|
+
@maxheight = maxheight
|
|
20
39
|
|
|
21
|
-
|
|
22
|
-
@
|
|
23
|
-
|
|
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)
|
|
24
49
|
|
|
25
|
-
@
|
|
50
|
+
@colcache = {}
|
|
51
|
+
@gids = {} # codepoint -> [:mono|:color, glyph-set id]
|
|
52
|
+
@nextgid = 1
|
|
26
53
|
|
|
27
|
-
# The glyph set
|
|
28
54
|
@gfmt = @dpy.render_find_standard_format(:a8).id
|
|
29
55
|
@glyphset = @dpy.render_create_glyph_set(@gfmt)
|
|
30
|
-
end
|
|
31
56
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
|
36
62
|
end
|
|
37
|
-
|
|
63
|
+
|
|
64
|
+
def inspect = "<Glyphs #{object_id}>"
|
|
38
65
|
|
|
39
66
|
def fill_for_col(col)
|
|
40
67
|
return @colcache[col] if @colcache[col]
|
|
@@ -44,89 +71,109 @@ module Skrift
|
|
|
44
71
|
g |= g << 8
|
|
45
72
|
b = col & 0xff
|
|
46
73
|
b |= b << 8
|
|
47
|
-
|
|
48
|
-
@colcache[col] ||= @dpy.render_create_solid_fill(r,g,b,0xffff)
|
|
74
|
+
@colcache[col] = @dpy.render_create_solid_fill(r, g, b, 0xffff)
|
|
49
75
|
end
|
|
50
76
|
|
|
51
|
-
def
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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)
|
|
59
97
|
end
|
|
60
98
|
end
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
#
|
|
68
|
-
#
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
99
|
+
|
|
100
|
+
private
|
|
101
|
+
|
|
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)
|
|
113
|
+
end
|
|
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])
|
|
76
118
|
else
|
|
77
|
-
|
|
119
|
+
@dpy.render_composite_glyphs32(:PictOpOver, fill_for_col(col), pic, @gfmt, @glyphset, 0, 0, [x, y, ids])
|
|
78
120
|
end
|
|
79
|
-
|
|
80
|
-
yoff = mtx.y_offset || baseline
|
|
81
|
-
|
|
82
|
-
info = ::X11::Form::GlyphInfo.new(
|
|
83
|
-
img.width, # w
|
|
84
|
-
img.height, # h
|
|
85
|
-
-mtx.left_side_bearing, # x
|
|
86
|
-
yoff-baseline, # y
|
|
87
|
-
fixed_width || mtx.advance_width, # || mtx.advance_width, # x_off
|
|
88
|
-
0
|
|
89
|
-
)
|
|
90
|
-
|
|
91
|
-
@dpy.render_add_glyphs(@glyphset, gid, info, data)
|
|
92
|
-
@glyphcache[gid] = mtx.advance_width#-mtx.left_side_bearing
|
|
93
121
|
end
|
|
94
122
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
gl.inject(0) {|sum,gl|
|
|
101
|
-
@glyphcache[gl].to_i + sum
|
|
102
|
-
}
|
|
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)
|
|
103
128
|
end
|
|
104
|
-
|
|
105
|
-
def
|
|
106
|
-
#
|
|
107
|
-
# glyph
|
|
108
|
-
|
|
109
|
-
|
|
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?
|
|
135
|
+
|
|
136
|
+
g = @cache.glyph(cp)
|
|
137
|
+
return nil unless g
|
|
138
|
+
|
|
139
|
+
gsgid = @nextgid
|
|
140
|
+
@nextgid += 1
|
|
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]
|
|
147
|
+
else
|
|
148
|
+
upload_blank # no outline (e.g. space)
|
|
110
149
|
end
|
|
111
150
|
end
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
151
|
+
|
|
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]
|
|
120
159
|
end
|
|
121
|
-
|
|
122
|
-
def
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
160
|
+
|
|
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)
|
|
165
|
+
end
|
|
166
|
+
|
|
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<*")
|
|
130
177
|
end
|
|
131
178
|
end
|
|
132
179
|
end
|
data/lib/skrift/x11/version.rb
CHANGED
data/skrift-x11.gemspec
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "lib/skrift/x11/version"
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = "skrift-x11"
|
|
7
|
+
spec.version = Skrift::X11::VERSION
|
|
8
|
+
spec.authors = ["Vidar Hokstad"]
|
|
9
|
+
spec.email = ["vidar@hokstad.com"]
|
|
10
|
+
|
|
11
|
+
spec.summary = "Helpers to use the pure-Ruvy TrueType engine Skrift with pure Pure-X11 X bindings"
|
|
12
|
+
#spec.description = "TODO: Write a longer description or delete this line."
|
|
13
|
+
#spec.homepage = "TODO: Put your gem's website or public repo URL here."
|
|
14
|
+
spec.license = "MIT"
|
|
15
|
+
spec.required_ruby_version = ">= 2.6.0"
|
|
16
|
+
|
|
17
|
+
#spec.metadata["allowed_push_host"] = "TODO: Set to your gem server 'https://example.com'"
|
|
18
|
+
|
|
19
|
+
#spec.metadata["homepage_uri"] = spec.homepage
|
|
20
|
+
#spec.metadata["source_code_uri"] = "TODO: Put your gem's public repo URL here."
|
|
21
|
+
#spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
|
|
22
|
+
|
|
23
|
+
# Specify which files should be added to the gem when it is released.
|
|
24
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
|
25
|
+
spec.files = Dir.chdir(__dir__) do
|
|
26
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
|
27
|
+
(File.expand_path(f) == __FILE__) ||
|
|
28
|
+
f.start_with?(*%w[bin/ test/ spec/ features/ .git .circleci appveyor Gemfile])
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
spec.bindir = "exe"
|
|
32
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
|
33
|
+
spec.require_paths = ["lib"]
|
|
34
|
+
|
|
35
|
+
# Uncomment to register a new dependency of your gem
|
|
36
|
+
# spec.add_dependency "example-gem", "~> 1.0"
|
|
37
|
+
spec.add_dependency "skrift", ">= 0.3.0"
|
|
38
|
+
spec.add_dependency "skrift-boxdrawing", ">= 0.2.0"
|
|
39
|
+
spec.add_dependency "pure-x11"
|
|
40
|
+
|
|
41
|
+
# For more information and examples about making a new gem, check out our
|
|
42
|
+
# guide at: https://bundler.io/guides/creating_gem.html
|
|
43
|
+
end
|
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:
|
|
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
|
|
@@ -55,6 +69,7 @@ files:
|
|
|
55
69
|
- lib/skrift/x11/version.rb
|
|
56
70
|
- resources/FiraGO-Regular_extended_with_NotoSansEgyptianHieroglyphs-Regular.ttf
|
|
57
71
|
- sig/skrift/x11.rbs
|
|
72
|
+
- skrift-x11.gemspec
|
|
58
73
|
homepage:
|
|
59
74
|
licenses:
|
|
60
75
|
- MIT
|