• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

sile-typesetter / sile / 9307175333

30 May 2024 06:08PM UTC coverage: 70.644% (-3.5%) from 74.124%
9307175333

push

github

web-flow
Merge b18390e74 into 70ff5c335

1910 of 2592 new or added lines in 108 files covered. (73.69%)

901 existing lines in 52 files now uncovered.

12203 of 17274 relevant lines covered (70.64%)

6112.16 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.32
/types/measurement.lua
1
--- SILE measurement type.
2
-- Measurements consist of an amount and a unit. Any registered `types.unit` may be used. Some units are relative and
3
-- their value may depend on the context where they are evaluated. Others are absolute. Unlike `types.length`
4
-- measurements have no stretch or shrink parameters.
5
-- @types measurement
6

7
local function _tonumber (amount)
8
   return SU.cast("number", amount)
44,834✔
9
end
10

11
local function _similarunit (a, b)
12
   if type(b) == "number" or type(a) == "number" then
175,476✔
13
      return true
16,277✔
14
   else
15
      return a.unit == b.unit
159,199✔
16
   end
17
end
18

19
local function _hardnumber (a, b)
20
   if type(b) == "number" or type(a) == "number" then
12,485✔
21
      return true
11,040✔
22
   else
23
      return false
1,445✔
24
   end
25
end
26

27
local function _unit (a, b)
28
   return type(a) == "table" and a.unit or b.unit
165,157✔
29
end
30

31
local function _amount (input)
32
   return type(input) == "number" and input or input.amount
331,568✔
33
end
34

35
local function _pt_amount (input)
36
   return type(input) == "number" and input or not input and 0 or input._mutable and input.amount or input:tonumber()
253,990✔
37
end
38

39
local function _error_if_immutable (input)
40
   if type(input) == "table" and not input._mutable then
252,631✔
NEW
41
      SU.error("Not so fast, we can't do mutating arithmetic except on 'pt' unit measurements!", true)
×
42
   end
43
end
44

45
local function _error_if_relative (a, b)
46
   if type(a) == "table" and a.relative or type(b) == "table" and b.relative then
22,177✔
NEW
47
      SU.error("Cannot do arithmetic on a relative measurement without explicitly absolutizing it.", true)
×
48
   end
49
end
50

51
--- @type measurement
52
local measurement = pl.class()
209✔
53
measurement.type = "measurement"
209✔
54

55
measurement.amount = 0
209✔
56
measurement.unit = "pt"
209✔
57
measurement.relative = false
209✔
58
measurement._mutable = false
209✔
59

60
--- Constructor.
61
-- @tparam number|length|measurement|string amount Amount of units or a string with the amount and unit.
62
-- @tparam[opt=pt] string unit Name of unit.
63
-- @treturn measurement
64
-- @usage
65
-- SILE.types.measurement(3, "em")
66
-- SILE.types.measurement("2%fw")
67
-- SILE.types.measurement(6)
68
function measurement:_init (amount, unit)
209✔
69
   if unit then
1,072,321✔
70
      self.unit = unit
165,996✔
71
   end
72
   if SU.type(amount) == "length" then
2,144,642✔
73
      self.amount = amount.length.amount
16✔
74
      self.unit = amount.length.unit
16✔
75
   elseif type(amount) == "table" then
1,072,305✔
76
      self.amount = amount.amount
197,213✔
77
      self.unit = amount.unit
197,213✔
78
   elseif type(tonumber(amount)) == "number" then
875,092✔
79
      self.amount = tonumber(amount)
409,761✔
80
   elseif type(amount) == "string" then
465,331✔
81
      local parsed = SILE.parserBits.measurement:match(amount)
332✔
82
      if not parsed then
332✔
NEW
83
         SU.error("Could not parse measurement '" .. amount .. "'")
×
84
      end
85
      self.amount, self.unit = parsed.amount, parsed.unit
332✔
86
   end
87
   local _su = SILE.types.unit[self.unit]
1,072,530✔
88
   if not _su then
1,072,321✔
NEW
89
      SU.error("Unknown unit: " .. unit)
×
90
   end
91
   self.relative = _su.relative
1,072,321✔
92
   if self.unit == "pt" then
1,072,321✔
93
      self._mutable = true
1,067,783✔
94
   end
95
end
96

97
--- Convert relative measurements to absolute values and return a measurement.
98
-- Resolves relative measurements (like em relevant to the current font size) into absolute measurements.
99
-- @treturn measurement A new measurement in pt with any relative values resolved.
100
-- @usage
101
-- > a = SILE.types.measurement("1.2em")
102
-- > print(a:absolute())
103
-- 12pt
104
function measurement:absolute ()
209✔
105
   return SILE.types.measurement(self:tonumber())
5,128✔
106
end
107

108
function measurement:tostring ()
209✔
NEW
109
   return self:__tostring()
×
110
end
111

