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