1if not modules then modules = { } end modules ['font-ttf'] = {
2 version = 1.001,
3 optimize = true,
4 comment = "companion to font-ini.mkiv",
5 author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
6 copyright = "PRAGMA ADE / ConTeXt Development Team",
7 license = "see context related readme files"
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
36local next, type, unpack = next, type, unpack
37local band, rshift = bit32.band, bit32.rshift
38local sqrt, round, abs, min, max = math.sqrt, math.round, math.abs, math.min, math.max
39local char, rep = string.char, string.rep
40local concat = table.concat
41local idiv = number.idiv
42local setmetatableindex = table.setmetatableindex
43
44local report = logs.reporter("otf reader","ttf")
45
46local trace_deltas = false
47
48local readers = fonts.handlers.otf.readers
49local streamreader = readers.streamreader
50
51local setposition = streamreader.setposition
52local getposition = streamreader.getposition
53local skipbytes = streamreader.skip
54local readbyte = streamreader.readcardinal1
55local readushort = streamreader.readcardinal2
56local readulong = streamreader.readcardinal4
57local readchar = streamreader.readinteger1
58local readshort = streamreader.readinteger2
59local read2dot14 = streamreader.read2dot14
60local readinteger = streamreader.readinteger1
61local readcardinaltable = streamreader.readcardinaltable
62local readintegertable = streamreader.readintegertable
63
64directives.register("fonts.streamreader",function()
65
66 streamreader = utilities.streams
67
68 setposition = streamreader.setposition
69 getposition = streamreader.getposition
70 skipbytes = streamreader.skip
71 readbyte = streamreader.readcardinal1
72 readushort = streamreader.readcardinal2
73 readulong = streamreader.readcardinal4
74 readchar = streamreader.readinteger1
75 readshort = streamreader.readinteger2
76 read2dot14 = streamreader.read2dot14
77 readinteger = streamreader.readinteger1
78 readcardinaltable = streamreader.readcardinaltable
79 readintegertable = streamreader.readintegertable
80
81end)
82
83local short = 2
84local ushort = 2
85local ulong = 4
86
87local helpers = readers.helpers
88local gotodatatable = helpers.gotodatatable
89
90local function mergecomposites(glyphs,shapes)
91
92
93
94 local function merge(index,shape,components)
95 local contours = { }
96 local points = { }
97 local nofcontours = 0
98 local nofpoints = 0
99 local offset = 0
100 local deltas = shape.deltas
101 for i=1,#components do
102 local component = components[i]
103 local subindex = component.index
104 local subshape = shapes[subindex]
105 local subcontours = subshape.contours
106 local subpoints = subshape.points
107 if not subcontours then
108 local subcomponents = subshape.components
109 if subcomponents then
110 subcontours, subpoints = merge(subindex,subshape,subcomponents)
111 end
112 end
113 if subpoints then
114 local matrix = component.matrix
115 local xscale = matrix[1]
116 local xrotate = matrix[2]
117 local yrotate = matrix[3]
118 local yscale = matrix[4]
119 local xoffset = matrix[5]
120 local yoffset = matrix[6]
121 local count = #subpoints
122 if xscale == 1 and yscale == 1 and xrotate == 0 and yrotate == 0 then
123 for i=1,count do
124 local p = subpoints[i]
125 nofpoints = nofpoints + 1
126 points[nofpoints] = {
127 p[1] + xoffset,
128 p[2] + yoffset,
129 p[3]
130 }
131 end
132 else
133 for i=1,count do
134 local p = subpoints[i]
135 local x = p[1]
136 local y = p[2]
137 nofpoints = nofpoints + 1
138 points[nofpoints] = {
139
140
141 xscale * x + xrotate * y + xoffset,
142 yscale * y + yrotate * x + yoffset,
143
144
145 p[3]
146 }
147 end
148 end
149 local subcount = #subcontours
150 if subcount == 1 then
151 nofcontours = nofcontours + 1
152 contours[nofcontours] = offset + subcontours[1]
153 else
154 for i=1,#subcontours do
155 nofcontours = nofcontours + 1
156 contours[nofcontours] = offset + subcontours[i]
157 end
158 end
159 offset = offset + count
160 else
161 report("missing contours composite %s, component %s of %s, glyph %s",index,i,#components,subindex)
162 end
163 end
164 shape.points = points
165 shape.contours = contours
166 shape.components = nil
167 return contours, points
168 end
169
170 for index=0,#glyphs do
171 local shape = shapes[index]
172 if shape then
173 local components = shape.components
174 if components then
175 merge(index,shape,components)
176 end
177 end
178 end
179
180end
181
182local function readnothing(f)
183 return {
184 type = "nothing",
185 }
186end
187
188
189
190local function curveto(m_x,m_y,l_x,l_y,r_x,r_y)
191 return
192 l_x + 2/3 *(m_x-l_x), l_y + 2/3 *(m_y-l_y),
193 r_x + 2/3 *(m_x-r_x), r_y + 2/3 *(m_y-r_y),
194 r_x, r_y, "c"
195end
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210local xv = { }
211local yv = { }
212
213local function applyaxis(glyph,shape,deltas,dowidth)
214 local points = shape.points
215 if points then
216 local nofpoints = #points
217 local dw = 0
218 local dl = 0
219 for i=1,#deltas do
220 local deltaset = deltas[i]
221 local xvalues = deltaset.xvalues
222 local yvalues = deltaset.yvalues
223 if xvalues and yvalues then
224 local dpoints = deltaset.points
225 local factor = deltaset.factor
226 if dpoints then
227 local cnt = #dpoints
228 if dowidth then
229 cnt = cnt - 4
230 end
231 if cnt == 1 then
232 local d = dpoints[1]
233 local x = xvalues[d] * factor
234 local y = yvalues[d] * factor
235 for i=1,nofpoints do
236 local p = points[i]
237 if x ~= 0 then
238 p[1] = p[1] + x
239 end
240 if y ~= 0 then
241 p[2] = p[2] + y
242 end
243 end
244 else
245
246
247
248 local function find(i)
249 local prv = cnt
250 for j=1,cnt do
251 local nxt = dpoints[j]
252 if nxt == i then
253 return false, j, false
254 elseif nxt > i then
255 return prv, false, j
256 end
257 prv = j
258 end
259 return prv, false, 1
260 end
261
262
263 for i=1,nofpoints do
264 local d1, d2, d3 = find(i)
265 local p2 = points[i]
266 if d2 then
267 xv[i] = xvalues[d2]
268 yv[i] = yvalues[d2]
269 else
270 local n1 = dpoints[d1]
271 local n3 = dpoints[d3]
272
273
274
275 if n1 > nofpoints then
276 n1 = nofpoints
277 end
278 if n3 > nofpoints then
279 n3 = nofpoints
280 end
281
282 local p1 = points[n1]
283 local p3 = points[n3]
284 local p1x = p1[1]
285 local p2x = p2[1]
286 local p3x = p3[1]
287 local p1y = p1[2]
288 local p2y = p2[2]
289 local p3y = p3[2]
290 local x1 = xvalues[d1]
291 local y1 = yvalues[d1]
292 local x3 = xvalues[d3]
293 local y3 = yvalues[d3]
294
295 local fx
296 local fy
297
298 if p1x == p3x then
299 if x1 == x3 then
300 fx = x1
301 else
302 fx = 0
303 end
304 elseif p2x <= min(p1x,p3x) then
305 if p1x < p3x then
306 fx = x1
307 else
308 fx = x3
309 end
310 elseif p2x >= max(p1x,p3x) then
311 if p1x > p3x then
312 fx = x1
313 else
314 fx = x3
315 end
316 else
317 fx = (p2x - p1x)/(p3x - p1x)
318 fx = (1 - fx) * x1 + fx * x3
319 end
320
321 if p1y == p3y then
322 if y1 == y3 then
323 fy = y1
324 else
325 fy = 0
326 end
327 elseif p2y <= min(p1y,p3y) then
328 if p1y < p3y then
329 fy = y1
330 else
331 fy = y3
332 end
333 elseif p2y >= max(p1y,p3y) then
334 if p1y > p3y then
335 fy = y1
336 else
337 fy = y3
338 end
339 else
340 fy = (p2y - p1y)/(p3y - p1y)
341 fy = (1 - fy) * y1 + fy * y3
342 end
343
344
345
346
347
348
349
350
351
352
353 xv[i] = fx
354 yv[i] = fy
355 end
356 end
357 for i=1,nofpoints do
358 local pi = points[i]
359 local fx = xv[i]
360 local fy = yv[i]
361 if fx ~= 0 then
362 pi[1] = pi[1] + factor * fx
363 end
364 if fy ~= 0 then
365 pi[2] = pi[2] + factor * fy
366 end
367 end
368 end
369 else
370 for i=1,nofpoints do
371 local p = points[i]
372 local x = xvalues[i]
373 if x then
374 local y = yvalues[i]
375 if x ~= 0 then
376 p[1] = p[1] + factor * x
377 end
378 if y ~= 0 then
379 p[2] = p[2] + factor * y
380 end
381 else
382 break
383 end
384 end
385 end
386 if dowidth then
387 local h = nofpoints + 2
388 local l = nofpoints + 1
389
390
391 local x = xvalues[h]
392 if x then
393 dw = dw + factor * x
394 end
395 local x = xvalues[l]
396 if x then
397 dl = dl + factor * x
398 end
399 end
400 end
401 end
402
403
404
405
406
407 if dowidth then
408 local width = glyph.width or 0
409
410 glyph.width = width + dw - dl
411 end
412 else
413 report("no points for glyph %a",glyph.name)
414 end
415end
416
417
418
419
420local quadratic = false
421
422local function contours2outlines_normal(glyphs,shapes)
423
424 for index=0,#glyphs-1 do
425 local shape = shapes[index]
426 if shape then
427 local glyph = glyphs[index]
428 local contours = shape.contours
429 local points = shape.points
430 if contours then
431 local nofcontours = #contours
432 local segments = { }
433 local nofsegments = 0
434 glyph.segments = segments
435 if nofcontours > 0 then
436 local px = 0
437 local py = 0
438 local first = 1
439 for i=1,nofcontours do
440 local last = contours[i]
441 if last >= first then
442 local first_pt = points[first]
443 local first_on = first_pt[3]
444
445 if first == last then
446 first_pt[3] = "m"
447 nofsegments = nofsegments + 1
448 segments[nofsegments] = first_pt
449 else
450 local first_on = first_pt[3]
451 local last_pt = points[last]
452 local last_on = last_pt[3]
453 local start = 1
454 local control_pt = false
455 if first_on then
456 start = 2
457 else
458 if last_on then
459 first_pt = last_pt
460 else
461 first_pt = { (first_pt[1]+last_pt[1])/2, (first_pt[2]+last_pt[2])/2, false }
462 end
463 control_pt = first_pt
464 end
465 local x = first_pt[1]
466 local y = first_pt[2]
467 if not done then
468 xmin = x
469 ymin = y
470 xmax = x
471 ymax = y
472 done = true
473 end
474 nofsegments = nofsegments + 1
475 segments[nofsegments] = { x, y, "m" }
476 if not quadratic then
477 px = x
478 py = y
479 end
480 local previous_pt = first_pt
481 for i=first,last do
482 local current_pt = points[i]
483 local current_on = current_pt[3]
484 local previous_on = previous_pt[3]
485 if previous_on then
486 if current_on then
487
488 local x, y = current_pt[1], current_pt[2]
489 nofsegments = nofsegments + 1
490 segments[nofsegments] = { x, y, "l" }
491 if not quadratic then
492 px, py = x, y
493 end
494 else
495 control_pt = current_pt
496 end
497 elseif current_on then
498 local x1 = control_pt[1]
499 local y1 = control_pt[2]
500 local x2 = current_pt[1]
501 local y2 = current_pt[2]
502 nofsegments = nofsegments + 1
503 if quadratic then
504 segments[nofsegments] = { x1, y1, x2, y2, "q" }
505 else
506 x1, y1, x2, y2, px, py = curveto(x1, y1, px, py, x2, y2)
507 segments[nofsegments] = { x1, y1, x2, y2, px, py, "c" }
508 end
509 control_pt = false
510 else
511 local x2 = (previous_pt[1]+current_pt[1])/2
512 local y2 = (previous_pt[2]+current_pt[2])/2
513 local x1 = control_pt[1]
514 local y1 = control_pt[2]
515 nofsegments = nofsegments + 1
516 if quadratic then
517 segments[nofsegments] = { x1, y1, x2, y2, "q" }
518 else
519 x1, y1, x2, y2, px, py = curveto(x1, y1, px, py, x2, y2)
520 segments[nofsegments] = { x1, y1, x2, y2, px, py, "c" }
521 end
522 control_pt = current_pt
523 end
524 previous_pt = current_pt
525 end
526 if first_pt == last_pt then
527
528 else
529 nofsegments = nofsegments + 1
530 local x2 = first_pt[1]
531 local y2 = first_pt[2]
532 if not control_pt then
533 segments[nofsegments] = { x2, y2, "l" }
534 elseif quadratic then
535 local x1 = control_pt[1]
536 local y1 = control_pt[2]
537 segments[nofsegments] = { x1, y1, x2, y2, "q" }
538 else
539 local x1 = control_pt[1]
540 local y1 = control_pt[2]
541 x1, y1, x2, y2, px, py = curveto(x1, y1, px, py, x2, y2)
542 segments[nofsegments] = { x1, y1, x2, y2, px, py, "c" }
543
544 end
545 end
546 end
547 end
548 first = last + 1
549 end
550 end
551 end
552 end
553 end
554end
555
556local function contours2outlines_shaped(glyphs,shapes,keepcurve)
557
558 for index=0,#glyphs-1 do
559 local shape = shapes[index]
560 if shape then
561 local glyph = glyphs[index]
562 local contours = shape.contours
563 local points = shape.points
564 if contours then
565 local nofcontours = #contours
566 local segments = keepcurve and { } or nil
567 local nofsegments = 0
568 if keepcurve then
569 glyph.segments = segments
570 end
571 if nofcontours > 0 then
572 local xmin, ymin, xmax, ymax, done = 0, 0, 0, 0, false
573 local px, py = 0, 0
574 local first = 1
575 for i=1,nofcontours do
576 local last = contours[i]
577 if last >= first then
578 local first_pt = points[first]
579 local first_on = first_pt[3]
580
581 if first == last then
582
583 if keepcurve then
584 first_pt[3] = "m"
585 nofsegments = nofsegments + 1
586 segments[nofsegments] = first_pt
587 end
588 else
589 local first_on = first_pt[3]
590 local last_pt = points[last]
591 local last_on = last_pt[3]
592 local start = 1
593 local control_pt = false
594 if first_on then
595 start = 2
596 else
597 if last_on then
598 first_pt = last_pt
599 else
600 first_pt = { (first_pt[1]+last_pt[1])/2, (first_pt[2]+last_pt[2])/2, false }
601 end
602 control_pt = first_pt
603 end
604 local x = first_pt[1]
605 local y = first_pt[2]
606 if not done then
607 xmin, ymin, xmax, ymax = x, y, x, y
608 done = true
609 else
610 if x < xmin then xmin = x elseif x > xmax then xmax = x end
611 if y < ymin then ymin = y elseif y > ymax then ymax = y end
612 end
613 if keepcurve then
614 nofsegments = nofsegments + 1
615 segments[nofsegments] = { x, y, "m" }
616 end
617 if not quadratic then
618 px = x
619 py = y
620 end
621 local previous_pt = first_pt
622 for i=first,last do
623 local current_pt = points[i]
624 local current_on = current_pt[3]
625 local previous_on = previous_pt[3]
626 if previous_on then
627 if current_on then
628
629 local x = current_pt[1]
630 local y = current_pt[2]
631 if x < xmin then xmin = x elseif x > xmax then xmax = x end
632 if y < ymin then ymin = y elseif y > ymax then ymax = y end
633 if keepcurve then
634 nofsegments = nofsegments + 1
635 segments[nofsegments] = { x, y, "l" }
636 end
637 if not quadratic then
638 px = x
639 py = y
640 end
641 else
642 control_pt = current_pt
643 end
644 elseif current_on then
645 local x1 = control_pt[1]
646 local y1 = control_pt[2]
647 local x2 = current_pt[1]
648 local y2 = current_pt[2]
649 if quadratic then
650 if x1 < xmin then xmin = x1 elseif x1 > xmax then xmax = x1 end
651 if y1 < ymin then ymin = y1 elseif y1 > ymax then ymax = y1 end
652 if keepcurve then
653 nofsegments = nofsegments + 1
654 segments[nofsegments] = { x1, y1, x2, y2, "q" }
655 end
656 else
657 x1, y1, x2, y2, px, py = curveto(x1, y1, px, py, x2, y2)
658 if x1 < xmin then xmin = x1 elseif x1 > xmax then xmax = x1 end
659 if y1 < ymin then ymin = y1 elseif y1 > ymax then ymax = y1 end
660 if x2 < xmin then xmin = x2 elseif x2 > xmax then xmax = x2 end
661 if y2 < ymin then ymin = y2 elseif y2 > ymax then ymax = y2 end
662 if px < xmin then xmin = px elseif px > xmax then xmax = px end
663 if py < ymin then ymin = py elseif py > ymax then ymax = py end
664 if keepcurve then
665 nofsegments = nofsegments + 1
666 segments[nofsegments] = { x1, y1, x2, y2, px, py, "c" }
667 end
668 end
669 control_pt = false
670 else
671 local x2 = (previous_pt[1]+current_pt[1])/2
672 local y2 = (previous_pt[2]+current_pt[2])/2
673 local x1 = control_pt[1]
674 local y1 = control_pt[2]
675 if quadratic then
676 if x1 < xmin then xmin = x1 elseif x1 > xmax then xmax = x1 end
677 if y1 < ymin then ymin = y1 elseif y1 > ymax then ymax = y1 end
678 if keepcurve then
679 nofsegments = nofsegments + 1
680 segments[nofsegments] = { x1, y1, x2, y2, "q" }
681 end
682 else
683 x1, y1, x2, y2, px, py = curveto(x1, y1, px, py, x2, y2)
684 if x1 < xmin then xmin = x1 elseif x1 > xmax then xmax = x1 end
685 if y1 < ymin then ymin = y1 elseif y1 > ymax then ymax = y1 end
686 if x2 < xmin then xmin = x2 elseif x2 > xmax then xmax = x2 end
687 if y2 < ymin then ymin = y2 elseif y2 > ymax then ymax = y2 end
688 if px < xmin then xmin = px elseif px > xmax then xmax = px end
689 if py < ymin then ymin = py elseif py > ymax then ymax = py end
690 if keepcurve then
691 nofsegments = nofsegments + 1
692 segments[nofsegments] = { x1, y1, x2, y2, px, py, "c" }
693 end
694 end
695 control_pt = current_pt
696 end
697 previous_pt = current_pt
698 end
699 if first_pt == last_pt then
700
701 elseif not control_pt then
702 if keepcurve then
703 nofsegments = nofsegments + 1
704 segments[nofsegments] = { first_pt[1], first_pt[2], "l" }
705 end
706 else
707 local x1 = control_pt[1]
708 local y1 = control_pt[2]
709 local x2 = first_pt[1]
710 local y2 = first_pt[2]
711 if x1 < xmin then xmin = x1 elseif x1 > xmax then xmax = x1 end
712 if y1 < ymin then ymin = y1 elseif y1 > ymax then ymax = y1 end
713 if quadratic then
714 if keepcurve then
715 nofsegments = nofsegments + 1
716 segments[nofsegments] = { x1, y1, x2, y2, "q" }
717 end
718 else
719 x1, y1, x2, y2, px, py = curveto(x1, y1, px, py, x2, y2)
720 if x2 < xmin then xmin = x2 elseif x2 > xmax then xmax = x2 end
721 if y2 < ymin then ymin = y2 elseif y2 > ymax then ymax = y2 end
722 if px < xmin then xmin = px elseif px > xmax then xmax = px end
723 if py < ymin then ymin = py elseif py > ymax then ymax = py end
724 if keepcurve then
725 nofsegments = nofsegments + 1
726 segments[nofsegments] = { x1, y1, x2, y2, px, py, "c" }
727 end
728
729 end
730 end
731 end
732 end
733 first = last + 1
734 end
735 glyph.boundingbox = { round(xmin), round(ymin), round(xmax), round(ymax) }
736 end
737 end
738 end
739 end
740end
741
742
743
744local c_zero = char(0)
745local s_zero = char(0,0)
746
747
748
749
750
751local function toushort(n)
752 return char(band(rshift(n,8),0xFF),band(n,0xFF))
753
754end
755
756local function toshort(n)
757 if n < 0 then
758 n = n + 0x10000
759 end
760 return char(band(rshift(n,8),0xFF),band(n,0xFF))
761
762end
763
764
765
766local chars = setmetatableindex(function(t,k)
767 for i=0,255 do local v = char(i) t[i] = v end return t[k]
768end)
769
770local function repackpoints(glyphs,shapes)
771 local noboundingbox = { 0, 0, 0, 0 }
772 local result = { }
773 local xpoints = { }
774 local ypoints = { }
775 for index=0,#glyphs do
776 local shape = shapes[index]
777 if shape then
778 local r = 0
779 local glyph = glyphs[index]
780 local contours = shape.contours
781 local nofcontours = contours and #contours or 0
782 local boundingbox = glyph.boundingbox or noboundingbox
783 r = r + 1 result[r] = toshort(nofcontours)
784 r = r + 1 result[r] = toshort(boundingbox[1])
785 r = r + 1 result[r] = toshort(boundingbox[2])
786 r = r + 1 result[r] = toshort(boundingbox[3])
787 r = r + 1 result[r] = toshort(boundingbox[4])
788 if nofcontours > 0 then
789 for i=1,nofcontours do
790 r = r + 1 result[r] = toshort(contours[i]-1)
791 end
792 r = r + 1 result[r] = s_zero
793 local points = shape.points
794 local currentx = 0
795 local currenty = 0
796
797
798 local x = 0
799 local y = 0
800 local lastflag = nil
801 local nofflags = 0
802 for i=1,#points do
803 local pt = points[i]
804 local px = pt[1]
805 local py = pt[2]
806 local fl = pt[3] and 0x01 or 0x00
807 if px == currentx then
808 fl = fl + 0x10
809 else
810 local dx = round(px - currentx)
811 x = x + 1
812 if dx < -255 or dx > 255 then
813 xpoints[x] = toshort(dx)
814 elseif dx < 0 then
815 fl = fl + 0x02
816
817 xpoints[x] = chars[-dx]
818 elseif dx > 0 then
819 fl = fl + 0x12
820
821 xpoints[x] = chars[dx]
822 else
823 fl = fl + 0x02
824 xpoints[x] = c_zero
825 end
826 end
827 if py == currenty then
828 fl = fl + 0x20
829 else
830 local dy = round(py - currenty)
831 y = y + 1
832 if dy < -255 or dy > 255 then
833 ypoints[y] = toshort(dy)
834 elseif dy < 0 then
835 fl = fl + 0x04
836
837 ypoints[y] = chars[-dy]
838 elseif dy > 0 then
839 fl = fl + 0x24
840
841 ypoints[y] = chars[dy]
842 else
843 fl = fl + 0x04
844 ypoints[y] = c_zero
845 end
846 end
847 currentx = px
848 currenty = py
849 if lastflag == fl then
850 if nofflags == 255 then
851
852 lastflag = lastflag + 0x08
853 r = r + 1 result[r] = char(lastflag,nofflags-1)
854 nofflags = 1
855 lastflag = fl
856 else
857 nofflags = nofflags + 1
858 end
859 else
860 if nofflags == 1 then
861
862 r = r + 1 result[r] = chars[lastflag]
863 elseif nofflags == 2 then
864 r = r + 1 result[r] = char(lastflag,lastflag)
865 elseif nofflags > 2 then
866 lastflag = lastflag + 0x08
867 r = r + 1 result[r] = char(lastflag,nofflags-1)
868 end
869 nofflags = 1
870 lastflag = fl
871 end
872 end
873 if nofflags == 1 then
874
875 r = r + 1 result[r] = chars[lastflag]
876 elseif nofflags == 2 then
877 r = r + 1 result[r] = char(lastflag,lastflag)
878 elseif nofflags > 2 then
879 lastflag = lastflag + 0x08
880 r = r + 1 result[r] = char(lastflag,nofflags-1)
881 end
882
883
884 r = r + 1 result[r] = concat(xpoints,"",1,x)
885 r = r + 1 result[r] = concat(ypoints,"",1,y)
886 end
887
888 local stream = concat(result,"",1,r)
889 local length = #stream
890 local padding = idiv(length+3,4) * 4 - length
891 if padding > 0 then
892
893 if padding == 1 then
894 padding = "\0"
895 elseif padding == 2 then
896 padding = "\0\0"
897 else
898 padding = "\0\0\0"
899 end
900 padding = stream .. padding
901 end
902 glyph.stream = stream
903 end
904 end
905end
906
907
908
909local flags = { }
910
911local function readglyph(f,nofcontours)
912 local points = { }
913
914 local contours = { }
915 for i=1,nofcontours do
916 contours[i] = readshort(f) + 1
917 end
918 local nofpoints = contours[nofcontours]
919 local nofinstructions = readushort(f)
920 skipbytes(f,nofinstructions)
921
922
923 local i = 1
924 while i <= nofpoints do
925 local flag = readbyte(f)
926 flags[i] = flag
927 if band(flag,0x08) ~= 0 then
928 local n = readbyte(f)
929 if n == 1 then
930 i = i + 1
931 flags[i] = flag
932 else
933 for j=1,n do
934 i = i + 1
935 flags[i] = flag
936 end
937 end
938 end
939 i = i + 1
940 end
941
942
943 local x = 0
944 for i=1,nofpoints do
945 local flag = flags[i]
946
947
948 if band(flag,0x02) ~= 0 then
949 if band(flag,0x10) ~= 0 then
950 x = x + readbyte(f)
951 else
952 x = x - readbyte(f)
953 end
954 elseif band(flag,0x10) ~= 0 then
955
956 else
957 x = x + readshort(f)
958 end
959 points[i] = { x, 0, band(flag,0x01) ~= 0 }
960 end
961 local y = 0
962 for i=1,nofpoints do
963 local flag = flags[i]
964
965
966 if band(flag,0x04) ~= 0 then
967 if band(flag,0x20) ~= 0 then
968 y = y + readbyte(f)
969 else
970 y = y - readbyte(f)
971 end
972 elseif band(flag,0x20) ~= 0 then
973
974 else
975 y = y + readshort(f)
976 end
977 points[i][2] = y
978 end
979 return {
980 type = "glyph",
981 points = points,
982 contours = contours,
983 nofpoints = nofpoints,
984 }
985end
986
987local function readcomposite(f)
988 local components = { }
989 local nofcomponents = 0
990 local instructions = false
991 while true do
992 local flags = readushort(f)
993 local index = readushort(f)
994
995 local f_xyarg = band(flags,0x0002) ~= 0
996
997
998
999
1000
1001
1002
1003
1004
1005 local f_offset = band(flags,0x0800) ~= 0
1006
1007 local xscale = 1
1008 local xrotate = 0
1009 local yrotate = 0
1010 local yscale = 1
1011 local xoffset = 0
1012 local yoffset = 0
1013 local base = false
1014 local reference = false
1015 if f_xyarg then
1016 if band(flags,0x0001) ~= 0 then
1017 xoffset = readshort(f)
1018 yoffset = readshort(f)
1019 else
1020 xoffset = readchar(f)
1021 yoffset = readchar(f)
1022 end
1023 else
1024 if band(flags,0x0001) ~= 0 then
1025 base = readshort(f)
1026 reference = readshort(f)
1027 else
1028 base = readchar(f)
1029 reference = readchar(f)
1030 end
1031 end
1032 if band(flags,0x0008) ~= 0 then
1033 xscale = read2dot14(f)
1034 yscale = xscale
1035 if f_xyarg and f_offset then
1036 xoffset = xoffset * xscale
1037 yoffset = yoffset * yscale
1038 end
1039 elseif band(flags,0x0040) ~= 0 then
1040 xscale = read2dot14(f)
1041 yscale = read2dot14(f)
1042 if f_xyarg and f_offset then
1043 xoffset = xoffset * xscale
1044 yoffset = yoffset * yscale
1045 end
1046 elseif band(flags,0x0080) ~= 0 then
1047 xscale = read2dot14(f)
1048 xrotate = read2dot14(f)
1049 yrotate = read2dot14(f)
1050 yscale = read2dot14(f)
1051 if f_xyarg and f_offset then
1052 xoffset = xoffset * sqrt(xscale ^2 + yrotate^2)
1053 yoffset = yoffset * sqrt(xrotate^2 + yscale ^2)
1054 end
1055 end
1056 nofcomponents = nofcomponents + 1
1057 components[nofcomponents] = {
1058 index = index,
1059 usemine = band(flags,0x0200) ~= 0,
1060 round = band(flags,0x0006) ~= 0,
1061 base = base,
1062 reference = reference,
1063 matrix = { xscale, xrotate, yrotate, yscale, xoffset, yoffset },
1064 }
1065 if band(flags,0x0100) ~= 0 then
1066 instructions = true
1067 end
1068 if band(flags,0x0020) == 0 then
1069 break
1070 end
1071 end
1072 return {
1073 type = "composite",
1074 components = components,
1075 }
1076end
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086function readers.loca(f,fontdata,specification)
1087 if specification.glyphs then
1088 local datatable = fontdata.tables.loca
1089 if datatable then
1090
1091 local offset = fontdata.tables.glyf.offset
1092 local format = fontdata.fontheader.indextolocformat
1093 local profile = fontdata.maximumprofile
1094 local nofglyphs = profile and profile.nofglyphs
1095 local locations = { }
1096 setposition(f,datatable.offset)
1097 if format == 1 then
1098 if not nofglyphs then
1099 nofglyphs = idiv(datatable.length,4) - 1
1100 end
1101 for i=0,nofglyphs do
1102 locations[i] = offset + readulong(f)
1103 end
1104 fontdata.nofglyphs = nofglyphs
1105 else
1106 if not nofglyphs then
1107 nofglyphs = idiv(datatable.length,2) - 1
1108 end
1109 for i=0,nofglyphs do
1110 locations[i] = offset + readushort(f) * 2
1111 end
1112 end
1113 fontdata.nofglyphs = nofglyphs
1114 fontdata.locations = locations
1115 end
1116 end
1117end
1118
1119function readers.glyf(f,fontdata,specification)
1120 local tableoffset = gotodatatable(f,fontdata,"glyf",specification.glyphs)
1121 if tableoffset then
1122 local locations = fontdata.locations
1123 if locations then
1124 local glyphs = fontdata.glyphs
1125 local nofglyphs = fontdata.nofglyphs
1126 local filesize = fontdata.filesize
1127 local nothing = { 0, 0, 0, 0 }
1128 local shapes = { }
1129 local loadshapes = specification.shapes or specification.instance or specification.streams
1130 for index=0,nofglyphs-1 do
1131 local location = locations[index]
1132 local length = locations[index+1] - location
1133 if location >= filesize then
1134 report("discarding %s glyphs due to glyph location bug",nofglyphs-index+1)
1135 fontdata.nofglyphs = index - 1
1136 fontdata.badfont = true
1137 break
1138 elseif length > 0 then
1139 setposition(f,location)
1140 local nofcontours = readshort(f)
1141 glyphs[index].boundingbox = {
1142 readshort(f),
1143 readshort(f),
1144 readshort(f),
1145 readshort(f),
1146 }
1147 if not loadshapes then
1148
1149 elseif nofcontours == 0 then
1150 shapes[index] = readnothing(f)
1151 elseif nofcontours > 0 then
1152 shapes[index] = readglyph(f,nofcontours)
1153 else
1154 shapes[index] = readcomposite(f,nofcontours)
1155 end
1156 else
1157 if loadshapes then
1158 shapes[index] = readnothing(f)
1159 end
1160 glyphs[index].boundingbox = nothing
1161 end
1162 end
1163 if loadshapes then
1164 if readers.gvar then
1165 readers.gvar(f,fontdata,specification,glyphs,shapes)
1166 end
1167 mergecomposites(glyphs,shapes)
1168 if specification.instance then
1169 if specification.streams then
1170 repackpoints(glyphs,shapes)
1171 else
1172 contours2outlines_shaped(glyphs,shapes,specification.shapes)
1173 end
1174 elseif specification.shapes then
1175 if specification.streams then
1176 repackpoints(glyphs,shapes)
1177 else
1178 contours2outlines_normal(glyphs,shapes)
1179 end
1180 elseif specification.streams then
1181 repackpoints(glyphs,shapes)
1182 end
1183 end
1184 end
1185 end
1186end
1187
1188
1189
1190
1191
1192local function readtuplerecord(f,nofaxis)
1193 local record = { }
1194 for i=1,nofaxis do
1195 record[i] = read2dot14(f)
1196 end
1197 return record
1198end
1199
1200
1201
1202
1203local function readpoints(f)
1204 local count = readbyte(f)
1205 if count == 0 then
1206
1207 return nil, 0
1208 else
1209 if count < 128 then
1210
1211 elseif band(count,0x80) ~= 0 then
1212 count = band(count,0x7F) * 256 + readbyte(f)
1213 else
1214
1215 end
1216 local points = { }
1217 local p = 0
1218 local n = 1
1219 while p < count do
1220 local control = readbyte(f)
1221 local runreader = band(control,0x80) ~= 0 and readushort or readbyte
1222 local runlength = band(control,0x7F)
1223 for i=1,runlength+1 do
1224 n = n + runreader(f)
1225 p = p + 1
1226 points[p] = n
1227 end
1228 end
1229 return points, p
1230 end
1231end
1232
1233local function readdeltas(f,nofpoints)
1234 local deltas = { }
1235 local p = 0
1236 while nofpoints > 0 do
1237 local control = readbyte(f)
1238 if control then
1239 local allzero = band(control,0x80) ~= 0
1240 local runlength = band(control,0x3F) + 1
1241 if allzero then
1242 for i=1,runlength do
1243 p = p + 1
1244 deltas[p] = 0
1245 end
1246 else
1247 local runreader = band(control,0x40) ~= 0 and readshort or readinteger
1248 for i=1,runlength do
1249 p = p + 1
1250 deltas[p] = runreader(f)
1251 end
1252 end
1253 nofpoints = nofpoints - runlength
1254 else
1255
1256 break
1257 end
1258 end
1259
1260 if p > 0 then
1261 return deltas
1262 else
1263
1264 end
1265end
1266
1267function readers.gvar(f,fontdata,specification,glyphdata,shapedata)
1268
1269 local instance = specification.instance
1270 if not instance then
1271 return
1272 end
1273 local factors = specification.factors
1274 if not factors then
1275 return
1276 end
1277 local tableoffset = gotodatatable(f,fontdata,"gvar",specification.variable or specification.shapes)
1278 if tableoffset then
1279 local version = readulong(f)
1280 local nofaxis = readushort(f)
1281 local noftuples = readushort(f)
1282 local tupleoffset = tableoffset + readulong(f)
1283 local nofglyphs = readushort(f)
1284 local flags = readushort(f)
1285 local dataoffset = tableoffset + readulong(f)
1286 local data = { }
1287 local tuples = { }
1288 local glyphdata = fontdata.glyphs
1289 local dowidth = not fontdata.variabledata.hvarwidths
1290
1291
1292 if band(flags,0x0001) ~= 0 then
1293 for i=1,nofglyphs+1 do
1294 data[i] = dataoffset + readulong(f)
1295 end
1296 else
1297 for i=1,nofglyphs+1 do
1298 data[i] = dataoffset + 2*readushort(f)
1299 end
1300 end
1301
1302 if noftuples > 0 then
1303 setposition(f,tupleoffset)
1304 for i=1,noftuples do
1305 tuples[i] = readtuplerecord(f,nofaxis)
1306 end
1307 end
1308 local nextoffset = false
1309 local startoffset = data[1]
1310 for i=1,nofglyphs do
1311 nextoffset = data[i+1]
1312 local glyph = glyphdata[i-1]
1313 local name = trace_deltas and glyph.name
1314 if startoffset == nextoffset then
1315 if name then
1316 report("no deltas for glyph %a",name)
1317 end
1318 else
1319 local shape = shapedata[i-1]
1320 if not shape then
1321 if name then
1322 report("no shape for glyph %a",name)
1323 end
1324 else
1325 lastoffset = startoffset
1326 setposition(f,startoffset)
1327 local flags = readushort(f)
1328 local count = band(flags,0x0FFF)
1329 local offset = startoffset + readushort(f)
1330 local deltas = { }
1331 local allpoints = (shape.nofpoints or 0)
1332 local shared = false
1333 local nofshared = 0
1334 if band(flags,0x8000) ~= 0 then
1335
1336 local current = getposition(f)
1337 setposition(f,offset)
1338 shared, nofshared = readpoints(f)
1339 offset = getposition(f)
1340 setposition(f,current)
1341
1342 end
1343 for j=1,count do
1344 local size = readushort(f)
1345 local flags = readushort(f)
1346 local index = band(flags,0x0FFF)
1347 local haspeak = band(flags,0x8000) ~= 0
1348 local intermediate = band(flags,0x4000) ~= 0
1349 local private = band(flags,0x2000) ~= 0
1350 local peak = nil
1351 local start = nil
1352 local stop = nil
1353 local xvalues = nil
1354 local yvalues = nil
1355 local points = shared
1356 local nofpoints = nofshared
1357
1358 if haspeak then
1359 peak = readtuplerecord(f,nofaxis)
1360
1361 else
1362 if index+1 > #tuples then
1363 report("error, bad tuple index",index)
1364 end
1365 peak = tuples[index+1]
1366 end
1367 if intermediate then
1368 start = readtuplerecord(f,nofaxis)
1369 stop = readtuplerecord(f,nofaxis)
1370
1371 end
1372
1373 if size > 0 then
1374 local current = getposition(f)
1375
1376 setposition(f,offset)
1377 if private then
1378 points, nofpoints = readpoints(f)
1379 end
1380 if nofpoints == 0 then
1381 nofpoints = allpoints + 4
1382 end
1383 if nofpoints > 0 then
1384
1385 xvalues = readdeltas(f,nofpoints)
1386 yvalues = readdeltas(f,nofpoints)
1387 end
1388
1389 offset = offset + size
1390
1391 setposition(f,current)
1392 end
1393 if not xvalues and not yvalues then
1394 points = nil
1395 end
1396 local s = 1
1397 for i=1,nofaxis do
1398 local f = factors[i]
1399 local peak = peak and peak [i] or 0
1400
1401
1402 local start = start and start[i] or (peak < 0 and peak or 0)
1403 local stop = stop and stop [i] or (peak > 0 and peak or 0)
1404
1405
1406 if start > peak or peak > stop then
1407
1408 elseif start < 0 and stop > 0 and peak ~= 0 then
1409
1410 elseif peak == 0 then
1411
1412 elseif f < start or f > stop then
1413
1414 s = 0
1415 break
1416 elseif f < peak then
1417 s = s * (f - start) / (peak - start)
1418 elseif f > peak then
1419 s = s * (stop - f) / (stop - peak)
1420 else
1421
1422 end
1423 end
1424 if s == 0 then
1425 if name then
1426 report("no deltas applied for glyph %a",name)
1427 end
1428 else
1429 deltas[#deltas+1] = {
1430 factor = s,
1431 points = points,
1432 xvalues = xvalues,
1433 yvalues = yvalues,
1434 }
1435 end
1436 end
1437 if shape.type == "glyph" then
1438 applyaxis(glyph,shape,deltas,dowidth)
1439 else
1440
1441
1442 shape.deltas = deltas
1443 end
1444 end
1445 end
1446 startoffset = nextoffset
1447 end
1448 end
1449end
1450 |