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