1if not modules then modules = { } end modules ['font-mps'] = {
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
9local type, tonumber, tostring = type, tonumber, tostring
10local concat, insert, remove = table.concat, table.insert, table.remove
11local formatters, match = string.formatters, string.match
12local utfbyte = utf.byte
13
14
15
16
17
18
19
20
21
22fonts = fonts or { }
23local metapost = fonts.metapost or { }
24fonts.metapost = metapost
25
26local f_moveto = formatters["(%N,%N)"]
27local f_lineto = formatters["--(%N,%N)"]
28local f_curveto = formatters["..controls(%N,%N)and(%N,%N)..(%N,%N)"]
29local s_cycle = "--cycle"
30
31local f_nofill = formatters["nofill %s;"]
32local f_dofill = formatters["fill %s;"]
33
34local f_draw_trace = formatters["drawpathonly %s;"]
35local f_draw = formatters["draw %s;"]
36
37local f_rectangle = formatters["((%N,%N)--(%N,%N)--(%N,%N)--(%N,%N)--cycle)"]
38local f_line = formatters["((%N,%N)--(%N,%N))"]
39
40function metapost.boundingbox(d,factor)
41 local bounds = d.boundingbox
42 local factor = factor or 1
43 local llx = factor*bounds[1]
44 local lly = factor*bounds[2]
45 local urx = factor*bounds[3]
46 local ury = factor*bounds[4]
47 return f_rectangle(llx,lly,urx,lly,urx,ury,llx,ury)
48end
49
50function metapost.baseline(d,factor)
51 local bounds = d.boundingbox
52 local factor = factor or 1
53 local llx = factor*bounds[1]
54 local urx = factor*bounds[3]
55 return f_line(llx,0,urx,0)
56end
57
58function metapost.widthline(d,factor)
59 local bounds = d.boundingbox
60 local factor = factor or 1
61 local lly = factor*bounds[2]
62 local ury = factor*bounds[4]
63 local width = factor*d.width
64 return f_line(width,lly,width,ury)
65end
66
67function metapost.zeroline(d,factor)
68 local bounds = d.boundingbox
69 local factor = factor or 1
70 local lly = factor*bounds[2]
71 local ury = factor*bounds[4]
72 return f_line(0,lly,0,ury)
73end
74
75function metapost.paths(d,xfactor,yfactor)
76 local sequence = d.sequence
77 local segments = d.segments
78 local list = { }
79 local path = { }
80 local size = 0
81 local xfactor = xfactor or 1
82 local yfactor = yfactor or xfactor
83 if sequence then
84 local i = 1
85 local n = #sequence
86 while i < n do
87 local operator = sequence[i]
88 if operator == "m" then
89 if size > 0 then
90 size = size + 1
91 path[size] = s_cycle
92 list[#list+1] = concat(path,"",1,size)
93 size = 1
94 else
95 size = size + 1
96 end
97 path[size] = f_moveto(xfactor*sequence[i+1],yfactor*sequence[i+2])
98 i = i + 3
99 elseif operator == "l" then
100 size = size + 1
101 path[size] = f_lineto(xfactor*sequence[i+1],yfactor*sequence[i+2])
102 i = i + 3
103 elseif operator == "c" then
104 size = size + 1
105 path[size] = f_curveto(xfactor*sequence[i+1],yfactor*sequence[i+2],xfactor*sequence[i+3],yfactor*sequence[i+4],xfactor*sequence[i+5],yfactor*sequence[i+6])
106 i = i + 7
107 elseif operator =="q" then
108 size = size + 1
109
110 local l_x = xfactor*sequence[i-2]
111 local l_y = yfactor*sequence[i-1]
112 local m_x = xfactor*sequence[i+1]
113 local m_y = yfactor*sequence[i+2]
114 local r_x = xfactor*sequence[i+3]
115 local r_y = yfactor*sequence[i+4]
116 path[size] = f_curveto (
117 l_x + 2/3 * (m_x-l_x),
118 l_y + 2/3 * (m_y-l_y),
119 r_x + 2/3 * (m_x-r_x),
120 r_y + 2/3 * (m_y-r_y),
121 r_x, r_y
122 )
123 i = i + 5
124 else
125
126 i = i + 1
127 end
128 end
129 elseif segments then
130
131 for i=1,#segments do
132 local segment = segments[i]
133 local operator = segment[#segment]
134 if operator == "m" then
135 if size > 0 then
136 size = size + 1
137 path[size] = s_cycle
138 list[#list+1] = concat(path,"",1,size)
139 size = 1
140 else
141 size = size + 1
142 end
143 path[size] = f_moveto(xfactor*segment[1],yfactor*segment[2])
144 elseif operator == "l" then
145 size = size + 1
146 path[size] = f_lineto(xfactor*segment[1],yfactor*segment[2])
147 elseif operator == "c" then
148 size = size + 1
149 path[size] = f_curveto(xfactor*segment[1],yfactor*segment[2],xfactor*segment[3],yfactor*segment[4],xfactor*segment[5],yfactor*segment[6])
150 elseif operator == "q" then
151 size = size + 1
152
153 local prev = segments[i-1]
154 local l_x = xfactor*prev[#prev-2]
155 local l_y = yfactor*prev[#prev-1]
156 local m_x = xfactor*segment[1]
157 local m_y = yfactor*segment[2]
158 local r_x = xfactor*segment[3]
159 local r_y = yfactor*segment[4]
160 path[size] = f_curveto (
161 l_x + 2/3 * (m_x-l_x),
162 l_y + 2/3 * (m_y-l_y),
163 r_x + 2/3 * (m_x-r_x),
164 r_y + 2/3 * (m_y-r_y),
165 r_x, r_y
166 )
167 else
168
169 end
170 end
171 else
172 return
173 end
174 if size > 0 then
175 size = size + 1
176 path[size] = s_cycle
177 list[#list+1] = concat(path,"",1,size)
178 end
179 return list
180end
181
182function metapost.fill(paths)
183 local r = { }
184 local n = #paths
185 for i=1,n do
186 if i < n then
187 r[i] = f_nofill(paths[i])
188 else
189 r[i] = f_dofill(paths[i])
190 end
191 end
192 return concat(r)
193end
194
195function metapost.draw(paths,trace)
196 local r = { }
197 local n = #paths
198 for i=1,n do
199 if trace then
200 r[i] = f_draw_trace(paths[i])
201 else
202 r[i] = f_draw(paths[i])
203 end
204 end
205 return concat(r)
206end
207
208function metapost.maxbounds(data,index,factor)
209 local maxbounds = data.maxbounds
210 local factor = factor or 1
211 local glyphs = data.glyphs
212 local glyph = glyphs[index]
213 local boundingbox = glyph.boundingbox
214 local xmin, ymin, xmax, ymax
215 if not maxbounds then
216 xmin = 0
217 ymin = 0
218 xmax = 0
219 ymax = 0
220 for i=1,#glyphs do
221 local d = glyphs[i]
222 if d then
223 local b = d.boundingbox
224 if b then
225 if b[1] < xmin then xmin = b[1] end
226 if b[2] < ymin then ymin = b[2] end
227 if b[3] > xmax then xmax = b[3] end
228 if b[4] > ymax then ymax = b[4] end
229 end
230 end
231 end
232 maxbounds = { xmin, ymin, xmax, ymax }
233 data.maxbounds = maxbounds
234 else
235 xmin = maxbounds[1]
236 ymin = maxbounds[2]
237 xmax = maxbounds[3]
238 ymax = maxbounds[4]
239 end
240 local llx = boundingbox[1]
241 local lly = boundingbox[2]
242 local urx = boundingbox[3]
243 local ury = boundingbox[4]
244 local width = glyph.width
245 if llx > 0 then
246 llx = 0
247 end
248 if width > urx then
249 urx = width
250 end
251 return f_rectangle(
252 factor*llx,factor*ymin,
253 factor*urx,factor*ymin,
254 factor*urx,factor*ymax,
255 factor*llx,factor*ymax
256 )
257end
258
259
260
261
262
263local texgetbox = tex.getbox
264
265local nodecodes = nodes.nodecodes
266local rulecodes = nodes.rulecodes
267
268local rule_code = nodecodes.rule
269
270local normalrule_code = rulecodes.normal
271local outlinerule_code = rulecodes.outline
272local userrule_code = rulecodes.user
273local emptyrule_code = rulecodes.empty
274
275local nuts = nodes.nuts
276
277local getexpansion = nuts.getexpansion
278local getscales = nuts.getscales
279local isglyph = nuts.isglyph
280local getglyphdimensions = nuts.getglyphdimensions
281
282local fonthashes = fonts.hashes
283local fontcharacters = fonthashes.characters
284local fontparameters = fonthashes.parameters
285local fontshapes = fonthashes.shapes
286local fontdescriptions = fonthashes.descriptions
287
288local topaths = metapost.paths
289
290local f_text = formatters["mfun_do_outline_text_flush(%q,%i,%N,%N,%q)(%,t);"]
291local f_rule = formatters["mfun_do_outline_rule_flush(%q,%N,%N,%N,%N);"]
292local f_bounds = formatters["checkbounds(%N,%N,%N,%N);"]
293local s_nothing = "(origin scaled 10)"
294
295local sc = 10
296local fc = number.dimenfactors.bp
297
298
299
300local function glyph(kind,font,char,advance,shift,ex,s,sx,sy)
301 local character = fontcharacters[font][char]
302 if character then
303 local index = character.index
304 if index then
305 local shapedata = fontshapes[font]
306 local glyphs = shapedata.glyphs
307 if glyphs then
308 local glyf = glyphs[index]
309 if glyf then
310 local units = 1000
311 local yfactor = (sc/units) * fontparameters[font].factor / 655.36
312 local xfactor = yfactor
313 local shift = shift or 0
314 local advance = advance or 0
315 local exfactor = ex or 0
316 local wfactor = 1
317 local detail = kind == "p" and tostring(char) or ""
318
319 local xoffset = character.xoffset or 0
320 local yoffset = character.yoffset or 0
321
322 if exfactor ~= 0 then
323 wfactor = (1+(ex/units)/1000)
324 xfactor = xfactor * wfactor
325 end
326 if xoffset ~= 0 then
327 advance = advance + s * sx * xoffset * fc / 1000000
328 end
329 if yoffset ~= 0 then
330 shift = shift + s * sy * yoffset * fc / 1000000
331 end
332 if s then
333 xfactor = (s/1000) * ((sx or 1000)/1000) * xfactor
334 yfactor = (s/1000) * ((sy or 1000)/1000) * yfactor
335 end
336 local paths = topaths(glyf,xfactor,yfactor)
337 if paths then
338 return f_text(kind,#paths,advance,shift,detail,paths)
339 end
340 end
341 end
342 end
343 end
344end
345
346metapost.glyph = glyph
347
348local kind = ""
349local buffer = { }
350local b = 0
351
352local function reset()
353 buffer = { }
354 b = 0
355end
356
357local function flushcharacter(current, pos_h, pos_v, pod_r, font, char)
358 if current then
359 local char, font = isglyph(current)
360 local s, sx, sy = getscales(current)
361 local code = glyph(kind,font,char,pos_h*fc,pos_v*fc,getexpansion(current),s,sx,sy)
362 if code then
363 b = b + 1
364 buffer[b] = code
365 end
366 else
367 logs.report("mlib-fnt","check 'flushcharacter', no current")
368 end
369end
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408local function flushrule(current,pos_h,pos_v,pos_r,size_h,size_v,subtype)
409 if subtype == normalrule_code then
410 b = b + 1
411 buffer[b] = f_rule(kind,pos_h*fc,pos_v*fc,size_h*fc,size_v*fc)
412 elseif subtype == outlinerule_code then
413 b = b + 1
414 buffer[b] = f_rule("d",pos_h*fc,pos_v*fc,size_h*fc,size_v*fc)
415 elseif subtype == userrule_code then
416
417
418
419 elseif subtype == emptyrule_code then
420
421 else
422
423
424 end
425end
426
427local function flushsimplerule(pos_h, pos_v, pos_r, size_h, size_v)
428 flushrule(false,pos_h,pos_v,pos_r,size_h,size_v,normalrule_code)
429end
430
431local function flushspecialrule(pos_h, pos_v, pos_r, w, h, d, l, outline)
432 flushrule(false,pos_h,pos_v-d,pos_r,w,h+d,outline and outlinerule_code or normalrule_code)
433end
434
435
436
437drivers.install {
438 name = "mpo",
439 actions = {
440 initialize = function()
441 reset()
442 end,
443 finalize = function(driver,details)
444 local bb = details.boundingbox
445 local llx = bb[1] * fc
446 local lly = bb[2] * fc
447 local urx = bb[3] * fc
448 local ury = bb[4] * fc
449 b = b + 1
450 buffer[b] = f_bounds(llx,lly,urx,ury)
451
452 end,
453 },
454 flushers = {
455 updatefontstate = updatefontstate,
456 character = flushcharacter,
457 rule = flushrule,
458 simplerule = flushsimplerule,
459 specialrule = flushspecialrule,
460 }
461}
462
463function metapost.boxtomp(n,k)
464 kind = k
465 nodes.handlers.finalizebox(n)
466 drivers.converters.lmtx(drivers.instances.mpo,texgetbox(n),"box",1)
467 local result = concat(buffer,";")
468 reset()
469 return result
470end
471
472
473
474local loaded = table.setmetatableindex(function(t,k)
475 local v = fonts.definers.internal({ name = k } ,"<lmt:glyphshape:font>")
476 t[k] = v
477 return v
478end)
479
480local mpdata = 0
481local mpstack = { }
482
483function mp.lmt_glyphshape_start(id,character)
484 if type(id) == "string" then
485 id = loaded[id]
486 end
487 local fontid = (id and id ~= 0 and id) or font.current()
488 local shapedata = fontshapes [fontid]
489 local characters = fontcharacters [fontid]
490 local descriptions = fontdescriptions[fontid]
491 local mathgaps = mathematics.gaps
492 local shapeglyphs = shapedata.glyphs or { }
493 if type(character) == "string" and character ~= "" then
494 local hex = match(character,"^0x(.+)")
495 if hex then
496 character = tonumber(hex,16)
497 else
498 character = utfbyte(character)
499 end
500 else
501 character = tonumber(character)
502 end
503 local unicode = mathgaps[character] or character
504 local chardata = characters[unicode]
505 local descdata = descriptions[unicode]
506 if chardata then
507 glyph = shapeglyphs[chardata.index]
508 if glyph then
509 mpdata = glyph.mpdata
510 if not mpdata then
511 if glyph.segments or glyph.sequence then
512 local units = shapedata.units or 1000
513 local factor = 100/units
514 local width = (descdata.width or 0) * factor
515 local height = descdata.boundingbox[4] * factor
516 local depth = descdata.boundingbox[2] * factor
517 local llx = descdata.boundingbox[1] * factor
518 local math = descdata.math
519 local italic = (math and math.italic or 0) * factor
520 local accent = (math and math.accent or 0) * factor
521 mpdata = {
522 paths = metapost.paths(glyph,factor),
523 boundingbox = metapost.boundingbox(glyph,factor),
524 baseline = metapost.baseline(glyph,factor),
525 width = width,
526 height = height,
527 depth = depth,
528 italic = italic,
529 accent = accent,
530 llx = llx,
531 usedbox = f_rectangle(llx,depth,llx+width,depth,llx+width,height,llx,height),
532 usedline = f_line(llx,0,llx+width,0),
533 }
534 glyph.mpdata = mpdata
535 else
536 print("CHECK 1",id,character)
537 end
538 end
539 end
540 else
541 print("CHECK 2",id,character)
542 end
543 insert(mpstack, mpdata)
544end
545
546local mpprint = mp.print
547local injectpair = mp.inject.pair
548local injectnumeric = mp.inject.numeric
549
550function mp.lmt_glyphshape_stop()
551 mpdata = remove(mpstack)
552end
553
554function mp.lmt_glyphshape_n()
555 if mpdata then
556 mpprint(#mpdata.paths)
557 else
558 injectnumeric(0)
559 end
560end
561
562function mp.lmt_glyphshape_path(i)
563 if mpdata then
564 mpprint(mpdata.paths[i])
565 else
566 injectpair(0,0)
567 end
568end
569
570function mp.lmt_glyphshape_boundingbox()
571 if mpdata then
572 mpprint(mpdata.boundingbox)
573 else
574 injectpair(0,0)
575 end
576end
577function mp.lmt_glyphshape_usedbox()
578 if mpdata then
579 mpprint(mpdata.usedbox)
580 else
581 injectpair(0,0)
582 end
583end
584
585function mp.lmt_glyphshape_baseline()
586 if mpdata then
587 mpprint(mpdata.baseline)
588 else
589 injectpair(0,0)
590 end
591end
592function mp.lmt_glyphshape_usedline()
593 if mpdata then
594 mpprint(mpdata.usedline)
595 else
596 injectpair(0,0)
597 end
598end
599
600function mp.lmt_glyphshape_width () injectnumeric(mpdata and mpdata.width or 0) end
601function mp.lmt_glyphshape_depth () injectnumeric(mpdata and mpdata.depth or 0) end
602function mp.lmt_glyphshape_height() injectnumeric(mpdata and mpdata.height or 0) end
603function mp.lmt_glyphshape_italic() injectnumeric(mpdata and mpdata.italic or 0) end
604function mp.lmt_glyphshape_accent() injectnumeric(mpdata and mpdata.accent or 0) end
605function mp.lmt_glyphshape_llx () injectnumeric(mpdata and mpdata.llx or 0) end
606 |