112
--- Convert relative measurements to absolute values and return a number.
113
-- Similar to `measurement:absolute` but returns a number instead of a new measurement type.
114
-- @treturn number A number (corresponding to pts) for the amount with any relative values resolved.
115
function measurement:tonumber ()
209✔
116
   local def = SILE.types.unit[self.unit]
417,601✔
117
   local amount = def.converter and def.converter(self.amount) or (self.amount * def.value)
422,679✔
118
   return amount
417,601✔
119
end
120

121
function measurement:__tostring ()
209✔
122
   return self.amount .. self.unit
124✔
123
end
124

125
function measurement:__concat (other)
209✔
126
   return tostring(self) .. tostring(other)
72✔
127
end
128

129
--- Addition meta-method.
130
-- Assuming matching relative units or absolute units, allows two measurements to be combined into one.
131
-- @tparam measurement other
132
-- @treturn measuremnet A new measurement of the same unit type as `self` with the value of `other` added.
133
-- @usage
134
-- > a = SILE.types.measurement(6, "em")
135
-- > b = SILE.types.measurement("2em")
136
-- > c = a + b
137
-- > print(c)
138
-- 8em
139
function measurement:__add (other)
209✔
140
   if _similarunit(self, other) then
175,044✔
141
      return SILE.types.measurement(_amount(self) + _amount(other), _unit(self, other))
349,752✔
142
   else
143
      _error_if_relative(self, other)
84✔
144
      return SILE.types.measurement(_tonumber(self) + _tonumber(other))
252✔
145
   end
146
end
147

148
-- Note all private math (_ + __func()) functions:
149
-- * Are much faster than regular math operations
150
-- * Are **not** intended for use outside of the most performance sensitive loops
151
-- * Modify the lhs input in-place, never instantiating new objects
152
-- * Always assume absolute lhs input and absolutize the rhs values at runtime
153
-- * Assmue the inputs are sane with much less error checking than regular math funcs
154
-- * Are not composable using chained methods since they return nil for safety
155
function measurement:___add (other)
209✔
156
   _error_if_immutable(self)
237,247✔
157
   self.amount = self.amount + _pt_amount(other)
474,494✔
158
   return nil
237,247✔
159
end
160

161
function measurement:__sub (other)
209✔
162
   if _similarunit(self, other) then
175,908✔
163
      return SILE.types.measurement(_amount(self) - _amount(other), _unit(self, other))
269,224✔
164
   else
165
      _error_if_relative(self, other)
20,648✔
166
      return SILE.types.measurement(_tonumber(self) - _tonumber(other))
61,944✔
167
   end
168
end
169

170
-- See usage comments on SILE.types.measurement:___add()
171
function measurement:___sub (other)
209✔
172
   _error_if_immutable(self)
15,384✔
173
   self.amount = self.amount - _pt_amount(other)
30,768✔
174
   return nil
15,384✔
175
end
176

177
function measurement:__mul (other)
209✔
178
   if _hardnumber(self, other) then
20,834✔
179
      return SILE.types.measurement(_amount(self) * _amount(other), _unit(self, other))
41,652✔
180
   else
181
      _error_if_relative(self, other)
4✔
182
      return SILE.types.measurement(_tonumber(self) * _tonumber(other))
12✔
183
   end
184
end
185

186
function measurement:__pow (other)
209✔
NEW
187
   if _hardnumber(self, other) then
×
NEW
188
      return SILE.types.measurement(_amount(self) ^ _amount(other), self.unit)
×
189
   else
NEW
190
      _error_if_relative(self, other)
×
NEW
191
      return SILE.types.measurement(_tonumber(self) ^ _tonumber(other))
×
192
   end
193
end
194

195
function measurement:__div (other)
209✔
196
   if _hardnumber(self, other) then
4,066✔
197
      return SILE.types.measurement(_amount(self) / _amount(other), self.unit)
1,881✔
198
   else
199
      _error_if_relative(self, other)
1,406✔
200
      return SILE.types.measurement(_tonumber(self) / _tonumber(other))
4,218✔
201
   end
202
end
203

204
function measurement:__mod (other)
209✔
205
   if _hardnumber(self, other) then
70✔
NEW
206
      return SILE.types.measurement(_amount(self) % _amount(other), self.unit)
×
207
   else
208
      _error_if_relative(self, other)
35✔
209
      return SILE.types.measurement(_tonumber(self) % _tonumber(other))
105✔
210
   end
211
end
212

213
function measurement:__unm ()
209✔
214
   local ret = SILE.types.measurement(self)
137✔
215
   ret.amount = self.amount * -1
137✔
216
   return ret
137✔
217
end
218

219
function measurement:__eq (other)
209✔
220
   return _tonumber(self) == _tonumber(other)
66✔
221
end
222

223
function measurement:__lt (other)
209✔
224
   return _tonumber(self) < _tonumber(other)
654✔
225
end
226

227
function measurement:__le (other)
209✔
NEW
228
   return _tonumber(self) <= _tonumber(other)
×
229
end
230

231
return measurement
209✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc