1if not modules then modules = { } end modules ['font-otr'] = {
2 version = 1.001,
3 comment = "companion to font-ini.mkiv",
4 author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
5 copyright = "PRAGMA ADE / ConTeXt Development Team",
6 license = "see context related readme files"
7}
8
9
10
11
12
13
14
15local ioopen = io.open
16local replacesuffix = file.replacesuffix
17
18local readers = fonts and fonts.handlers.otf.readers
19
20local streamreader = readers and readers.streamreader or utilities.files
21local streamwriter = readers and readers.streamwriter or utilities.files
22
23local readstring = streamreader.readstring
24local readcardinal2 = streamreader.readcardinal2
25local readcardinal4 = streamreader.readcardinal4
26local getsize = streamreader.getsize
27local setposition = streamreader.setposition
28local getposition = streamreader.getposition
29
30local writestring = streamwriter.writestring
31local writecardinal4 = streamwriter.writecardinal4
32local writecardinal2 = streamwriter.writecardinal2
33local writebyte = streamwriter.writebyte
34
35local decompress = zlib.decompress
36
37directives.register("fonts.streamreader",function()
38
39 streamreader = utilities.streams
40
41 readstring = streamreader.readstring
42 readcardinal2 = streamreader.readcardinal2
43 readcardinal4 = streamreader.readcardinal4
44 getsize = streamreader.getsize
45 setposition = streamreader.setposition
46 getposition = streamreader.getposition
47
48end)
49
50local infotags = {
51 ["os/2"] = true,
52 ["head"] = true,
53 ["maxp"] = true,
54 ["hhea"] = true,
55 ["hmtx"] = true,
56 ["post"] = true,
57 ["cmap"] = true,
58}
59
60
61
62
63
64
65
66
67
68
69
70local report = logs.reporter("fonts","woff")
71
72local runner = sandbox.registerrunner {
73 name = "woff2otf",
74 method = "execute",
75 program = "woff2_decompress",
76 template = "%inputfile% %outputfile%",
77 reporter = report,
78 checkers = {
79 inputfile = "readable",
80 outputfile = "writable",
81 }
82}
83
84local function woff2otf(inpname,outname,infoonly)
85
86 local outname = outname or replacesuffix(inpname,"otf")
87 local inp = ioopen(inpname,"rb")
88
89 if not inp then
90 report("invalid input file %a",inpname)
91 return
92 end
93
94 local signature = readstring(inp,4)
95
96 if not (signature == "wOFF" or signature == "wOF2") then
97 inp:close()
98 report("invalid signature in %a",inpname)
99 return
100 end
101
102 local flavor = readstring(inp,4)
103
104 if not (flavor == "OTTO" or flavor == "true" or flavor == "\0\1\0\0") then
105 inp:close()
106 report("unsupported flavor %a in %a",flavor,inpname)
107 return
108 end
109
110 if signature == "wOF2" then
111 inp:close()
112 if false then
113 if runner then
114 runner {
115 inputfile = inpname,
116 outputfile = outname,
117 }
118 end
119 return outname, flavor
120 else
121 report("skipping version 2 file %a",inpname)
122 return
123 end
124 end
125
126 local out = ioopen(outname,"wb")
127
128 if not out then
129 inp:close()
130 report("invalid output file %a",outname)
131 return
132 end
133
134 local header = {
135 signature = signature,
136 flavor = flavor,
137 length = readcardinal4(inp),
138 numtables = readcardinal2(inp),
139 reserved = readcardinal2(inp),
140 totalsfntsize = readcardinal4(inp),
141 majorversion = readcardinal2(inp),
142 minorversion = readcardinal2(inp),
143 metaoffset = readcardinal4(inp),
144 metalength = readcardinal4(inp),
145 metaoriglength = readcardinal4(inp),
146 privoffset = readcardinal4(inp),
147 privlength = readcardinal4(inp),
148 }
149
150 local entries = { }
151
152 for i=1,header.numtables do
153 local entry = {
154 tag = readstring (inp,4),
155 offset = readcardinal4(inp),
156 compressed = readcardinal4(inp),
157 size = readcardinal4(inp),
158 checksum = readcardinal4(inp),
159 }
160 if not infoonly or infotags[lower(entry.tag)] then
161 entries[#entries+1] = entry
162 end
163 end
164
165 local nofentries = #entries
166 local entryselector = 0
167 local searchrange = 0
168 local rangeshift = 0
169
170 writestring (out,flavor)
171 writecardinal2(out,nofentries)
172 writecardinal2(out,entryselector)
173 writecardinal2(out,searchrange)
174 writecardinal2(out,rangeshift)
175
176 local offset = 12 + nofentries * 16
177 local offsets = { }
178
179 for i=1,nofentries do
180 local entry = entries[i]
181 local size = entry.size
182 writestring(out,entry.tag)
183 writecardinal4(out,entry.checksum)
184 writecardinal4(out,offset)
185 writecardinal4(out,size)
186 offsets[i] = offset
187 offset = offset + size
188 local p = 4 - offset % 4
189 if p > 0 then
190 offset = offset + p
191 end
192 end
193
194 for i=1,nofentries do
195 local entry = entries[i]
196 local offset = offsets[i]
197 local size = entry.size
198 setposition(inp,entry.offset+1)
199 local data = readstring(inp,entry.compressed)
200 if #data ~= size then
201 data = decompress(data)
202 end
203 setposition(out,offset+1)
204 writestring(out,data)
205 local p = 4 - offset + size % 4
206 if p > 0 then
207 for i=1,p do
208 writebyte(out,0)
209 end
210 end
211 end
212
213 inp:close()
214 out:close()
215
216 return outname, flavor
217
218end
219
220if readers then
221 readers.woff2otf = woff2otf
222else
223 return woff2otf
224end
225 |