1if not modules then modules = { } end modules ['util-dim'] = {
2 version = 1.001,
3 comment = "support for dimensions",
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
14local format, match, gsub, type, setmetatable = string.format, string.match, string.gsub, type, setmetatable
15local P, S, R, Cc, C, lpegmatch = lpeg.P, lpeg.S, lpeg.R, lpeg.Cc, lpeg.C, lpeg.match
16
17local allocate = utilities.storage.allocate
18local setmetatableindex = table.setmetatableindex
19local formatters = string.formatters
20
21local texget = tex and tex.get or function() return 65536*10*100 end
22
23local p_stripzeros = lpeg.patterns.stripzeros
24
25
26
27number = number or { }
28local number = number
29
30number.tonumberf = function(n) return lpegmatch(p_stripzeros,format("%.20f",n)) end
31number.tonumberg = function(n) return format("%.20g",n) end
32
33local dimenfactors = allocate {
34 ["pt"] = 1/65536,
35 ["in"] = ( 100/ 7227)/65536,
36 ["cm"] = ( 254/ 7227)/65536,
37 ["mm"] = ( 2540/ 7227)/65536,
38 ["sp"] = 1,
39 ["bp"] = ( 7200/ 7227)/65536,
40 ["pc"] = ( 1/ 12)/65536,
41 ["dd"] = ( 1157/ 1238)/65536,
42 ["cc"] = ( 1157/14856)/65536,
43
44
45 ["es"] = ( 9176/ 129)/65536,
46 ["ts"] = ( 4588/ 645)/65536,
47}
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90local f_none = formatters["%s%s"]
91local f_true = formatters["%0.5F%s"]
92
93local function numbertodimen(n,unit,fmt)
94 if type(n) == 'string' then
95 return n
96 else
97 unit = unit or 'pt'
98 n = n * dimenfactors[unit]
99 if not fmt then
100 fmt = f_none(n,unit)
101 elseif fmt == true then
102 fmt = f_true(n,unit)
103 else
104 return formatters[fmt](n,unit)
105 end
106 end
107end
108
109
110
111number.maxdimen = 1073741823
112number.todimen = numbertodimen
113number.dimenfactors = dimenfactors
114
115function number.topoints (n,fmt) return numbertodimen(n,"pt",fmt) end
116function number.toinches (n,fmt) return numbertodimen(n,"in",fmt) end
117function number.tocentimeters (n,fmt) return numbertodimen(n,"cm",fmt) end
118function number.tomillimeters (n,fmt) return numbertodimen(n,"mm",fmt) end
119
120function number.toscaledpoints(n) return n .. "sp" end
121function number.tobasepoints (n,fmt) return numbertodimen(n,"bp",fmt) end
122function number.topicas (n,fmt) return numbertodimen(n "pc",fmt) end
123function number.todidots (n,fmt) return numbertodimen(n,"dd",fmt) end
124function number.tociceros (n,fmt) return numbertodimen(n,"cc",fmt) end
125
126
127function number.toediths (n,fmt) return numbertodimen(n,"es",fmt) end
128function number.totoves (n,fmt) return numbertodimen(n,"ts",fmt) end
129
130
131
132
133
134
135local amount = (S("+-")^0 * R("09")^0 * P(".")^0 * R("09")^0) + Cc("0")
136local unit = R("az")^1 + P("%")
137
138local dimenpair = amount/tonumber * (unit^1/dimenfactors + Cc(1))
139
140lpeg.patterns.dimenpair = dimenpair
141
142local splitter = amount/tonumber * C(unit^1)
143
144function number.splitdimen(str)
145 return lpegmatch(splitter,str)
146end
147
148
149
150
151setmetatableindex(dimenfactors, function(t,s)
152
153 return false
154end)
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170local stringtodimen
171
172local amount = S("+-")^0 * R("09")^0 * S(".,")^0 * R("09")^0
173local unit = P("pt") + P("cm") + P("mm") + P("sp") + P("bp")
174 + P("es") + P("ts") + P("pc") + P("dd") + P("cc")
175 + P("in")
176
177
178local validdimen = amount * unit
179
180lpeg.patterns.validdimen = validdimen
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199local dimensions = { }
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230function dimensions.__add(a, b)
231 local ta, tb = type(a), type(b)
232 if ta == "string" then a = stringtodimen(a) elseif ta == "table" then a = a[1] end
233 if tb == "string" then b = stringtodimen(b) elseif tb == "table" then b = b[1] end
234 return setmetatable({ a + b }, dimensions)
235end
236
237function dimensions.__sub(a, b)
238 local ta, tb = type(a), type(b)
239 if ta == "string" then a = stringtodimen(a) elseif ta == "table" then a = a[1] end
240 if tb == "string" then b = stringtodimen(b) elseif tb == "table" then b = b[1] end
241 return setmetatable({ a - b }, dimensions)
242end
243
244function dimensions.__mul(a, b)
245 local ta, tb = type(a), type(b)
246 if ta == "string" then a = stringtodimen(a) elseif ta == "table" then a = a[1] end
247 if tb == "string" then b = stringtodimen(b) elseif tb == "table" then b = b[1] end
248 return setmetatable({ a * b }, dimensions)
249end
250
251function dimensions.__div(a, b)
252 local ta, tb = type(a), type(b)
253 if ta == "string" then a = stringtodimen(a) elseif ta == "table" then a = a[1] end
254 if tb == "string" then b = stringtodimen(b) elseif tb == "table" then b = b[1] end
255 return setmetatable({ a / b }, dimensions)
256end
257
258function dimensions.__unm(a)
259 local ta = type(a)
260 if ta == "string" then a = stringtodimen(a) elseif ta == "table" then a = a[1] end
261 return setmetatable({ - a }, dimensions)
262end
263
264
265
266
267
268
269
270
271
272
273
274
275function dimensions.__lt(a, b)
276 return a[1] < b[1]
277end
278
279function dimensions.__eq(a, b)
280 return a[1] == b[1]
281end
282
283
284
285
286function dimensions.__tostring(a)
287 return a[1]/65536 .. "pt"
288end
289
290
291
292
293
294
295function dimensions.__index(tab,key)
296 local d = dimenfactors[key]
297 if not d then
298 error("illegal property of dimen: " .. key)
299 d = 1
300 end
301 return 1/d
302end
303
304
305
306
307
308
309
310
311 dimenfactors["ex"] = 4 /65536
312 dimenfactors["em"] = 10 /65536
313
314 dimenfactors["eu"] = (9176/129)/65536
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333local known = { } setmetatable(known, { __mode = "v" })
334
335function dimen(a)
336 if a then
337 local ta= type(a)
338 if ta == "string" then
339 local k = known[a]
340 if k then
341 a = k
342 else
343 local value, unit = lpegmatch(dimenpair,a)
344 if value and unit then
345 k = value/unit
346 else
347 k = 0
348 end
349 known[a] = k
350 a = k
351 end
352 elseif ta == "table" then
353 a = a[1]
354 end
355 return setmetatable({ a }, dimensions)
356 else
357 return setmetatable({ 0 }, dimensions)
358 end
359end
360
361function string.todimen(str)
362 local t = type(str)
363 if t == "number" then
364 return str
365 else
366 local k = known[str]
367 if not k then
368 if t == "string" then
369 local value, unit = lpegmatch(dimenpair,str)
370 if value and unit then
371 k = value/unit
372 else
373 k = 0
374 end
375 else
376 k = 0
377 end
378 known[str] = k
379 end
380 return k
381 end
382end
383
384
385
386
387
388
389
390
391
392
393
394
395stringtodimen = string.todimen
396
397function number.toscaled(d)
398 return format("%0.5f",d/0x10000)
399end
400
401
402
403
404
405
406function number.percent(n,d)
407 d = d or texget("hsize")
408 if type(d) == "string" then
409 d = stringtodimen(d)
410 end
411 return (n/100) * d
412end
413
414number["%"] = number.percent
415 |