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
60local report = logs.reporter("fonts","woff")
61
62local runner = sandbox.registerrunner {
63 name = "woff2otf",
64 method = "execute",
65 program = "woff2_decompress",
66 template = "%inputfile% %outputfile%",
67 reporter = report,
68 checkers = {
69 inputfile = "readable",
70 outputfile = "writable",
71 }
72}
73
74local function woff2otf(inpname,outname,infoonly)
75
76 local outname = outname or replacesuffix(inpname,"otf")
77 local inp = ioopen(inpname,"rb")
78
79 if not inp then
80 report("invalid input file %a",inpname)
81 return
82 end
83
84 local signature = readstring(inp,4)
85
86 if not (signature == "wOFF" or signature == "wOF2") then
87 inp:close()
88 report("invalid signature in %a",inpname)
89 return
90 end
91
92 local flavor = readstring(inp,4)
93
94 if not (flavor == "OTTO" or flavor == "true" or flavor == "\0\1\0\0") then
95 inp:close()
96 report("unsupported flavor %a in %a",flavor,inpname)
97 return
98 end
99
100 if signature == "wOF2" then
101 inp:close()
102 if false then
103 if runner then
104 runner {
105 inputfile = inpname,
106 outputfile = outname,
107 }
108 end
109 return outname, flavor
110 else
111 report("skipping version 2 file %a",inpname)
112 return
113 end
114 end
115
116 local out = ioopen(outname,"wb")
117
118 if not out then
119 inp:close()
120 report("invalid output file %a",outname)
121 return
122 end
123
124 local header = {
125 signature = signature,
126 flavor = flavor,
127 length = readcardinal4(inp),
128 numtables = readcardinal2(inp),
129 reserved = readcardinal2(inp),
130 totalsfntsize = readcardinal4(inp),
131 majorversion = readcardinal2(inp),
132 minorversion = readcardinal2(inp),
133 metaoffset = readcardinal4(inp),
134 metalength = readcardinal4(inp),
135 metaoriglength = readcardinal4(inp),
136 privoffset = readcardinal4(inp),
137 privlength = readcardinal4(inp),
138 }
139
140 local entries = { }
141
142 for i=1,header.numtables do
143 local entry = {
144 tag = readstring (inp,4),
145 offset = readcardinal4(inp),
146 compressed = readcardinal4(inp),
147 size = readcardinal4(inp),
148 checksum = readcardinal4(inp),
149 }
150 if not infoonly or infotags[lower(entry.tag)] then
151 entries[#entries+1] = entry
152 end
153 end
154
155 local nofentries = #entries
156 local entryselector = 0
157 local searchrange = 0
158 local rangeshift = 0
159
160 writestring (out,flavor)
161 writecardinal2(out,nofentries)
162 writecardinal2(out,entryselector)
163 writecardinal2(out,searchrange)
164 writecardinal2(out,rangeshift)
165
166 local offset = 12 + nofentries * 16
167 local offsets = { }
168
169 for i=1,nofentries do
170 local entry = entries[i]
171 local size = entry.size
172 writestring(out,entry.tag)
173 writecardinal4(out,entry.checksum)
174 writecardinal4(out,offset)
175 writecardinal4(out,size)
176 offsets[i] = offset
177 offset = offset + size
178 local p = 4 - offset % 4
179 if p > 0 then
180 offset = offset + p
181 end
182 end
183
184 for i=1,nofentries do
185 local entry = entries[i]
186 local offset = offsets[i]
187 local size = entry.size
188 setposition(inp,entry.offset+1)
189 local data = readstring(inp,entry.compressed)
190 if #data ~= size then
191 data = decompress(data)
192 end
193 setposition(out,offset+1)
194 writestring(out,data)
195 local p = 4 - offset + size % 4
196 if p > 0 then
197 for i=1,p do
198 writebyte(out,0)
199 end
200 end
201 end
202
203 inp:close()
204 out:close()
205
206 return outname, flavor
207
208end
209
210if readers then
211 readers.woff2otf = woff2otf
212else
213 return woff2otf
214end
215 |