1if not modules then modules = { } end modules [ ' font-def ' ] = {
2 version = 1 . 001 ,
3 comment = " companion to font-ini.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
10
11local lower , gsub = string . lower , string . gsub
12local tostring , next = tostring , next
13local lpegmatch = lpeg . match
14local suffixonly , removesuffix , basename = file . suffix , file . removesuffix , file . basename
15local formatters = string . formatters
16local sortedhash , sortedkeys = table . sortedhash , table . sortedkeys
17
18local allocate = utilities . storage . allocate
19
20local trace_defining = false trackers . register ( " fonts.defining " , function ( v ) trace_defining = v end )
21local directive_embedall = false directives . register ( " fonts.embedall " , function ( v ) directive_embedall = v end )
22
23trackers . register ( " fonts.loading " , " fonts.defining " , " otf.loading " , " afm.loading " , " tfm.loading " )
24
25local report_defining = logs . reporter ( " fonts " , " defining " )
26
27
31
32local fonts = fonts
33local fontdata = fonts . hashes . identifiers
34local readers = fonts . readers
35local definers = fonts . definers
36local specifiers = fonts . specifiers
37local constructors = fonts . constructors
38local fontgoodies = fonts . goodies
39
40readers . sequence = allocate { ' otf ' , ' ttf ' , ' afm ' , ' tfm ' , ' lua ' }
41
42local variants = allocate ( )
43specifiers . variants = variants
44
45definers . methods = definers . methods or { }
46
47local internalized = allocate ( )
48
49local loadedfonts = constructors . loadedfonts
50local designsizes = constructors . designsizes
51
52
53
54local resolvefile = fontgoodies and fontgoodies . filenames and fontgoodies . filenames . resolve or function ( s ) return s end
55
56
62
63
75
76
77
78
79
80
81
82local function makespecification ( specification , lookup , name , sub , method , detail , size )
83 size = size or 655360
84 if not lookup or lookup = = " " then
85 lookup = definers . defaultlookup
86 end
87 if trace_defining then
88 report_defining ( " specification %a, lookup %a, name %a, sub %a, method %a, detail %a " ,
89 specification , lookup , name , sub , method , detail )
90 end
91 local t = {
92 lookup = lookup ,
93 specification = specification ,
94 size = size ,
95 name = name ,
96 sub = sub ,
97 method = method ,
98 detail = detail ,
99 resolved = " " ,
100 forced = " " ,
101 features = { } ,
102 }
103 return t
104end
105
106definers . makespecification = makespecification
107
108if context then
109
110 local splitter , splitspecifiers = nil , " "
111
112 local P , C , S , Cc , Cs = lpeg . P , lpeg . C , lpeg . S , lpeg . Cc , lpeg . Cs
113
114 local left = P ( " ( " )
115 local right = P ( " ) " )
116 local colon = P ( " : " )
117 local space = P ( " " )
118 local lbrace = P ( " { " )
119 local rbrace = P ( " } " )
120
121 definers . defaultlookup = " file "
122
123 local prefixpattern = P ( false )
124
125 local function addspecifier ( symbol )
126 splitspecifiers = splitspecifiers . . symbol
127 local method = S ( splitspecifiers )
128 local lookup = C ( prefixpattern ) * colon
129 local sub = left * C ( P ( 1 - left - right - method ) ^ 1 ) * right
130 local specification = C ( method ) * C ( P ( 1 ) ^ 1 )
131 local name = Cs ( ( lbrace / " " ) * ( 1 - rbrace ) ^ 1 * ( rbrace / " " ) + ( 1 - sub - specification ) ^ 1 )
132 splitter = P ( ( lookup + Cc ( " " ) ) * name * ( sub + Cc ( " " ) ) * ( specification + Cc ( " " ) ) )
133 end
134
135 local function addlookup ( str )
136 prefixpattern = prefixpattern + P ( str )
137 end
138
139 definers . addlookup = addlookup
140
141 addlookup ( " file " )
142 addlookup ( " name " )
143 addlookup ( " spec " )
144
145 local function getspecification ( str )
146 return lpegmatch ( splitter , str or " " )
147 end
148
149 definers . getspecification = getspecification
150
151 function definers . registersplit ( symbol , action , verbosename )
152 addspecifier ( symbol )
153 variants [ symbol ] = action
154 if verbosename then
155 variants [ verbosename ] = action
156 end
157 end
158
159 function definers . analyze ( specification , size )
160
161 local lookup , name , sub , method , detail = getspecification ( specification or " " )
162 return makespecification ( specification , lookup , name , sub , method , detail , size )
163 end
164
165end
166
167
170
171definers . resolvers = definers . resolvers or { }
172local resolvers = definers . resolvers
173
174
175
176function resolvers . file ( specification )
177 local name = resolvefile ( specification . name )
178 local suffix = lower ( suffixonly ( name ) )
179 if fonts . formats [ suffix ] then
180 specification . forced = suffix
181 specification . forcedname = name
182 specification . name = removesuffix ( name )
183 else
184 specification . name = name
185 end
186end
187
188function resolvers . name ( specification )
189 local resolve = fonts . names . resolve
190 if resolve then
191 local resolved , sub , subindex , instance = resolve ( specification . name , specification . sub , specification )
192 if resolved then
193 specification . resolved = resolved
194 specification . sub = sub
195 specification . subindex = subindex
196
197 if instance then
198 specification . instance = instance
199 local features = specification . features
200 if not features then
201 features = { }
202 specification . features = features
203 end
204 local normal = features . normal
205 if not normal then
206 normal = { }
207 features . normal = normal
208 end
209 normal . instance = instance
210 end
211
212 local suffix = lower ( suffixonly ( resolved ) )
213 if fonts . formats [ suffix ] then
214 specification . forced = suffix
215 specification . forcedname = resolved
216 specification . name = removesuffix ( resolved )
217 else
218 specification . name = resolved
219 end
220 end
221 else
222 resolvers . file ( specification )
223 end
224end
225
226function resolvers . spec ( specification )
227 local resolvespec = fonts . names . resolvespec
228 if resolvespec then
229 local resolved , sub , subindex = resolvespec ( specification . name , specification . sub , specification )
230 if resolved then
231 specification . resolved = resolved
232 specification . sub = sub
233 specification . subindex = subindex
234 specification . forced = lower ( suffixonly ( resolved ) )
235 specification . forcedname = resolved
236 specification . name = removesuffix ( resolved )
237 end
238 else
239 resolvers . name ( specification )
240 end
241end
242
243function definers . resolve ( specification )
244 if not specification . resolved or specification . resolved = = " " then
245 local r = resolvers [ specification . lookup ]
246 if r then
247 r ( specification )
248 end
249 end
250 if specification . forced = = " " then
251 specification . forced = nil
252 specification . forcedname = nil
253 end
254 specification . hash = lower ( specification . name . . ' @ ' . . constructors . hashfeatures ( specification ) )
255 if specification . sub and specification . sub ~ = " " then
256 specification . hash = specification . sub . . ' @ ' . . specification . hash
257 end
258 return specification
259end
260
261
276
277
278
279function definers . applypostprocessors ( tfmdata )
280 local postprocessors = tfmdata . postprocessors
281 if postprocessors then
282 local properties = tfmdata . properties
283 for i = 1 , # postprocessors do
284 local extrahash = postprocessors [ i ] ( tfmdata )
285 if type ( extrahash ) = = " string " and extrahash ~ = " " then
286
287 extrahash = gsub ( lower ( extrahash ) , " [^a-z] " , " - " )
288 properties . fullname = formatters [ " %s-%s " ] ( properties . fullname , extrahash )
289 end
290 end
291 end
292 return tfmdata
293end
294
295
296
297
298
299local function checkembedding ( tfmdata )
300 local properties = tfmdata . properties
301 local embedding
302 if directive_embedall then
303 embedding = " full "
304 elseif properties and properties . filename and constructors . dontembed [ properties . filename ] then
305 embedding = " no "
306 else
307 embedding = " subset "
308 end
309 if properties then
310 properties . embedding = embedding
311 else
312 tfmdata . properties = { embedding = embedding }
313 end
314 tfmdata . embedding = embedding
315end
316
317local function checkfeatures ( tfmdata )
318 local resources = tfmdata . resources
319 local shared = tfmdata . shared
320 if resources and shared then
321 local features = resources . features
322 local usedfeatures = shared . features
323 if features and usedfeatures then
324 local usedlanguage = usedfeatures . language or " dflt "
325 local usedscript = usedfeatures . script or " dflt "
326 local function check ( what )
327 if what then
328 local foundlanguages = { }
329 for feature , scripts in next , what do
330 if usedscript = = " auto " or scripts [ " * " ] then
331
332 elseif not scripts [ usedscript ] then
333
334
335 else
336 for script , languages in next , scripts do
337 if languages [ " * " ] then
338
339 elseif context and not languages [ usedlanguage ] then
340 report_defining ( " font %!font:name!, feature %a, script %a, no language %a " ,
341 tfmdata , feature , script , usedlanguage )
342 end
343 end
344 end
345 for script , languages in next , scripts do
346 for language in next , languages do
347 foundlanguages [ language ] = true
348 end
349 end
350 end
351 if false then
352 foundlanguages [ " * " ] = nil
353 foundlanguages = sortedkeys ( foundlanguages )
354 for feature , scripts in sortedhash ( what ) do
355 for script , languages in next , scripts do
356 if not languages [ " * " ] then
357 for i = 1 , # foundlanguages do
358 local language = foundlanguages [ i ]
359 if context and not languages [ language ] then
360 report_defining ( " font %!font:name!, feature %a, script %a, no language %a " ,
361 tfmdata , feature , script , language )
362 end
363 end
364 end
365 end
366 end
367 end
368 end
369 end
370 check ( features . gsub )
371 check ( features . gpos )
372 end
373 end
374end
375
376function definers . loadfont ( specification )
377 local hash = constructors . hashinstance ( specification )
378
379 local tfmdata = loadedfonts [ hash ]
380 if not tfmdata then
381
382 local forced = specification . forced or " "
383 if forced ~ = " " then
384 local reader = readers [ lower ( forced ) ]
385 tfmdata = reader and reader ( specification )
386 if not tfmdata then
387 report_defining ( " forced type %a of %a not found " , forced , specification . name )
388 end
389 else
390 local sequence = readers . sequence
391 for s = 1 , # sequence do
392 local reader = sequence [ s ]
393 if readers [ reader ] then
394 if trace_defining then
395 report_defining ( " trying (reader sequence driven) type %a for %a with file %a " , reader , specification . name , specification . filename )
396 end
397 tfmdata = readers [ reader ] ( specification )
398 if tfmdata then
399 break
400 else
401 specification . filename = nil
402 end
403 end
404 end
405 end
406 if tfmdata then
407 tfmdata = definers . applypostprocessors ( tfmdata )
408 checkembedding ( tfmdata )
409 loadedfonts [ hash ] = tfmdata
410 designsizes [ specification . hash ] = tfmdata . parameters . designsize
411 checkfeatures ( tfmdata )
412 end
413 end
414 if not tfmdata then
415 report_defining ( " font with asked name %a is not found using lookup %a " , specification . name , specification . lookup )
416 end
417 return tfmdata
418end
419
420function constructors . readanddefine ( name , size )
421 local specification = definers . analyze ( name , size )
422 local method = specification . method
423 if method and variants [ method ] then
424 specification = variants [ method ] ( specification )
425 end
426 specification = definers . resolve ( specification )
427 local hash = constructors . hashinstance ( specification )
428 local id = definers . registered ( hash )
429 if not id then
430 local tfmdata = definers . loadfont ( specification )
431 if tfmdata then
432 tfmdata . properties . hash = hash
433 id = font . define ( tfmdata )
434 definers . register ( tfmdata , id )
435 else
436 id = 0
437 end
438 end
439 return fontdata [ id ] , id
440end
441
442
453
454function definers . registered ( hash )
455 local id = internalized [ hash ]
456 return id , id and fontdata [ id ]
457end
458
459function definers . register ( tfmdata , id )
460 if tfmdata and id then
461 local hash = tfmdata . properties . hash
462 if not hash then
463 report_defining ( " registering font, id %a, name %a, invalid hash " , id , tfmdata . properties . filename or " ? " )
464 elseif not internalized [ hash ] then
465 internalized [ hash ] = id
466 if trace_defining then
467 report_defining ( " registering font, id %s, hash %a " , id , hash )
468 end
469 fontdata [ id ] = tfmdata
470 end
471 end
472end
473
474function definers . read ( specification , size , id )
475 statistics . starttiming ( fonts )
476 if type ( specification ) = = " string " then
477 specification = definers . analyze ( specification , size )
478 end
479 local method = specification . method
480 if method and variants [ method ] then
481 specification = variants [ method ] ( specification )
482 end
483 specification = definers . resolve ( specification )
484 local hash = constructors . hashinstance ( specification )
485 local tfmdata = definers . registered ( hash )
486 if tfmdata then
487 if trace_defining then
488 report_defining ( " already hashed: %s " , hash )
489 end
490 else
491 tfmdata = definers . loadfont ( specification )
492 if tfmdata then
493 if trace_defining then
494 report_defining ( " loaded and hashed: %s " , hash )
495 end
496 tfmdata . properties . hash = hash
497 if id then
498 definers . register ( tfmdata , id )
499 end
500 else
501 if trace_defining then
502 report_defining ( " not loaded and hashed: %s " , hash )
503 end
504 end
505 end
506 if not tfmdata then
507 report_defining ( " unknown font %a, loading aborted " , specification . name )
508 elseif trace_defining and type ( tfmdata ) = = " table " then
509 local properties = tfmdata . properties or { }
510 local parameters = tfmdata . parameters or { }
511 report_defining ( " using %a font with id %a, name %a, size %a, bytes %a, encoding %a, fullname %a, filename %a " ,
512 properties . format or " unknown " , id or " - " , properties . name , parameters . size , properties . encodingbytes ,
513 properties . encodingname , properties . fullname , basename ( properties . filename ) )
514 end
515 statistics . stoptiming ( fonts )
516 return tfmdata
517end
518
519function font . getfont ( id )
520 return fontdata [ id ]
521end
522
523
526
527if not context then
528 callbacks . register ( ' define_font ' , definers . read , " definition of fonts (tfmdata preparation) " )
529end
530 |