summaryrefslogtreecommitdiff
path: root/macros/luatex/generic/barracuda/src/lib-geo
diff options
context:
space:
mode:
Diffstat (limited to 'macros/luatex/generic/barracuda/src/lib-geo')
-rw-r--r--macros/luatex/generic/barracuda/src/lib-geo/brcd-gacanvas.lua340
-rw-r--r--macros/luatex/generic/barracuda/src/lib-geo/brcd-libgeo.lua289
2 files changed, 629 insertions, 0 deletions
diff --git a/macros/luatex/generic/barracuda/src/lib-geo/brcd-gacanvas.lua b/macros/luatex/generic/barracuda/src/lib-geo/brcd-gacanvas.lua
new file mode 100644
index 0000000000..cccc68f447
--- /dev/null
+++ b/macros/luatex/generic/barracuda/src/lib-geo/brcd-gacanvas.lua
@@ -0,0 +1,340 @@
+-- class gaCanvas
+-- Copyright (C) 2019 Roberto Giacomelli
+
+-- ga -- basic function
+
+local gaCanvas = {
+ _VERSION = "gacanvas v0.0.4",
+ _NAME = "gaCanvas",
+ _DESCRIPTION = "A library for dealing with ga stream",
+}
+gaCanvas.__index = gaCanvas
+
+-- ga specification: see the file ga-grammar.pdf in the doc directory
+
+-- gaCanvas constructor
+function gaCanvas:new() --> object
+ local o = {
+ _data = {},
+ _v = 100, -- version of the ga format
+ }
+ setmetatable(o, self)
+ return o
+end
+
+-- Ipothetical another constructor
+-- function gaCanvas:from_tcp_server() --> err
+-- end
+
+-- line width: opcode <1> <w: DIM>
+function gaCanvas:encode_linethick(w) --> err
+ if type(w) ~= "number" then return "[ArgErr] 'w' number expected" end
+ if w < 0 then return "[ArgErr] negative value for 'w'" end
+ local data = self._data
+ data[#data + 1] = 1 -- opcode for line thickness
+ data[#data + 1] = w
+end
+
+-- line cap style: opcode <2> <cap: u8>
+-- 0 Butt cap
+-- 1 Round cap
+-- 2 Projecting square cap
+function gaCanvas:encode_linecap(cap) --> err
+ if type(cap) ~= "number" then return "[ArgErr] 'cap' arg number expected" end
+ if cap == 0 or cap == 1 or cap == 2 then
+ local data = self._data
+ data[#data + 1] = 2 -- opcode for line cap style
+ data[#data + 1] = cap
+ else
+ return "[ArgErr] invalid value for 'cap'"
+ end
+end
+
+-- line cap style: opcode <3> <join: u8>
+-- 0 Miter join
+-- 1 Round join
+-- 2 Bevel join
+function gaCanvas:encode_linejoin(join) --> err
+ if type(join) ~= "number" then return "[ArgErr] 'join' arg number expected" end
+ if join == 0 or join == 1 or join == 2 then
+ local data = self._data
+ data[#data + 1] = 3 -- opcode for line join style
+ data[#data + 1] = join
+ else
+ return "[ArgErr] invalid value for 'join'"
+ end
+end
+
+-- Stop checking the bounding box
+-- opcode: <30>
+function gaCanvas:start_bbox_group() --> err
+ local data = self._data
+ data[#data + 1] = 30
+end
+
+-- restart checking the bounding box
+-- and insert the specified bb for the entire object group
+-- code: <31> x1 y1 x2 y2
+function gaCanvas:stop_bbox_group(x1, y1, x2, y2) --> err
+ if type(x1) ~= "number" then return "[ArgErr] 'x1' number expected" end
+ if type(y1) ~= "number" then return "[ArgErr] 'y1' number expected" end
+ if type(x2) ~= "number" then return "[ArgErr] 'x2' number expected" end
+ if type(y2) ~= "number" then return "[ArgErr] 'y2' number expected" end
+ if x1 > x2 then x1, x2 = x2, x1 end -- reorder coordinates
+ if y1 > y2 then y1, y2 = y2, y1 end
+ local data = self._data
+ data[#data + 1] = 31 -- bounding box of the object group
+ data[#data + 1] = x1
+ data[#data + 1] = y1
+ data[#data + 1] = x2
+ data[#data + 1] = y2
+end
+
+-- insert a line from point (x1, y1) to the point (x2, y2)
+-- <32> x1 y1 x2 y2
+function gaCanvas:encode_line(x1, y1, x2, y2) --> err
+ if type(x1) ~= "number" then return "[ArgErr] 'x1' number expected" end
+ if type(y1) ~= "number" then return "[ArgErr] 'y1' number expected" end
+ if type(x2) ~= "number" then return "[ArgErr] 'x2' number expected" end
+ if type(y2) ~= "number" then return "[ArgErr] 'y2' number expected" end
+ local data = self._data
+ data[#data + 1] = 32 -- append line data
+ data[#data + 1] = x1
+ data[#data + 1] = y1
+ data[#data + 1] = x2
+ data[#data + 1] = y2
+end
+
+-- insert an horizontal line from point (x1, y) to point (x2, y)
+-- <33> x1 x2 y
+function gaCanvas:encode_hline(x1, x2, y) --> err
+ if type(x1) ~= "number" then return "[ArgErr] 'x1' number expected" end
+ if type(x2) ~= "number" then return "[ArgErr] 'x2' number expected" end
+ if type(y) ~= "number" then return "[ArgErr] 'y2' number expected" end
+ local data = self._data
+ data[#data + 1] = 33 -- append hline data
+ data[#data + 1] = x1
+ data[#data + 1] = x2
+ data[#data + 1] = y
+end
+
+-- insert a rectangle from point (x1, x2) to (x2, y2)
+-- <48> <x1: DIM> <y1: DIM> <x2: DIM> <y2: DIM>
+function gaCanvas:encode_rectangle(x1, y1, x2, y2) --> err
+ if type(x1) ~= "number" then return "[ArgErr] 'x1' number expected" end
+ if type(y1) ~= "number" then return "[ArgErr] 'y1' number expected" end
+ if type(x2) ~= "number" then return "[ArgErr] 'x2' number expected" end
+ if type(y2) ~= "number" then return "[ArgErr] 'y2' number expected" end
+ local d = self._data
+ d[#d + 1] = 48 -- append rectangle data
+ d[#d + 1] = x1
+ d[#d + 1] = y1
+ d[#d + 1] = x2
+ d[#d + 1] = y2
+end
+
+-- Vbar object: opcode <36>
+-- x0, y1, y2 ordinates
+function gaCanvas:encode_Vbar(vbar, x0, y1, y2) --> err
+ if type(vbar) ~= "table" then
+ return "[ArgErr] table expected for 'vbar'"
+ end
+ if x0 == nil then
+ x0 = 0
+ elseif type(x0) ~= "number" then
+ return "[ArgErr] 'x0' number expected"
+ end
+ if type(y1) ~= "number" then return "[ArgErr] 'y1' number expected" end
+ if type(y2) ~= "number" then return "[ArgErr] 'y2' number expected" end
+ -- ordinates
+ if y1 == y2 then return "[ArgErr] 'y1' 'y2' are the same value" end
+ if y1 > y2 then y1, y2 = y2, y1 end
+ local bars = assert(vbar._yline, "[InternalErr] no '_yline' field")
+ local bdim = #bars
+ if bdim == 0 then return "[InternalErr] number of bars is zero" end
+ if bdim % 2 ~= 0 then
+ return "[InternalErr] '_yline' does not have an even number of elements"
+ end
+ local data = self._data
+ data[#data + 1] = 36 -- vbar sequence start
+ data[#data + 1] = y1
+ data[#data + 1] = y2
+ data[#data + 1] = bdim / 2 -- the number of bars <x_i t_i>
+ for i = 1, bdim, 2 do
+ local coord = bars[i]
+ local width = bars[i + 1]
+ if type(coord) ~= "number" then
+ return "[Err] a coordinates is not a number"
+ end
+ if type(width) ~= "number" then
+ return "[Err] a width is not a number"
+ end
+ data[#data + 1] = coord + x0
+ data[#data + 1] = width
+ end
+end
+
+-- [text] <130> ax ay x y chars
+function gaCanvas:encode_Text(txt, xpos, ypos, ax, ay) --> err
+ if type(txt) ~= "table" then
+ return "[ArgErr] 'txt' object table expected"
+ end
+ if type(xpos) ~= "number" then
+ return "[ArgErr] 'xpos' number expected"
+ end
+ if type(ypos) ~= "number" then
+ return "[ArgErr] 'ypos' number expected"
+ end
+ if ax == nil then
+ ax = 0
+ elseif type(ax) ~= "number" then
+ return "[ArgErr] 'ax' number expected"
+ end
+ if ay == nil then
+ ay = 0
+ elseif type(ay) ~= "number" then
+ return "[ArgErr] 'ay' number expected"
+ end
+ local data = self._data
+ data[#data + 1] = 130
+ data[#data + 1] = ax -- relative anchor x-coordinate
+ data[#data + 1] = ay -- relative anchor y-coordinate
+ data[#data + 1] = xpos -- text x-coordinate
+ data[#data + 1] = ypos -- text y-coordinate
+ local chars = assert(txt.codepoint, "[InternalErr] no 'codepoint' field in txt")
+ if #chars == 0 then return "[InternalErr] 'txt' has no chars" end
+ for _, c in ipairs(chars) do
+ data[#data + 1] = c
+ end
+ data[#data + 1] = 0 -- end string signal
+end
+
+-- glyphs equally spaced along the baseline
+-- [text_xspaced] <131> x1 xgap ay ypos chars
+function gaCanvas:encode_Text_xspaced(txt, x1, xgap, ypos, ay) --> err
+ if type(txt)~= "table" then return "[ArgErr] 'txt' object table expected" end
+ local chars = assert(txt.codepoint, "[InternalErr] no 'codepoint' field in txt")
+ if #chars == 0 then return "[InternalErr] 'txt' has no chars" end
+ if type(x1) ~= "number" then return "[ArgErr] 'x1' number expected" end
+ if type(xgap) ~= "number" then return "[ArgErr] 'xgap' number expected" end
+ if xgap < 0 then
+ local n = #chars
+ x1 = x1 + (n - 1) * xgap
+ xgap = -xgap
+ end
+ if type(ypos) ~= "number" then return "[ArgErr] 'ypos' number expected" end
+ if ay == nil then
+ ay = 0
+ elseif type(ay) ~= "number" then
+ return "[ArgErr] 'ay' number expected"
+ end
+ local data = self._data
+ data[#data + 1] = 131
+ data[#data + 1] = x1 -- x-coordinate of the first axis from left to right
+ data[#data + 1] = xgap -- axial distance among gliphs
+ data[#data + 1] = ay -- anchor relative y-coordinate
+ data[#data + 1] = ypos -- text y-coordinate
+ for _, c in ipairs(chars) do
+ data[#data + 1] = c
+ end
+ data[#data + 1] = 0 -- end string signal
+end
+
+-- text_xwidth
+-- text equally spaced but within [x1, x2] coordinate interval
+-- <132> <ay: FLOAT> <x1: DIM> <x2: DIM> <y: DIM> <c: CHARS>
+function gaCanvas:encode_Text_xwidth(txt, x1, x2, ypos, ay) --> err
+ if type(txt)~= "table" then return "[ArgErr] 'txt' object table expected" end
+ if type(x1) ~= "number" then return "[ArgErr] 'x1' number expected" end
+ if type(x2) ~= "number" then return "[ArgErr] 'x2' number expected" end
+ if type(ypos) ~= "number" then return "[ArgErr] 'ypos' number expected" end
+ if ay == nil then
+ ay = 0
+ elseif type(ay) ~= "number" then
+ return "[ArgErr] 'ay' number expected"
+ end
+ local chars = assert(txt.codepoint, "[InternalErr] no 'codepoint' field in txt")
+ if #chars == 0 then return "[InternalErr] 'txt' has no chars" end
+ if x1 > x2 then x1, x2 = x2, x1 end -- reorder coordinates
+ local data = self._data
+ data[#data + 1] = 132
+ data[#data + 1] = ay -- anchor relative y-coordinate
+ data[#data + 1] = x1 -- left limit of the text box
+ data[#data + 1] = x2 -- right limit of the text box
+ data[#data + 1] = ypos -- text y-coordinate
+ for _, c in ipairs(chars) do
+ data[#data + 1] = c
+ end
+ data[#data + 1] = 0 -- end string signal
+end
+
+-- experimental code section
+-- new opcodes under assessment
+
+-- [start_text_group] 140
+function gaCanvas:start_text_group() --> err
+ local data = self._data
+ data[#data + 1] = 140
+end
+
+-- [gtext] 141
+function gaCanvas:gtext(chars) --> err
+ if type(chars) ~= "table" then return "[ArgErr] 'chars' table expected" end
+ local data = self._data
+ data[#data + 1] = 141
+ for _, c in ipairs(chars) do
+ data[#data + 1] = c
+ end
+ data[#data + 1] = 0 -- end string signal
+end
+
+-- [gtext_spaced] 142 gap string
+function gaCanvas:gtext_spaced(gap, chars) --> err
+ if type(gap) ~= "number" then return "[ArgErr] 'gap' number expected" end
+ if type(chars) ~= "table" then return "[ArgErr] 'chars' table expected" end
+ local data = self._data
+ data[#data + 1] = 142
+ data[#data + 1] = gap
+ for _, c in ipairs(chars) do
+ data[#data + 1] = c
+ end
+ data[#data + 1] = 0 -- end string signal
+end
+
+-- [gtext_space] 143 gap
+function gaCanvas:gtext_gap(gap) --> err
+ if type(gap) ~= "number" then return "[ArgErr] 'gap' number expected" end
+ local data = self._data
+ data[#data + 1] = 143
+ data[#data + 1] = gap
+end
+
+
+-- [end_text_group] 149 ax ay x y
+function gaCanvas:end_text_group(xpos, ypos, ax, ay) --> err
+ if type(xpos) ~= "number" then return "[ArgErr] 'xpos' number expected" end
+ if type(ypos) ~= "number" then return "[ArgErr] 'ypos' number expected" end
+ if type(ax) ~= "number" then return "[ArgErr] 'ax' number expected" end
+ if type(ay) ~= "number" then return "[ArgErr] 'ay' number expected" end
+ local data = self._data
+ data[#data + 1] = 149
+ data[#data + 1] = ax -- anchor relative x-coordinate
+ data[#data + 1] = ay -- anchor relative y-coordinate
+ data[#data + 1] = xpos -- text x-coordinate
+ data[#data + 1] = ypos -- text y-coordinate
+end
+
+-- amazing...
+function gaCanvas:to_string() --> string
+
+end
+
+function gaCanvas:get_bbox()
+
+end
+
+function gaCanvas:check() --> boolean, err
+
+end
+
+return gaCanvas
diff --git a/macros/luatex/generic/barracuda/src/lib-geo/brcd-libgeo.lua b/macros/luatex/generic/barracuda/src/lib-geo/brcd-libgeo.lua
new file mode 100644
index 0000000000..fd53ca6509
--- /dev/null
+++ b/macros/luatex/generic/barracuda/src/lib-geo/brcd-libgeo.lua
@@ -0,0 +1,289 @@
+
+-- libgeo simple Geometric Library
+-- Copyright (C) 2018 Roberto Giacomelli
+
+-- All dimension must be in scaled point (sp)
+
+local libgeo = {
+ _VERSION = "libgeo v0.0.3",
+ _NAME = "libgeo",
+ _DESCRIPTION = "simple geometric library",
+}
+
+-- VBar class
+-- a pure geometric entity of several infinite vertical lines
+libgeo.Vbar = {}
+local Vbar = libgeo.Vbar
+Vbar.__index = Vbar
+
+-- Vbar costructors
+
+-- VBar costructor from an array [xcenter1, width1, xcenter2, width2, ...]
+function Vbar:from_array(yl_arr) --> <vbar object>
+ assert(type(yl_arr) == "table", "'yline_array' is a mandatory arg")
+ -- stream scanning
+ local i = 1
+ local xlim = 0.0
+ while yl_arr[i] do
+ local x = yl_arr[i]; i = i + 1
+ local w = yl_arr[i]; i = i + 1
+ assert(type(x) == "number", "[InternalErr] not a number")
+ assert(type(w) == "number", "[InternalErr] not a number")
+ xlim = x + w/2
+ end
+ assert(i % 2 == 0, "[InternalErr] the index is not even")
+ assert(i > 0, "[InternalErr] empty array")
+ local o = {
+ _yline = yl_arr, -- [<xcenter>, <width>, ...] flat array
+ _x_lim = xlim, -- right external bounding box coordinates
+ }
+ setmetatable(o, self)
+ return o
+end
+
+-- costructor useful for Code128 encoder
+-- from an integer: 21231 -> binary 11 0 11 000 1 -> symbol 110110001
+-- is_bar :: boolean :: bar or space as first element, default true
+function Vbar:from_int(ngen, mod, is_bar) --> <vbar object>
+ assert(type(ngen) == "number", "Invalid argument for n")
+ assert(type(mod) == "number", "Invalid argument for module width")
+ if is_bar == nil then is_bar = true else
+ assert(type(is_bar) == "boolean", "Invalid argument for is_bar")
+ end
+ -- scan ngen for digits
+ local digits = {}
+ while ngen > 0 do
+ local d = ngen % 10
+ digits[#digits + 1] = d
+ ngen = (ngen - d)/10
+ end
+ local x0 = 0.0 -- axis reference
+ local yl = {}
+ for k = #digits, 1, -1 do
+ local d = digits[k]
+ local w = d*mod -- bar width
+ if is_bar then -- bar
+ yl[#yl + 1] = x0 + w/2
+ yl[#yl + 1] = w
+ end
+ x0 = x0 + w
+ is_bar = not is_bar
+ end
+ local o = {
+ _yline = yl, -- [<xcenter>, <width>, ...] flat array
+ _x_lim = x0, -- right external coordinate
+ }
+ setmetatable(o, self)
+ return o
+end
+
+-- from an integer to read from right to left
+-- 13212 ->rev 21231 ->binary 11 0 11 000 1 -> symbol 110110001
+-- is_bar :: boolean :: bar or space for first, default true
+function Vbar:from_int_revstep(ngen, mod, is_bar) --> <vbar object>
+ assert(type(ngen) == "number", "Invalid argument for n")
+ assert(type(mod) == "number", "Invalid argument for module width")
+ if is_bar == nil then is_bar = true else
+ assert(type(is_bar) == "boolean", "Invalid argument for is_bar")
+ end
+ --
+ local x0 = 0.0 -- axis reference
+ local i = 0
+ local yl = {}
+ while ngen > 0 do
+ local d = ngen % 10 -- first digit
+ local w = d*mod -- bar width
+ if is_bar then -- bar
+ i = i + 1; yl[i] = x0 + w/2
+ i = i + 1; yl[i] = w
+ end
+ x0 = x0 + w
+ is_bar = not is_bar
+ ngen = (ngen - d)/10
+ end
+ assert(not is_bar, "[InternalErr] the last element in not a bar")
+ local o = {
+ _yline = yl, -- [<xcenter>, <width>, ...] flat array
+ _x_lim = x0, -- right external coordinate
+ }
+ setmetatable(o, self)
+ return o
+end
+
+-- costructor useful for Code39 encoder
+-- i.e. 11212 -> rev -> 2 1 2 1 1 -> decodes to -> B w B w b
+-- build a yline array from the integer definition. Digit decoding rule:
+-- mod: b or w => 1 -- narrow bar/space
+-- MOD: B or W => 2 -- wide bar/space
+-- is_bar: the first element is a bar not a space, default to true
+function Vbar:from_int_revpair(ngen, mod, MOD, is_bar) --> <vbar object>
+ assert(type(ngen) == "number", "Invalid argument for n")
+ assert(type(mod) == "number", "Invalid argument for narrow module width")
+ assert(type(MOD) == "number", "Invalid argument for wide module width")
+ assert(mod < MOD, "Not ordered narrow/Wide values")
+ if is_bar == nil then
+ is_bar = true
+ else
+ assert(type(is_bar) == "boolean", "Invalid argument for 'is_bar'")
+ end
+ local yl = {}
+ local x0 = 0.0
+ local k = 0
+ while ngen > 0 do
+ local d = ngen % 10 -- digit
+ ngen = (ngen - d)/10
+ local w; if d == 1 then
+ w = mod
+ elseif d == 2 then
+ w = MOD
+ end; assert(w, "[InternalErr] Allowed digits are only 1 or 2")
+ if is_bar then -- bars
+ k = k + 1; yl[k] = x0 + w/2 -- xcenter
+ k = k + 1; yl[k] = w -- width
+ end
+ is_bar = not is_bar
+ x0 = x0 + w
+ end
+ assert(not is_bar, "[InternalErr] the last element is not a bar")
+ local o = {
+ _yline = yl, -- [<xcenter>, <width>, ...] flat array
+ _x_lim = x0, -- external x coordinate
+ }
+ setmetatable(o, self)
+ return o
+end
+
+-- return a Vbar inteleaving to sequence narrow/Wide
+-- tbar, tspace = {boolean sequence}, true -> narrow, false -> Wide
+function Vbar:from_two_tab(tbar, tspace, mod, MOD) --> <vbar object>
+ assert(type(tbar) == "table", "tbar must be a table")
+ assert(type(tspace) == "table", "tspace must be a table")
+ assert(#tbar == #tspace, "tbar and tspace must be longer the same")
+ assert(type(mod) == "number", "Invalid argument for narrow module width")
+ assert(type(MOD) == "number", "Invalid argument for wide module width")
+ assert(mod < MOD, "Not ordered narrow/Wide values")
+ local x0 = 0.0 -- x-coordinate
+ local yl = {}
+ for i = 1, #tbar do
+ local is_narrow = tbar[i]
+ assert(type(is_narrow) == "boolean", "[InternalErr] found a not boolean value")
+ if is_narrow then
+ yl[#yl + 1] = x0 + mod/2 -- bar x-coordinate
+ yl[#yl + 1] = mod -- bar width
+ x0 = x0 + mod
+ else
+ yl[#yl + 1] = x0 + MOD/2 -- bar x-coordinate
+ yl[#yl + 1] = MOD -- bar width
+ x0 = x0 + MOD
+ end
+ local is_narrow_space = tspace[i]
+ assert(type(is_narrow_space) == "boolean", "[InternalErr] found a not boolean value")
+ if is_narrow_space then
+ x0 = x0 + mod
+ else
+ x0 = x0 + MOD
+ end
+ end
+ local o = {
+ _yline = yl, -- [<xcenter>, <width>, ...] flat array
+ _x_lim = x0, -- external x coordinate
+ }
+ setmetatable(o, self)
+ return o
+end
+
+-- Text class
+
+libgeo.Text = {}
+local Text = libgeo.Text
+Text.__index = Text
+
+-- costructors
+-- internally it keeps text as a sequence of codepoint
+function Text:from_string(s) --> object
+ assert(type(s) == "string", "[ArgErr] 's' not a valid string")
+ assert(#s > 0, "[Err] 's' empty string not allowed")
+
+ local cp = {} -- codepoint array
+ for b in string.gmatch(s, ".") do
+ cp[#cp + 1] = string.byte(b)
+ end
+ local o = {
+ codepoint = cp,
+ }
+ setmetatable(o, self)
+ return o
+end
+
+-- arr, array of single digit number
+-- i start index
+-- j stop index
+function Text:from_digit_array(arr, i, j) --> object
+ assert(type(arr) == "table", "[ArgErr] 'arr' not a table")
+ assert(#arr > 0, "[ArgErr] 'arr' is an empty array")
+ local cp = {} -- codepoint array
+ if i ~= nil then
+ assert(type(i) == "number", "[ArgErr] 'i' is not a number")
+ else
+ i = 1
+ end
+ if j ~= nil then
+ assert(type(j) == "number", "[ArgErr] 'j' is not a number")
+ assert(i <= j, "[ArgErr] not suitable pair of array index")
+ else
+ j = #arr
+ end
+ for k = i, j do
+ local d = arr[k]
+ assert(type(d) == "number", "[ArgErr] array contains a not number element")
+ assert(d == math.floor(d), "[ArgErr] array contains a not integer number")
+ assert(d >= 0 or d < 10, "[ArgErr] array contains a not single digit number")
+ cp[#cp + 1] = d + 48
+ end
+ local o = {
+ codepoint = cp,
+ }
+ setmetatable(o, self)
+ return o
+end
+
+
+-- from an array of chars
+function Text:from_chars(chars) --> object
+ assert(type(chars) == "table", "[ArgErr] 'chars' must be a table")
+ local arr = {}
+ for _, c in ipairs(chars) do
+ arr[#arr + 1] = string.byte(c)
+ end
+ local o = {
+ codepoint = arr,
+ }
+ setmetatable(o, self)
+ return o
+end
+
+-- provide an integer to build a Text object
+function Text:from_int(n) --> object
+ assert(type(n) == "number", "[ArgErr] 'n' must be a number")
+ assert( n > 0, "[Err] 'n' must be positive")
+ assert( n == math.floor(n), "[Err] 'n' must be an integer")
+ local cp = {}
+ while n > 0 do
+ local d = n % 10
+ cp[#cp + 1] = d + 48
+ n = (n - d)/10
+ end
+ local digits = #cp
+ for i = 1, digits/2 do -- reverse the array
+ local d = cp[digits - i + 1]
+ cp[digits - i + 1] = cp[i]
+ cp[i] = d
+ end
+ local o = {
+ codepoint = cp,
+ }
+ setmetatable(o, self)
+ return o
+end
+
+return libgeo