1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
|
-------------------------------------------------------------------------------
-- FILE: luaotfload-dvi.lua
-- DESCRIPTION: part of luaotfload / DVI
-------------------------------------------------------------------------------
assert(luaotfload_module, "This is a part of luaotfload and should not be loaded independently") {
name = "luaotfload-dvi",
version = "3.28", --TAGVERSION
date = "2024-02-14", --TAGDATE
description = "luaotfload submodule / DVI",
license = "GPL v2.0",
author = "Marcel Krüger",
copyright = "Luaotfload Development Team",
}
local getfont = font.getfont
local setfont = node.direct.setfont
local getdisc = node.direct.getdisc
local getlist = node.direct.getlist
local is_glyph = node.direct.is_glyph
local todirect = node.direct.todirect
local traverse_glyph = node.direct.traverse_glyph
local traverse_list = node.direct.traverse_list
local traverse_id = node.direct.traverse_id
local traverse = node.direct.traverse
local uses_font = node.direct.uses_font
local define_font = font.define
local hlist_t = node.id'hlist'
local vlist_t = node.id'vlist'
local disc_t = node.id'disc'
local glyph_t = node.id'glyph'
require'luaotfload-configuration'
local configuration = config.luaotfload
-- DVI output support
--
-- When writing DVI files, we can't assume that the DVI reader has access to our
-- font dictionaries, so we need an independent representation for our glyphs. The
-- approach we chose in coordination with the dvisvgm author is to create a DVI font
-- name which indicates the font filename and some essential backend parameters
-- (especially otential extend, shrink, embolden, etc. factors) and then use GIDs in
-- the page stream.
--
-- Normally we could easily implement this using virtual fonts, but because we are
-- dealing with DVI output, LuaTeX is not evaluating virtual fonts by itself.
-- So instead, we process the shaped output and replace all glyph nodes with glyph
-- nodes from special fonts which have the characteristics expected brom the DVI reader.
-- mapped_fonts maps fontids from the user to fontids used in the DVI file
local mapped_fonts = setmetatable({}, {__index = function(t, fid)
local font = getfont(fid)
local mapped = font and font.backend_font or false
t[fid] = mapped
return mapped
end})
local full_hprocess
local function full_vprocess(head)
for _, id, _, list in traverse_list(head) do
if id == hlist_t then
full_hprocess(list)
elseif id == vlist_t then
full_vprocess(list)
else
assert(false)
end
end
end
function full_hprocess(head)
local last_f, last_mapping, last_mapped_font
for n, id in traverse(head) do
if id == hlist_t then
full_hprocess(getlist(n))
elseif id == vlist_t then
full_vprocess(getlist(n))
elseif id == disc_t then
local _, _, rep = getdisc(n)
for n, c, f in traverse_glyph(rep) do
if f ~= last_f then
last_f, last_mapping = f, mapped_fonts[f]
last_mapped_font = last_mapping and last_mapping.font
end
if last_mapping then
local mapped = last_mapping[c]
if mapped then setfont(n, last_mapped_font, mapped) end
end
end
elseif id == glyph_t then
local c, f = is_glyph(n)
if f ~= last_f then
last_f, last_mapping = f, mapped_fonts[f]
last_mapped_font = last_mapping and last_mapping.font
end
if last_mapping then
local mapped = last_mapping[c]
if mapped then setfont(n, last_mapped_font, mapped) end
end
end
end
end
local function delayed_register_callback()
luatexbase.add_to_callback('pre_shipout_filter', function(list)
full_vprocess(todirect(list)) -- list should always be a single list node, so we can treat it as if it were a vlist with one node
return true
end, 'luaotfload.dvi')
delayed_register_callback = function() end
end
local function process(head, font)
local mapping = mapped_fonts[font]
if not mapping then return head end
local mapped_font = mapping.font
for n, c, f in traverse_glyph(head) do if f == font then
local mapped = mapping[c]
if mapped then setfont(n, mapped_font, mapped) end
end end
for n in traverse_id(disc_t, head) do if uses_font(n, font) then
local pre, post, rep = getdisc(n)
for n, c, f in traverse_glyph(pre) do if f == font then
local mapped = mapping[c]
if mapped then setfont(n, mapped_font, mapped) end
end end
for n, c, f in traverse_glyph(post) do if f == font then
local mapped = mapping[c]
if mapped then setfont(n, mapped_font, mapped) end
end end
for n, c, f in traverse_glyph(rep) do if f == font then
local mapped = mapping[c]
if mapped then setfont(n, mapped_font, mapped) end
end end
end end
end
local function manipulate(tfmdata, _, dvi_kind)
if dvi_kind == 'xdvipsk' then
-- xdvipsk wants to read tea leaves instead of using reasonable interfaces.
-- They will have to make sense of whatever output this produces.
return
elseif dvi_kind ~= 'dvisvgm' then
texio.write_nl(string.format('WARNING (luaotfload): Unsupported DVI backend %q, falling back to dvisvgm.', dvi_kind))
end
-- Legacy fonts can be written to the DVI file directly
if 2 ~= (tfmdata.encodingbytes or ((tfmdata.format == 'truetype' or tfmdata.format == 'opentype') and 2 or 1)) then
return
end
delayed_register_callback()
local newfont = {}
for k,v in pairs(tfmdata) do
newfont[k] = v
end
local newchars = {}
newfont.characters = newchars
local lookup = {}
for k,v in pairs(tfmdata.characters) do
local newchar = {
width = v.width, -- Only width should really be necessary
height = v.height,
depth = v.depth,
}
newchars[v.index or k] = newchar
lookup[k] = v.index or k
end
newfont.checksum = string.unpack('>I4', 'LuaF') -- Use a magic checksum such that the reader
-- can uniquely identify these fonts.
local name = '[' .. newfont.name .. ']:' -- TODO: Why .name? I would have expected .filename
if newfont.subfont and newfont.subfont ~= 1 then
name = name .. 'index=' .. tostring(math.tointeger(newfont.subfont-1)) .. ';'
end
if newfont.extend and newfont.extend ~= 1000 then
name = name .. 'extend=' .. tostring(math.tointeger(math.round(newfont.extend*65.536))) .. ';'
end
if newfont.slant and newfont.slant ~= 0 then
name = name .. 'slant=' .. tostring(math.tointeger(math.round(newfont.slant*65.536))) .. ';'
end
if newfont.mode == 2 and newfont.width and newfont.width ~= 0 then
name = name .. 'embolden=' .. tostring(math.tointeger(math.round(newfont.width*65.78176))) .. ';'
end
newfont.name = name:sub(1,-2) -- Stri th trailing : or ;
tfmdata.backend_font = lookup
lookup.font = define_font(newfont)
end
fonts.constructors.features.otf.register {
name = "dvifont",
default = configuration.run.default_dvi_driver,
manipulators = {
node = manipulate,
base = manipulate,
},
-- Processors are not needed since we are using pre_shipout_filter
-- processors = {
-- node = process,
-- base = process,
-- },
}
|