back-exp-imp-fnt.lmt /size: 6780 b    last modification: 2025-02-21 11:03
1if not modules then modules = { } end modules ['back-exp-imp-fnt'] = {
2    version   = 1.001,
3    comment   = "companion to back-exp.mkiv",
4    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
5    copyright = "PRAGMA ADE / ConTeXt Development Team",
6    license   = "see context related readme files"
7}
8
9-- font-style : oblique 25deg ;
10
11-- After many years of using fonts and also looking at the css spec for dealing with
12-- missing fonts we decided to just go for filenames and putting fonts in a
13-- predictable place. Names are often a mess and all this fallback when can't be
14-- downloaded doesn't serve our purpose. We're not talking fancy websites here but
15-- predictable documents.
16--
17-- We also assume that fonts are taken from the regular set of free fonts that tex
18-- distributions ship.
19
20local sortedhash, concat = table.sortedhash, table.concat
21local formatters = string.formatters
22local addsuffix, replacesuffix = file.addsuffix, file.replacesuffix
23
24local structurestags = structures.tags
25local backend        = structurestags.backend
26
27do
28
29    local s_fontface_b <const> = "@font-face {"
30    local f_fontface_f         = formatters["  font-family: %q ;"]
31    local f_fontface_s         = formatters["  src: %, t ;"]
32    local f_fontface_u         = formatters['url("%s")']
33    local s_fontface_e <const> = "}"
34
35    local mapping = {
36        Serif = {
37            normal         = "SerifNormal",
38            bold           = "SerifBold",
39            italic         = "SerifItalic",
40            bolditalic     = "SerifBoldItalic",
41            TextNormal     = "SerifNormal",
42            TextBold       = "SerifBold",
43            TextItalic     = "SerifItalic",
44            TextBoldItalic = "SerifBoldItalic",
45        },
46        Sans = {
47            normal         = "SansNormal",
48            bold           = "SansBold",
49            italic         = "SansItalic",
50            bolditalic     = "SansBoldItalic",
51            TextNormal     = "SansNormal",
52            TextBold       = "SansBold",
53            TextItalic     = "SansItalic",
54            TextBoldItalic = "SansBoldItalic",
55        },
56        Mono = {
57            normal         = "MonoNormal",
58            bold           = "MonoBold",
59            italic         = "MonoItalic",
60            bolditalic     = "MonoBoldItalic",
61            TextNormal     = "MonoNormal",
62            TextBold       = "MonoBold",
63            TextItalic     = "MonoItalic",
64            TextBoldItalic = "MonoBoldItalic",
65        },
66    }
67
68    -- todo: multiple paths ../tex/texmf/fonts/...
69
70    local defaults = {
71        base    = { "dejavu" },
72        default = "Serif",
73        path    = "../fonts/",
74        woff2   = true,
75        list    = {
76            Serif = { font = "DejavuSerif" },
77            Sans  = { font = "DejavuSans"  },
78            Mono  = { font = "DejavuMono"  },
79            Math  = { font = "DejavuMath"  },
80        },
81    }
82
83 -- structures.tags.setupexport {
84 --     fontfaces = {
85 --         base    = { "dejavu", "bonum" },
86 --         default = "Serif",
87 --         path    = "../fonts/",
88 --         woff2   = true,
89 --         list    = {
90 --             Serif = { font = "Bonum"       },
91 --             Mono  = { font = "DejavuMono"  },
92 --             Sans  = { font = "DejavuSans"  },
93 --             Math  = { font = "BonumMath"   },
94 --         },
95 --     }
96 -- }
97
98    function backend.tofontface(specification)
99        if not specification then
100            specification = defaults
101        end
102        local base = specification.base
103        local list = specification.list
104        if not list then
105            return ""
106        end
107        if type(base) == "string" then
108            base = { base }
109        elseif type(base) ~= "table" then
110            return ""
111        end
112        local known = { }
113        local used  = { }
114        for i=1,#base do
115            local data = dofile(resolvers.findfile("back-exp-imp-"..base[i]..".lfg"))
116            if data then
117                local faces = data.fontfaces
118                if faces then
119                    for k, v in next, faces do
120                        known[k] = v
121                    end
122                end
123            end
124        end
125        if next(known) then
126            local result  = { }
127            local r       = 0
128            local path    = specification.path or ""
129            local woff2   = specification.woff2
130            local default = specification.default or "Serif"
131            local hash    = { }
132    --
133            local function define(alternative,specification,path,woff2)
134                local list = { } -- for now only one
135                --
136                local filename  = addsuffix(specification.filename,"otf")
137                local woffname  = replacesuffix(filename,"woff2")
138                local filefound = resolvers.findfile(filename) or ""
139                local wofffound = resolvers.findfile(woffname) or ""
140                if woff2 and wofffound ~= "" then
141                    local fullname = path .. woffname
142                    list[#list+1] = f_fontface_u(fullname)
143                    used[fullname] = { path, woffname, wofffound }
144                else
145                    local fullname = path .. filename
146                    list[#list+1] = f_fontface_u(fullname)
147                    used[fullname] = { path, filename, filefound }
148                end
149                --
150                r = r + 1 ; result[r] = s_fontface_b
151                r = r + 1 ; result[r] = f_fontface_f(alternative)
152                r = r + 1 ; result[r] = f_fontface_s(list)
153                r = r + 1 ; result[r] = s_fontface_e
154            end
155
156            for set, spec in sortedhash(list) do
157                local font = spec.font
158                if font then
159                    local faces = known[font]
160                    if faces then
161                        for alternative, specification in sortedhash(faces) do
162                            define(alternative,specification,path,woff2)
163                            hash[alternative] = specification
164                        end
165                    end
166                end
167            end
168
169            local dl = mapping[default]
170            if dl then
171                for alternative, spec in sortedhash(dl) do
172                    local valid = hash[spec]
173                    if valid then
174                        define(alternative,valid,path,woff2)
175                    end
176                end
177            end
178            return concat(result,"\n"), used
179        end
180    end
181
182    local direct = {
183        normal     = { family = "normal" },
184        bold       = { family = "bold" },
185        italic     = { family = "italic" },
186        bolditalic = { family = "bolditalic" },
187    }
188
189    function backend.fontfaced(s)
190        return direct[s]
191    end
192
193end
194