summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/fontloader/runtime/fontloader-basics-gen.lua38
-rw-r--r--src/fontloader/runtime/fontloader-reference.lua17888
2 files changed, 12577 insertions, 5349 deletions
diff --git a/src/fontloader/runtime/fontloader-basics-gen.lua b/src/fontloader/runtime/fontloader-basics-gen.lua
index c4d6536..c298f6d 100644
--- a/src/fontloader/runtime/fontloader-basics-gen.lua
+++ b/src/fontloader/runtime/fontloader-basics-gen.lua
@@ -63,15 +63,19 @@ logs = {
}
callbacks = {
- register = function(n,f) return callback.register(n,f) end,
+ register = function(n,f)
+ return callback.register(n,f)
+ end,
}
-utilities = {
- storage = {
- allocate = function(t) return t or { } end,
- mark = function(t) return t or { } end,
- },
+utilities = utilities or { } utilities.storage = {
+ allocate = function(t)
+ return t or { }
+ end,
+ mark = function(t)
+ return t or { }
+ end,
}
characters = characters or {
@@ -355,12 +359,28 @@ end
--
+-- function table.setmetatableindex(t,f)
+-- if type(t) ~= "table" then
+-- f = f or t
+-- t = { }
+-- end
+-- setmetatable(t,{ __index = f })
+-- return t
+-- end
+
function table.setmetatableindex(t,f)
if type(t) ~= "table" then
- f = f or t
- t = { }
+ f, t = t, { }
+ end
+ local m = getmetatable(t)
+ if f == "table" then
+ f = function(t,k) local v = { } t[k] = v return v end
+ end
+ if m then
+ m.__index = f
+ else
+ setmetatable(t,{ __index = f })
end
- setmetatable(t,{ __index = f })
return t
end
diff --git a/src/fontloader/runtime/fontloader-reference.lua b/src/fontloader/runtime/fontloader-reference.lua
index b135c44..e4ae182 100644
--- a/src/fontloader/runtime/fontloader-reference.lua
+++ b/src/fontloader/runtime/fontloader-reference.lua
@@ -1,6 +1,6 @@
-- merged file : c:/data/develop/context/sources/luatex-fonts-merged.lua
-- parent file : c:/data/develop/context/sources/luatex-fonts.lua
--- merge date : 01/08/16 19:09:31
+-- merge date : 04/04/16 13:06:24
do -- begin closure to overcome local limits and interference
@@ -11,10 +11,14 @@ if not modules then modules={} end modules ['l-lua']={
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
-local major,minor=string.match(_VERSION,"^[^%d]+(%d+)%.(%d+).*$")
-_MAJORVERSION=tonumber(major) or 5
-_MINORVERSION=tonumber(minor) or 1
+_MAJORVERSION,_MINORVERSION=string.match(_VERSION,"^[^%d]+(%d+)%.(%d+).*$")
+_MAJORVERSION=tonumber(_MAJORVERSION) or 5
+_MINORVERSION=tonumber(_MINORVERSION) or 1
_LUAVERSION=_MAJORVERSION+_MINORVERSION/10
+if _LUAVERSION<5.2 and jit then
+ _MINORVERSION=2
+ _LUAVERSION=5.2
+end
if not lpeg then
lpeg=require("lpeg")
end
@@ -3470,6 +3474,149 @@ end -- closure
do -- begin closure to overcome local limits and interference
+if not modules then modules={} end modules ['util-fil']={
+ version=1.001,
+ comment="companion to luat-lib.mkiv",
+ author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright="PRAGMA ADE / ConTeXt Development Team",
+ license="see context related readme files"
+}
+local byte=string.byte
+local extract=bit32.extract
+utilities=utilities or {}
+local files={}
+utilities.files=files
+local zerobased={}
+function files.open(filename,zb)
+ local f=io.open(filename,"rb")
+ if f then
+ zerobased[f]=zb or false
+ end
+ return f
+end
+function files.close(f)
+ zerobased[f]=nil
+ f:close()
+end
+function files.size(f)
+ return f:seek("end")
+end
+function files.setposition(f,n)
+ if zerobased[f] then
+ f:seek("set",n)
+ else
+ f:seek("set",n-1)
+ end
+end
+function files.getposition(f)
+ if zerobased[f] then
+ return f:seek()
+ else
+ return f:seek()+1
+ end
+end
+function files.look(f,n,chars)
+ local p=f:seek()
+ local s=f:read(n)
+ f:seek("set",p)
+ if chars then
+ return s
+ else
+ return byte(s,1,#s)
+ end
+end
+function files.skip(f,n)
+ if n==1 then
+ f:read(n)
+ else
+ f:seek("set",f:seek()+n)
+ end
+end
+function files.readbyte(f)
+ return byte(f:read(1))
+end
+function files.readbytes(f,n)
+ return byte(f:read(n),1,n)
+end
+function files.readchar(f)
+ return f:read(1)
+end
+function files.readstring(f,n)
+ return f:read(n or 1)
+end
+function files.readinteger1(f)
+ local n=byte(f:read(1))
+ if n>=0x80 then
+ return n-0xFF-1
+ else
+ return n
+ end
+end
+files.readcardinal1=files.readbyte
+files.readcardinal=files.readcardinal1
+files.readinteger=files.readinteger1
+function files.readcardinal2(f)
+ local a,b=byte(f:read(2),1,2)
+ return 0x100*a+b
+end
+function files.readinteger2(f)
+ local a,b=byte(f:read(2),1,2)
+ local n=0x100*a+b
+ if n>=0x8000 then
+ return n-0xFFFF-1
+ else
+ return n
+ end
+end
+function files.readcardinal3(f)
+ local a,b,c=byte(f:read(3),1,3)
+ return 0x10000*a+0x100*b+c
+end
+function files.readcardinal4(f)
+ local a,b,c,d=byte(f:read(4),1,4)
+ return 0x1000000*a+0x10000*b+0x100*c+d
+end
+function files.readinteger4(f)
+ local a,b,c,d=byte(f:read(4),1,4)
+ local n=0x1000000*a+0x10000*b+0x100*c+d
+ if n>=0x8000000 then
+ return n-0xFFFFFFFF-1
+ else
+ return n
+ end
+end
+function files.readfixed4(f)
+ local a,b,c,d=byte(f:read(4),1,4)
+ local n=0x100*a+b
+ if n>=0x8000 then
+ return n-0xFFFF-1+(0x100*c+d)/0xFFFF
+ else
+ return n+(0x100*c+d)/0xFFFF
+ end
+end
+function files.read2dot14(f)
+ local a,b=byte(f:read(2),1,2)
+ local n=0x100*a+b
+ local m=extract(n,0,30)
+ if n>0x7FFF then
+ n=extract(n,30,2)
+ return m/0x4000-4
+ else
+ n=extract(n,30,2)
+ return n+m/0x4000
+ end
+end
+function files.skipshort(f,n)
+ f:read(2*(n or 1))
+end
+function files.skiplong(f,n)
+ f:read(4*(n or 1))
+end
+
+end -- closure
+
+do -- begin closure to overcome local limits and interference
+
if not modules then modules={} end modules ['luat-basics-gen']={
version=1.100,
comment="companion to luatex-*.tex",
@@ -3525,13 +3672,17 @@ logs={
report=dummyfunction,
}
callbacks={
- register=function(n,f) return callback.register(n,f) end,
+ register=function(n,f)
+ return callback.register(n,f)
+ end,
}
-utilities={
- storage={
- allocate=function(t) return t or {} end,
- mark=function(t) return t or {} end,
- },
+utilities=utilities or {} utilities.storage={
+ allocate=function(t)
+ return t or {}
+ end,
+ mark=function(t)
+ return t or {}
+ end,
}
characters=characters or {
data={}
@@ -3731,10 +3882,17 @@ function caches.compile(data,luaname,lucname)
end
function table.setmetatableindex(t,f)
if type(t)~="table" then
- f=f or t
- t={}
+ f,t=t,{}
+ end
+ local m=getmetatable(t)
+ if f=="table" then
+ f=function(t,k) local v={} t[k]=v return v end
+ end
+ if m then
+ m.__index=f
+ else
+ setmetatable(t,{ __index=f })
end
- setmetatable(t,{ __index=f })
return t
end
arguments={}
@@ -4028,6 +4186,42 @@ nuts.getlist=direct.getlist
nuts.setlist=direct.setlist or function(n,l) setfield(n,"list",l) end
nuts.getleader=direct.getleader
nuts.setleader=direct.setleader or function(n,l) setfield(n,"leader",l) end
+if not direct.is_glyph then
+ local getchar=direct.getchar
+ local getid=direct.getid
+ local getfont=direct.getfont
+ local glyph_code=nodes.nodecodes.glyph
+ function direct.is_glyph(n,f)
+ local id=getid(n)
+ if id==glyph_code then
+ if f and getfont(n)==f then
+ return getchar(n)
+ else
+ return false
+ end
+ else
+ return nil,id
+ end
+ end
+ function direct.is_char(n,f)
+ local id=getid(n)
+ if id==glyph_code then
+ if getsubtype(n)>=256 then
+ return false
+ elseif f and getfont(n)==f then
+ return getchar(n)
+ else
+ return false
+ end
+ else
+ return nil,id
+ end
+ end
+end
+nuts.ischar=direct.is_char
+nuts.is_char=direct.is_char
+nuts.isglyph=direct.is_glyph
+nuts.is_glyph=direct.is_glyph
nuts.insert_before=direct.insert_before
nuts.insert_after=direct.insert_after
nuts.delete=direct.delete
@@ -4081,6 +4275,760 @@ end -- closure
do -- begin closure to overcome local limits and interference
+
+characters=characters or {}
+characters.blockrange={}
+characters.classifiers={
+ [1536]=4,
+ [1537]=4,
+ [1538]=4,
+ [1539]=4,
+ [1540]=4,
+ [1541]=4,
+ [1542]=6,
+ [1543]=6,
+ [1544]=4,
+ [1545]=6,
+ [1546]=6,
+ [1547]=4,
+ [1548]=6,
+ [1549]=6,
+ [1550]=6,
+ [1551]=6,
+ [1552]=5,
+ [1553]=5,
+ [1554]=5,
+ [1555]=5,
+ [1556]=5,
+ [1557]=5,
+ [1558]=5,
+ [1559]=5,
+ [1560]=5,
+ [1561]=5,
+ [1562]=5,
+ [1563]=6,
+ [1564]=6,
+ [1566]=6,
+ [1567]=6,
+ [1568]=2,
+ [1569]=4,
+ [1570]=3,
+ [1571]=3,
+ [1572]=3,
+ [1573]=3,
+ [1574]=2,
+ [1575]=3,
+ [1576]=2,
+ [1577]=3,
+ [1578]=2,
+ [1579]=2,
+ [1580]=2,
+ [1581]=2,
+ [1582]=2,
+ [1583]=3,
+ [1584]=3,
+ [1585]=3,
+ [1586]=3,
+ [1587]=2,
+ [1588]=2,
+ [1589]=2,
+ [1590]=2,
+ [1591]=2,
+ [1592]=2,
+ [1593]=2,
+ [1594]=2,
+ [1595]=2,
+ [1596]=2,
+ [1597]=2,
+ [1598]=2,
+ [1599]=2,
+ [1600]=2,
+ [1601]=2,
+ [1602]=2,
+ [1603]=2,
+ [1604]=2,
+ [1605]=2,
+ [1606]=2,
+ [1607]=2,
+ [1608]=3,
+ [1609]=2,
+ [1610]=2,
+ [1611]=5,
+ [1612]=5,
+ [1613]=5,
+ [1614]=5,
+ [1615]=5,
+ [1616]=5,
+ [1617]=5,
+ [1618]=5,
+ [1619]=5,
+ [1620]=5,
+ [1621]=5,
+ [1622]=5,
+ [1623]=5,
+ [1624]=5,
+ [1625]=5,
+ [1626]=5,
+ [1627]=5,
+ [1628]=5,
+ [1629]=5,
+ [1630]=5,
+ [1631]=5,
+ [1632]=6,
+ [1633]=6,
+ [1634]=6,
+ [1635]=6,
+ [1636]=6,
+ [1637]=6,
+ [1638]=6,
+ [1639]=6,
+ [1640]=6,
+ [1641]=6,
+ [1642]=6,
+ [1643]=6,
+ [1644]=6,
+ [1645]=6,
+ [1646]=2,
+ [1647]=2,
+ [1648]=5,
+ [1649]=3,
+ [1650]=3,
+ [1651]=3,
+ [1652]=4,
+ [1653]=3,
+ [1654]=3,
+ [1655]=3,
+ [1656]=2,
+ [1657]=2,
+ [1658]=2,
+ [1659]=2,
+ [1660]=2,
+ [1661]=2,
+ [1662]=2,
+ [1663]=2,
+ [1664]=2,
+ [1665]=2,
+ [1666]=2,
+ [1667]=2,
+ [1668]=2,
+ [1669]=2,
+ [1670]=2,
+ [1671]=2,
+ [1672]=3,
+ [1673]=3,
+ [1674]=3,
+ [1675]=3,
+ [1676]=3,
+ [1677]=3,
+ [1678]=3,
+ [1679]=3,
+ [1680]=3,
+ [1681]=3,
+ [1682]=3,
+ [1683]=3,
+ [1684]=3,
+ [1685]=3,
+ [1686]=3,
+ [1687]=3,
+ [1688]=3,
+ [1689]=3,
+ [1690]=2,
+ [1691]=2,
+ [1692]=2,
+ [1693]=2,
+ [1694]=2,
+ [1695]=2,
+ [1696]=2,
+ [1697]=2,
+ [1698]=2,
+ [1699]=2,
+ [1700]=2,
+ [1701]=2,
+ [1702]=2,
+ [1703]=2,
+ [1704]=2,
+ [1705]=2,
+ [1706]=2,
+ [1707]=2,
+ [1708]=2,
+ [1709]=2,
+ [1710]=2,
+ [1711]=2,
+ [1712]=2,
+ [1713]=2,
+ [1714]=2,
+ [1715]=2,
+ [1716]=2,
+ [1717]=2,
+ [1718]=2,
+ [1719]=2,
+ [1720]=2,
+ [1721]=2,
+ [1722]=2,
+ [1723]=2,
+ [1724]=2,
+ [1725]=2,
+ [1726]=2,
+ [1727]=2,
+ [1728]=3,
+ [1729]=2,
+ [1730]=2,
+ [1731]=3,
+ [1732]=3,
+ [1733]=3,
+ [1734]=3,
+ [1735]=3,
+ [1736]=3,
+ [1737]=3,
+ [1738]=3,
+ [1739]=3,
+ [1740]=2,
+ [1741]=3,
+ [1742]=2,
+ [1743]=3,
+ [1744]=2,
+ [1745]=2,
+ [1746]=3,
+ [1747]=3,
+ [1748]=6,
+ [1749]=3,
+ [1750]=5,
+ [1751]=5,
+ [1752]=5,
+ [1753]=5,
+ [1754]=5,
+ [1755]=5,
+ [1756]=5,
+ [1757]=4,
+ [1758]=6,
+ [1759]=5,
+ [1760]=5,
+ [1761]=5,
+ [1762]=5,
+ [1763]=5,
+ [1764]=5,
+ [1765]=6,
+ [1766]=6,
+ [1767]=5,
+ [1768]=5,
+ [1769]=6,
+ [1770]=5,
+ [1771]=5,
+ [1772]=5,
+ [1773]=5,
+ [1774]=3,
+ [1775]=3,
+ [1776]=6,
+ [1777]=6,
+ [1778]=6,
+ [1779]=6,
+ [1780]=6,
+ [1781]=6,
+ [1782]=6,
+ [1783]=6,
+ [1784]=6,
+ [1785]=6,
+ [1786]=2,
+ [1787]=2,
+ [1788]=2,
+ [1789]=6,
+ [1790]=6,
+ [1791]=2,
+ [1792]=6,
+ [1793]=6,
+ [1794]=6,
+ [1795]=6,
+ [1796]=6,
+ [1797]=6,
+ [1798]=6,
+ [1799]=6,
+ [1800]=6,
+ [1801]=6,
+ [1802]=6,
+ [1803]=6,
+ [1804]=6,
+ [1805]=6,
+ [1807]=6,
+ [1808]=3,
+ [1809]=5,
+ [1810]=2,
+ [1811]=2,
+ [1812]=2,
+ [1813]=3,
+ [1814]=3,
+ [1815]=3,
+ [1816]=3,
+ [1817]=3,
+ [1818]=2,
+ [1819]=2,
+ [1820]=2,
+ [1821]=2,
+ [1822]=3,
+ [1823]=2,
+ [1824]=2,
+ [1825]=2,
+ [1826]=2,
+ [1827]=2,
+ [1828]=2,
+ [1829]=2,
+ [1830]=2,
+ [1831]=2,
+ [1832]=3,
+ [1833]=2,
+ [1834]=3,
+ [1835]=2,
+ [1836]=3,
+ [1837]=2,
+ [1838]=2,
+ [1839]=3,
+ [1840]=5,
+ [1841]=5,
+ [1842]=5,
+ [1843]=5,
+ [1844]=5,
+ [1845]=5,
+ [1846]=5,
+ [1847]=5,
+ [1848]=5,
+ [1849]=5,
+ [1850]=5,
+ [1851]=5,
+ [1852]=5,
+ [1853]=5,
+ [1854]=5,
+ [1855]=5,
+ [1856]=5,
+ [1857]=5,
+ [1858]=5,
+ [1859]=5,
+ [1860]=5,
+ [1861]=5,
+ [1862]=5,
+ [1863]=5,
+ [1864]=5,
+ [1865]=5,
+ [1866]=5,
+ [1869]=3,
+ [1870]=2,
+ [1871]=2,
+ [1872]=2,
+ [1873]=2,
+ [1874]=2,
+ [1875]=2,
+ [1876]=2,
+ [1877]=2,
+ [1878]=2,
+ [1879]=2,
+ [1880]=2,
+ [1881]=3,
+ [1882]=3,
+ [1883]=3,
+ [1884]=2,
+ [1885]=2,
+ [1886]=2,
+ [1887]=2,
+ [1888]=2,
+ [1889]=2,
+ [1890]=2,
+ [1891]=2,
+ [1892]=2,
+ [1893]=2,
+ [1894]=2,
+ [1895]=2,
+ [1896]=2,
+ [1897]=2,
+ [1898]=2,
+ [1899]=3,
+ [1900]=3,
+ [1901]=2,
+ [1902]=2,
+ [1903]=2,
+ [1904]=2,
+ [1905]=3,
+ [1906]=2,
+ [1907]=3,
+ [1908]=3,
+ [1909]=2,
+ [1910]=2,
+ [1911]=2,
+ [1912]=3,
+ [1913]=3,
+ [1914]=2,
+ [1915]=2,
+ [1916]=2,
+ [1917]=2,
+ [1918]=2,
+ [1919]=2,
+ [1984]=6,
+ [1985]=6,
+ [1986]=6,
+ [1987]=6,
+ [1988]=6,
+ [1989]=6,
+ [1990]=6,
+ [1991]=6,
+ [1992]=6,
+ [1993]=6,
+ [1994]=2,
+ [1995]=2,
+ [1996]=2,
+ [1997]=2,
+ [1998]=2,
+ [1999]=2,
+ [2000]=2,
+ [2001]=2,
+ [2002]=2,
+ [2003]=2,
+ [2004]=2,
+ [2005]=2,
+ [2006]=2,
+ [2007]=2,
+ [2008]=2,
+ [2009]=2,
+ [2010]=2,
+ [2011]=2,
+ [2012]=2,
+ [2013]=2,
+ [2014]=2,
+ [2015]=2,
+ [2016]=2,
+ [2017]=2,
+ [2018]=2,
+ [2019]=2,
+ [2020]=2,
+ [2021]=2,
+ [2022]=2,
+ [2023]=2,
+ [2024]=2,
+ [2025]=2,
+ [2026]=2,
+ [2027]=5,
+ [2028]=5,
+ [2029]=5,
+ [2030]=5,
+ [2031]=5,
+ [2032]=5,
+ [2033]=5,
+ [2034]=5,
+ [2035]=5,
+ [2036]=6,
+ [2037]=6,
+ [2038]=6,
+ [2039]=6,
+ [2040]=6,
+ [2041]=6,
+ [2042]=2,
+ [2112]=3,
+ [2113]=2,
+ [2114]=2,
+ [2115]=2,
+ [2116]=2,
+ [2117]=2,
+ [2118]=3,
+ [2119]=3,
+ [2120]=2,
+ [2121]=3,
+ [2122]=2,
+ [2123]=2,
+ [2124]=2,
+ [2125]=2,
+ [2126]=2,
+ [2127]=2,
+ [2128]=2,
+ [2129]=2,
+ [2130]=2,
+ [2131]=2,
+ [2132]=3,
+ [2133]=2,
+ [2134]=4,
+ [2135]=4,
+ [2136]=4,
+ [2208]=2,
+ [2209]=2,
+ [2210]=2,
+ [2211]=2,
+ [2212]=2,
+ [2213]=2,
+ [2214]=2,
+ [2215]=2,
+ [2216]=2,
+ [2217]=2,
+ [2218]=3,
+ [2219]=3,
+ [2220]=3,
+ [2221]=4,
+ [2222]=3,
+ [2223]=2,
+ [2224]=2,
+ [2225]=3,
+ [2226]=3,
+ [2227]=2,
+ [2228]=2,
+ [6150]=4,
+ [6151]=2,
+ [6154]=2,
+ [6158]=4,
+ [6176]=2,
+ [6177]=2,
+ [6178]=2,
+ [6179]=2,
+ [6180]=2,
+ [6181]=2,
+ [6182]=2,
+ [6183]=2,
+ [6184]=2,
+ [6185]=2,
+ [6186]=2,
+ [6187]=2,
+ [6188]=2,
+ [6189]=2,
+ [6190]=2,
+ [6191]=2,
+ [6192]=2,
+ [6193]=2,
+ [6194]=2,
+ [6195]=2,
+ [6196]=2,
+ [6197]=2,
+ [6198]=2,
+ [6199]=2,
+ [6200]=2,
+ [6201]=2,
+ [6202]=2,
+ [6203]=2,
+ [6204]=2,
+ [6205]=2,
+ [6206]=2,
+ [6207]=2,
+ [6208]=2,
+ [6209]=2,
+ [6210]=2,
+ [6211]=2,
+ [6212]=2,
+ [6213]=2,
+ [6214]=2,
+ [6215]=2,
+ [6216]=2,
+ [6217]=2,
+ [6218]=2,
+ [6219]=2,
+ [6220]=2,
+ [6221]=2,
+ [6222]=2,
+ [6223]=2,
+ [6224]=2,
+ [6225]=2,
+ [6226]=2,
+ [6227]=2,
+ [6228]=2,
+ [6229]=2,
+ [6230]=2,
+ [6231]=2,
+ [6232]=2,
+ [6233]=2,
+ [6234]=2,
+ [6235]=2,
+ [6236]=2,
+ [6237]=2,
+ [6238]=2,
+ [6239]=2,
+ [6240]=2,
+ [6241]=2,
+ [6242]=2,
+ [6243]=2,
+ [6244]=2,
+ [6245]=2,
+ [6246]=2,
+ [6247]=2,
+ [6248]=2,
+ [6249]=2,
+ [6250]=2,
+ [6251]=2,
+ [6252]=2,
+ [6253]=2,
+ [6254]=2,
+ [6255]=2,
+ [6256]=2,
+ [6257]=2,
+ [6258]=2,
+ [6259]=2,
+ [6260]=2,
+ [6261]=2,
+ [6262]=2,
+ [6263]=2,
+ [6272]=4,
+ [6273]=4,
+ [6274]=4,
+ [6275]=4,
+ [6276]=4,
+ [6277]=4,
+ [6278]=4,
+ [6279]=2,
+ [6280]=2,
+ [6281]=2,
+ [6282]=2,
+ [6283]=2,
+ [6284]=2,
+ [6285]=2,
+ [6286]=2,
+ [6287]=2,
+ [6288]=2,
+ [6289]=2,
+ [6290]=2,
+ [6291]=2,
+ [6292]=2,
+ [6293]=2,
+ [6294]=2,
+ [6295]=2,
+ [6296]=2,
+ [6297]=2,
+ [6298]=2,
+ [6299]=2,
+ [6300]=2,
+ [6301]=2,
+ [6302]=2,
+ [6303]=2,
+ [6304]=2,
+ [6305]=2,
+ [6306]=2,
+ [6307]=2,
+ [6308]=2,
+ [6309]=2,
+ [6310]=2,
+ [6311]=2,
+ [6312]=2,
+ [6314]=2,
+ [8204]=4,
+ [8205]=2,
+ [8294]=4,
+ [8295]=4,
+ [8296]=4,
+ [8297]=4,
+ [43072]=2,
+ [43073]=2,
+ [43074]=2,
+ [43075]=2,
+ [43076]=2,
+ [43077]=2,
+ [43078]=2,
+ [43079]=2,
+ [43080]=2,
+ [43081]=2,
+ [43082]=2,
+ [43083]=2,
+ [43084]=2,
+ [43085]=2,
+ [43086]=2,
+ [43087]=2,
+ [43088]=2,
+ [43089]=2,
+ [43090]=2,
+ [43091]=2,
+ [43092]=2,
+ [43093]=2,
+ [43094]=2,
+ [43095]=2,
+ [43096]=2,
+ [43097]=2,
+ [43098]=2,
+ [43099]=2,
+ [43100]=2,
+ [43101]=2,
+ [43102]=2,
+ [43103]=2,
+ [43104]=2,
+ [43105]=2,
+ [43106]=2,
+ [43107]=2,
+ [43108]=2,
+ [43109]=2,
+ [43110]=2,
+ [43111]=2,
+ [43112]=2,
+ [43113]=2,
+ [43114]=2,
+ [43115]=2,
+ [43116]=2,
+ [43117]=2,
+ [43118]=2,
+ [43119]=2,
+ [43120]=2,
+ [43121]=2,
+ [43122]=1,
+ [43123]=4,
+ [68288]=2,
+ [68289]=2,
+ [68290]=2,
+ [68291]=2,
+ [68292]=2,
+ [68293]=3,
+ [68294]=4,
+ [68295]=3,
+ [68296]=4,
+ [68297]=3,
+ [68298]=3,
+ [68299]=4,
+ [68300]=4,
+ [68301]=1,
+ [68302]=3,
+ [68303]=3,
+ [68304]=3,
+ [68305]=3,
+ [68306]=3,
+ [68307]=2,
+ [68308]=2,
+ [68309]=2,
+ [68310]=2,
+ [68311]=1,
+ [68312]=2,
+ [68313]=2,
+ [68314]=2,
+ [68315]=2,
+ [68316]=2,
+ [68317]=3,
+ [68318]=2,
+ [68319]=2,
+ [68320]=2,
+ [68321]=3,
+ [68322]=4,
+ [68323]=4,
+ [68324]=3,
+ [68331]=2,
+ [68332]=2,
+ [68333]=2,
+ [68334]=2,
+ [68335]=3,
+ [68480]=2,
+ [68481]=3,
+ [68482]=2,
+ [68483]=3,
+ [68484]=3,
+ [68485]=3,
+ [68486]=2,
+ [68487]=2,
+ [68488]=2,
+ [68489]=3,
+ [68490]=2,
+ [68491]=2,
+ [68492]=3,
+ [68493]=2,
+ [68494]=3,
+ [68495]=3,
+ [68496]=2,
+ [68497]=3,
+ [68521]=3,
+ [68522]=3,
+ [68523]=3,
+ [68524]=3,
+ [68525]=2,
+ [68526]=2,
+ [68527]=4,
+}
+
+end -- closure
+
+do -- begin closure to overcome local limits and interference
+
if not modules then modules={} end modules ['font-ini']={
version=1.001,
comment="companion to font-ini.mkiv",
@@ -5489,25 +6437,22 @@ end
local f_single=formatters["%04X"]
local f_double=formatters["%04X%04X"]
local function tounicode16(unicode,name)
- if unicode<0x10000 then
+ if unicode<0xD7FF or (unicode>0xDFFF and unicode<=0xFFFF) then
return f_single(unicode)
- elseif unicode<0x1FFFFFFFFF then
- return f_double(floor(unicode/1024),unicode%1024+0xDC00)
else
- report_fonts("can't convert %a in %a into tounicode",unicode,name)
+ unicode=unicode-0x10000
+ return f_double(floor(unicode/1024)+0xD800,unicode%1024+0xDC00)
end
end
local function tounicode16sequence(unicodes,name)
local t={}
for l=1,#unicodes do
local u=unicodes[l]
- if u<0x10000 then
+ if u<0xD7FF or (u>0xDFFF and u<=0xFFFF) then
t[l]=f_single(u)
- elseif unicode<0x1FFFFFFFFF then
- t[l]=f_double(floor(u/1024),u%1024+0xDC00)
else
- report_fonts ("can't convert %a in %a into tounicode",u,name)
- return
+ u=u-0x10000
+ t[l]=f_double(floor(u/1024)+0xD800,u%1024+0xDC00)
end
end
return concat(t)
@@ -5517,23 +6462,20 @@ local function tounicode(unicode,name)
local t={}
for l=1,#unicode do
local u=unicode[l]
- if u<0x10000 then
+ if u<0xD7FF or (u>0xDFFF and u<=0xFFFF) then
t[l]=f_single(u)
- elseif u<0x1FFFFFFFFF then
- t[l]=f_double(floor(u/1024),u%1024+0xDC00)
else
- report_fonts ("can't convert %a in %a into tounicode",u,name)
- return
+ u=u-0x10000
+ t[l]=f_double(floor(u/1024)+0xD800,u%1024+0xDC00)
end
end
return concat(t)
else
- if unicode<0x10000 then
+ if unicode<0xD7FF or (unicode>0xDFFF and unicode<=0xFFFF) then
return f_single(unicode)
- elseif unicode<0x1FFFFFFFFF then
- return f_double(floor(unicode/1024),unicode%1024+0xDC00)
else
- report_fonts("can't convert %a in %a into tounicode",unicode,name)
+ unicode=unicode-0x10000
+ return f_double(floor(unicode/1024)+0xD800,unicode%1024+0xDC00)
end
end
end
@@ -5542,7 +6484,7 @@ local function fromunicode16(str)
return tonumber(str,16)
else
local l,r=match(str,"(....)(....)")
- return (tonumber(l,16))*0x400+tonumber(r,16)-0xDC00
+ return 0x10000+(tonumber(l,16)-0xD800)*0x400+tonumber(r,16)-0xDC00
end
end
mappings.makenameparser=makenameparser
@@ -5775,10 +6717,9 @@ local fonts=fonts
fonts.names=fonts.names or {}
fonts.names.version=1.001
fonts.names.basename="luatex-fonts-names"
-fonts.names.new_to_old={}
-fonts.names.old_to_new={}
fonts.names.cache=containers.define("fonts","data",fonts.names.version,true)
-local data,loaded=nil,false
+local data=nil
+local loaded=false
local fileformats={ "lua","tex","other text files" }
function fonts.names.reportmissingbase()
texio.write("<missing font database, run: mtxrun --script fonts --reload --simple>")
@@ -5864,7 +6805,8 @@ tfm.maxnestingsize=65536*1024
local tfmfeatures=constructors.newfeatures("tfm")
local registertfmfeature=tfmfeatures.register
constructors.resolvevirtualtoo=false
-fonts.formats.tfm="type1"
+fonts.formats.tfm="type1"
+fonts.formats.ofm="type1"
function tfm.setfeatures(tfmdata,features)
local okay=constructors.initializefeatures("tfm",tfmdata,features,trace_features,report_tfm)
if okay then
@@ -5990,6 +6932,7 @@ function readers.tfm(specification)
end
return check_tfm(specification,fullname)
end
+readers.ofm=readers.tfm
end -- closure
@@ -7018,46 +7961,6 @@ end -- closure
do -- begin closure to overcome local limits and interference
-if not modules then modules={} end modules ['luatex-fonts-tfm']={
- version=1.001,
- comment="companion to luatex-*.tex",
- author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright="PRAGMA ADE / ConTeXt Development Team",
- license="see context related readme files"
-}
-if context then
- texio.write_nl("fatal error: this module is not for context")
- os.exit()
-end
-local fonts=fonts
-local tfm={}
-fonts.handlers.tfm=tfm
-fonts.formats.tfm="type1"
-function fonts.readers.tfm(specification)
- local fullname=specification.filename or ""
- if fullname=="" then
- local forced=specification.forced or ""
- if forced~="" then
- fullname=specification.name.."."..forced
- else
- fullname=specification.name
- end
- end
- local foundname=resolvers.findbinfile(fullname,'tfm') or ""
- if foundname=="" then
- foundname=resolvers.findbinfile(fullname,'ofm') or ""
- end
- if foundname~="" then
- specification.filename=foundname
- specification.format="ofm"
- return font.read_tfm(specification.filename,specification.size)
- end
-end
-
-end -- closure
-
-do -- begin closure to overcome local limits and interference
-
if not modules then modules={} end modules ['font-oti']={
version=1.001,
comment="companion to font-ini.mkiv",
@@ -7199,1371 +8102,5600 @@ end -- closure
do -- begin closure to overcome local limits and interference
-if not modules then modules={} end modules ['font-otf']={
+if not modules then modules={} end modules ['font-otr']={
version=1.001,
comment="companion to font-ini.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
-local utfbyte=utf.byte
-local gmatch,gsub,find,match,lower,strip=string.gmatch,string.gsub,string.find,string.match,string.lower,string.strip
-local type,next,tonumber,tostring=type,next,tonumber,tostring
-local abs=math.abs
-local reversed,concat,insert,remove,sortedkeys=table.reversed,table.concat,table.insert,table.remove,table.sortedkeys
-local ioflush=io.flush
-local fastcopy,tohash,derivetable=table.fastcopy,table.tohash,table.derive
-local formatters=string.formatters
-local P,R,S,C,Ct,lpegmatch=lpeg.P,lpeg.R,lpeg.S,lpeg.C,lpeg.Ct,lpeg.match
+if not characters then
+ require("char-def")
+ require("char-ini")
+end
+local next,type,unpack=next,type,unpack
+local byte,lower,char,strip,gsub=string.byte,string.lower,string.char,string.strip,string.gsub
+local bittest=bit32.btest
+local concat,remove,unpack=table.concat,table.remov,table.unpack
+local floor,mod,abs,sqrt,round=math.floor,math.mod,math.abs,math.sqrt,math.round
+local P,R,S,C,Cs,Cc,Ct,Carg,Cmt=lpeg.P,lpeg.R,lpeg.S,lpeg.C,lpeg.Cs,lpeg.Cc,lpeg.Ct,lpeg.Carg,lpeg.Cmt
+local lpegmatch=lpeg.match
local setmetatableindex=table.setmetatableindex
-local allocate=utilities.storage.allocate
-local registertracker=trackers.register
-local registerdirective=directives.register
-local starttiming=statistics.starttiming
-local stoptiming=statistics.stoptiming
-local elapsedtime=statistics.elapsedtime
-local findbinfile=resolvers.findbinfile
-local trace_private=false registertracker("otf.private",function(v) trace_private=v end)
-local trace_subfonts=false registertracker("otf.subfonts",function(v) trace_subfonts=v end)
-local trace_loading=false registertracker("otf.loading",function(v) trace_loading=v end)
-local trace_features=false registertracker("otf.features",function(v) trace_features=v end)
-local trace_dynamics=false registertracker("otf.dynamics",function(v) trace_dynamics=v end)
-local trace_sequences=false registertracker("otf.sequences",function(v) trace_sequences=v end)
-local trace_markwidth=false registertracker("otf.markwidth",function(v) trace_markwidth=v end)
-local trace_defining=false registertracker("fonts.defining",function(v) trace_defining=v end)
-local compact_lookups=true registertracker("otf.compactlookups",function(v) compact_lookups=v end)
-local purge_names=true registertracker("otf.purgenames",function(v) purge_names=v end)
-local report_otf=logs.reporter("fonts","otf loading")
-local fonts=fonts
-local otf=fonts.handlers.otf
-otf.glists={ "gsub","gpos" }
-otf.version=2.820
-otf.cache=containers.define("fonts","otf",otf.version,true)
-local hashes=fonts.hashes
-local definers=fonts.definers
-local readers=fonts.readers
-local constructors=fonts.constructors
-local fontdata=hashes and hashes.identifiers
-local chardata=characters and characters.data
-local otffeatures=constructors.newfeatures("otf")
-local registerotffeature=otffeatures.register
-local enhancers=allocate()
-otf.enhancers=enhancers
-local patches={}
-enhancers.patches=patches
-local forceload=false
-local cleanup=0
-local packdata=true
-local syncspace=true
-local forcenotdef=false
-local includesubfonts=false
-local overloadkerns=false
-local applyruntimefixes=fonts.treatments and fonts.treatments.applyfixes
-local wildcard="*"
-local default="dflt"
-local fontloader=fontloader
-local open_font=fontloader.open
-local close_font=fontloader.close
-local font_fields=fontloader.fields
-local apply_featurefile=fontloader.apply_featurefile
-local mainfields=nil
-local glyphfields=nil
-local formats=fonts.formats
-formats.otf="opentype"
-formats.ttf="truetype"
-formats.ttc="truetype"
-formats.dfont="truetype"
-registerdirective("fonts.otf.loader.cleanup",function(v) cleanup=tonumber(v) or (v and 1) or 0 end)
-registerdirective("fonts.otf.loader.force",function(v) forceload=v end)
-registerdirective("fonts.otf.loader.pack",function(v) packdata=v end)
-registerdirective("fonts.otf.loader.syncspace",function(v) syncspace=v end)
-registerdirective("fonts.otf.loader.forcenotdef",function(v) forcenotdef=v end)
-registerdirective("fonts.otf.loader.overloadkerns",function(v) overloadkerns=v end)
-function otf.fileformat(filename)
- local leader=lower(io.loadchunk(filename,4))
- local suffix=lower(file.suffix(filename))
- if leader=="otto" then
- return formats.otf,suffix=="otf"
- elseif leader=="ttcf" then
- return formats.ttc,suffix=="ttc"
- elseif suffix=="ttc" then
- return formats.ttc,true
- elseif suffix=="dfont" then
- return formats.dfont,true
+local formatters=string.formatters
+local sortedkeys=table.sortedkeys
+local sortedhash=table.sortedhash
+local stripstring=string.strip
+local utf16_to_utf8_be=utf.utf16_to_utf8_be
+local report=logs.reporter("otf reader")
+local trace_cmap=false
+fonts=fonts or {}
+local handlers=fonts.handlers or {}
+fonts.handlers=handlers
+local otf=handlers.otf or {}
+handlers.otf=otf
+local readers=otf.readers or {}
+otf.readers=readers
+local streamreader=utilities.files
+readers.streamreader=streamreader
+local openfile=streamreader.open
+local closefile=streamreader.close
+local skipbytes=streamreader.skip
+local setposition=streamreader.setposition
+local skipshort=streamreader.skipshort
+local readbytes=streamreader.readbytes
+local readstring=streamreader.readstring
+local readbyte=streamreader.readcardinal1
+local readushort=streamreader.readcardinal2
+local readuint=streamreader.readcardinal3
+local readulong=streamreader.readcardinal4
+local readchar=streamreader.readinteger1
+local readshort=streamreader.readinteger2
+local readlong=streamreader.readinteger4
+local readfixed=streamreader.readfixed4
+local readfword=readshort
+local readufword=readushort
+local readoffset=readushort
+local read2dot14=streamreader.read2dot14
+function streamreader.readtag(f)
+ return lower(strip(readstring(f,4)))
+end
+local function readlongdatetime(f)
+ local a,b,c,d,e,f,g,h=readbytes(f,8)
+ return 0x100000000*d+0x1000000*e+0x10000*f+0x100*g+h
+end
+local tableversion=0.004
+local privateoffset=fonts.constructors and fonts.constructors.privateoffset or 0xF0000
+readers.tableversion=tableversion
+local reportedskipped={}
+local function reportskippedtable(tag)
+ if not reportedskipped[tag] then
+ report("loading of table %a skipped (reported once only)",tag)
+ reportedskipped[tag]=true
+ end
+end
+local reservednames={ [0]="copyright",
+ "family",
+ "subfamily",
+ "uniqueid",
+ "fullname",
+ "version",
+ "postscriptname",
+ "trademark",
+ "manufacturer",
+ "designer",
+ "description",
+ "venderurl",
+ "designerurl",
+ "license",
+ "licenseurl",
+ "reserved",
+ "typographicfamily",
+ "typographicsubfamily",
+ "compatiblefullname",
+ "sampletext",
+ "cidfindfontname",
+ "wwsfamily",
+ "wwssubfamily",
+ "lightbackgroundpalette",
+ "darkbackgroundpalette",
+}
+local platforms={ [0]="unicode",
+ "macintosh",
+ "iso",
+ "windows",
+ "custom",
+}
+local encodings={
+ unicode={ [0]="unicode 1.0 semantics",
+ "unicode 1.1 semantics",
+ "iso/iec 10646",
+ "unicode 2.0 bmp",
+ "unicode 2.0 full",
+ "unicode variation sequences",
+ "unicode full repertoire",
+ },
+ macintosh={ [0]="roman","japanese","chinese (traditional)","korean","arabic","hebrew","greek","russian",
+ "rsymbol","devanagari","gurmukhi","gujarati","oriya","bengali","tamil","telugu","kannada",
+ "malayalam","sinhalese","burmese","khmer","thai","laotian","georgian","armenian",
+ "chinese (simplified)","tibetan","mongolian","geez","slavic","vietnamese","sindhi",
+ "uninterpreted",
+ },
+ iso={ [0]="7-bit ascii",
+ "iso 10646",
+ "iso 8859-1",
+ },
+ windows={ [0]="symbol",
+ "unicode bmp",
+ "shiftjis",
+ "prc",
+ "big5",
+ "wansung",
+ "johab",
+ "reserved 7",
+ "reserved 8",
+ "reserved 9",
+ "unicode ucs-4",
+ },
+ custom={
+ }
+}
+local decoders={
+ unicode={},
+ macintosh={},
+ iso={},
+ windows={
+ ["unicode bmp"]=utf16_to_utf8_be
+ },
+ custom={},
+}
+local languages={
+ unicode={
+ [ 0]="english",
+ },
+ macintosh={
+ [ 0]="english",
+ },
+ iso={},
+ windows={
+ [0x0409]="english - united states",
+ },
+ custom={},
+}
+local standardromanencoding={ [0]=
+ "notdef",".null","nonmarkingreturn","space","exclam","quotedbl",
+ "numbersign","dollar","percent","ampersand","quotesingle","parenleft",
+ "parenright","asterisk","plus","comma","hyphen","period","slash",
+ "zero","one","two","three","four","five","six","seven","eight",
+ "nine","colon","semicolon","less","equal","greater","question","at",
+ "A","B","C","D","E","F","G","H","I","J","K","L","M","N","O",
+ "P","Q","R","S","T","U","V","W","X","Y","Z","bracketleft",
+ "backslash","bracketright","asciicircum","underscore","grave","a","b",
+ "c","d","e","f","g","h","i","j","k","l","m","n","o","p","q",
+ "r","s","t","u","v","w","x","y","z","braceleft","bar",
+ "braceright","asciitilde","Adieresis","Aring","Ccedilla","Eacute",
+ "Ntilde","Odieresis","Udieresis","aacute","agrave","acircumflex",
+ "adieresis","atilde","aring","ccedilla","eacute","egrave",
+ "ecircumflex","edieresis","iacute","igrave","icircumflex","idieresis",
+ "ntilde","oacute","ograve","ocircumflex","odieresis","otilde","uacute",
+ "ugrave","ucircumflex","udieresis","dagger","degree","cent","sterling",
+ "section","bullet","paragraph","germandbls","registered","copyright",
+ "trademark","acute","dieresis","notequal","AE","Oslash","infinity",
+ "plusminus","lessequal","greaterequal","yen","mu","partialdiff",
+ "summation","product","pi","integral","ordfeminine","ordmasculine",
+ "Omega","ae","oslash","questiondown","exclamdown","logicalnot",
+ "radical","florin","approxequal","Delta","guillemotleft",
+ "guillemotright","ellipsis","nonbreakingspace","Agrave","Atilde",
+ "Otilde","OE","oe","endash","emdash","quotedblleft","quotedblright",
+ "quoteleft","quoteright","divide","lozenge","ydieresis","Ydieresis",
+ "fraction","currency","guilsinglleft","guilsinglright","fi","fl",
+ "daggerdbl","periodcentered","quotesinglbase","quotedblbase",
+ "perthousand","Acircumflex","Ecircumflex","Aacute","Edieresis","Egrave",
+ "Iacute","Icircumflex","Idieresis","Igrave","Oacute","Ocircumflex",
+ "apple","Ograve","Uacute","Ucircumflex","Ugrave","dotlessi",
+ "circumflex","tilde","macron","breve","dotaccent","ring","cedilla",
+ "hungarumlaut","ogonek","caron","Lslash","lslash","Scaron","scaron",
+ "Zcaron","zcaron","brokenbar","Eth","eth","Yacute","yacute","Thorn",
+ "thorn","minus","multiply","onesuperior","twosuperior","threesuperior",
+ "onehalf","onequarter","threequarters","franc","Gbreve","gbreve",
+ "Idotaccent","Scedilla","scedilla","Cacute","cacute","Ccaron","ccaron",
+ "dcroat",
+}
+local weights={
+ [100]="thin",
+ [200]="extralight",
+ [300]="light",
+ [400]="normal",
+ [500]="medium",
+ [600]="semibold",
+ [700]="bold",
+ [800]="extrabold",
+ [900]="black",
+}
+local widths={
+ [1]="ultracondensed",
+ [2]="extracondensed",
+ [3]="condensed",
+ [4]="semicondensed",
+ [5]="normal",
+ [6]="semiexpanded",
+ [7]="expanded",
+ [8]="extraexpanded",
+ [9]="ultraexpanded",
+}
+setmetatableindex(weights,function(t,k)
+ local r=floor((k+50)/100)*100
+ local v=(r>900 and "black") or rawget(t,r) or "normal"
+ return v
+end)
+setmetatableindex(widths,function(t,k)
+ return "normal"
+end)
+local panoseweights={
+ [ 0]="normal",
+ [ 1]="normal",
+ [ 2]="verylight",
+ [ 3]="light",
+ [ 4]="thin",
+ [ 5]="book",
+ [ 6]="medium",
+ [ 7]="demi",
+ [ 8]="bold",
+ [ 9]="heavy",
+ [10]="black",
+}
+local panosewidths={
+ [ 0]="normal",
+ [ 1]="normal",
+ [ 2]="normal",
+ [ 3]="normal",
+ [ 4]="normal",
+ [ 5]="expanded",
+ [ 6]="condensed",
+ [ 7]="veryexpanded",
+ [ 8]="verycondensed",
+ [ 9]="monospaced",
+}
+function readers.name(f,fontdata)
+ local datatable=fontdata.tables.name
+ if datatable then
+ setposition(f,datatable.offset)
+ local format=readushort(f)
+ local nofnames=readushort(f)
+ local offset=readushort(f)
+ local namelists={
+ unicode={},
+ windows={},
+ macintosh={},
+ }
+ for i=1,nofnames do
+ local platform=platforms[readushort(f)]
+ if platform then
+ local namelist=namelists[platform]
+ if namelist then
+ local encoding=readushort(f)
+ local language=readushort(f)
+ local encodings=encodings[platform]
+ local languages=languages[platform]
+ if encodings and languages then
+ local encoding=encodings[encoding]
+ local language=languages[language]
+ if encoding and language then
+ local name=reservednames[readushort(f)]
+ if name then
+ namelist[#namelist+1]={
+ platform=platform,
+ encoding=encoding,
+ language=language,
+ name=name,
+ length=readushort(f),
+ offset=readushort(f),
+ }
+ else
+ skipshort(f,2)
+ end
+ else
+ skipshort(f,3)
+ end
+ else
+ skipshort(f,3)
+ end
+ else
+ skipshort(f,5)
+ end
+ else
+ skipshort(f,5)
+ end
+ end
+ local start=datatable.offset+offset
+ local names={}
+ local done={}
+ local function filter(platform,e,l)
+ local namelist=namelists[platform]
+ for i=1,#namelist do
+ local name=namelist[i]
+ local nametag=name.name
+ if not done[nametag] then
+ local encoding=name.encoding
+ local language=name.language
+ if (not e or encoding==e) and (not l or language==l) then
+ setposition(f,start+name.offset)
+ local content=readstring(f,name.length)
+ local decoder=decoders[platform]
+ if decoder then
+ decoder=decoder[encoding]
+ end
+ if decoder then
+ content=decoder(content)
+ end
+ names[nametag]={
+ content=content,
+ platform=platform,
+ encoding=encoding,
+ language=language,
+ }
+ done[nametag]=true
+ end
+ end
+ end
+ end
+ filter("windows","unicode bmp","english - united states")
+ filter("macintosh","roman","english")
+ filter("windows")
+ filter("macintosh")
+ filter("unicode")
+ fontdata.names=names
+ else
+ fontdata.names={}
+ end
+end
+readers["os/2"]=function(f,fontdata)
+ local datatable=fontdata.tables["os/2"]
+ if datatable then
+ setposition(f,datatable.offset)
+ local version=readushort(f)
+ local windowsmetrics={
+ version=version,
+ averagewidth=readshort(f),
+ weightclass=readushort(f),
+ widthclass=readushort(f),
+ fstype=readushort(f),
+ subscriptxsize=readshort(f),
+ subscriptysize=readshort(f),
+ subscriptxoffset=readshort(f),
+ subscriptyoffset=readshort(f),
+ superscriptxsize=readshort(f),
+ superscriptysize=readshort(f),
+ superscriptxoffset=readshort(f),
+ superscriptyoffset=readshort(f),
+ strikeoutsize=readshort(f),
+ strikeoutpos=readshort(f),
+ familyclass=readshort(f),
+ panose={ readbytes(f,10) },
+ unicoderanges={ readulong(f),readulong(f),readulong(f),readulong(f) },
+ vendor=readstring(f,4),
+ fsselection=readushort(f),
+ firstcharindex=readushort(f),
+ lastcharindex=readushort(f),
+ typoascender=readshort(f),
+ typodescender=readshort(f),
+ typolinegap=readshort(f),
+ winascent=readushort(f),
+ windescent=readushort(f),
+ }
+ if version>=1 then
+ windowsmetrics.codepageranges={ readulong(f),readulong(f) }
+ end
+ if version>=3 then
+ windowsmetrics.xheight=readshort(f)
+ windowsmetrics.capheight=readshort(f)
+ windowsmetrics.defaultchar=readushort(f)
+ windowsmetrics.breakchar=readushort(f)
+ end
+ windowsmetrics.weight=windowsmetrics.weightclass and weights[windowsmetrics.weightclass]
+ windowsmetrics.width=windowsmetrics.widthclass and widths [windowsmetrics.widthclass]
+ windowsmetrics.panoseweight=panoseweights[windowsmetrics.panose[3]]
+ windowsmetrics.panosewidth=panosewidths [windowsmetrics.panose[4]]
+ fontdata.windowsmetrics=windowsmetrics
else
- return formats.ttf,suffix=="ttf"
+ fontdata.windowsmetrics={}
+ end
+end
+readers.head=function(f,fontdata)
+ local datatable=fontdata.tables.head
+ if datatable then
+ setposition(f,datatable.offset)
+ local fontheader={
+ version=readfixed(f),
+ revision=readfixed(f),
+ checksum=readulong(f),
+ magic=readulong(f),
+ flags=readushort(f),
+ units=readushort(f),
+ created=readlongdatetime(f),
+ modified=readlongdatetime(f),
+ xmin=readshort(f),
+ ymin=readshort(f),
+ xmax=readshort(f),
+ ymax=readshort(f),
+ macstyle=readushort(f),
+ smallpixels=readushort(f),
+ directionhint=readshort(f),
+ indextolocformat=readshort(f),
+ glyphformat=readshort(f),
+ }
+ fontdata.fontheader=fontheader
+ else
+ fontdata.fontheader={}
+ end
+ fontdata.nofglyphs=0
+end
+readers.hhea=function(f,fontdata,specification)
+ if specification.details then
+ local datatable=fontdata.tables.hhea
+ if datatable then
+ setposition(f,datatable.offset)
+ fontdata.horizontalheader={
+ version=readfixed(f),
+ ascender=readfword(f),
+ descender=readfword(f),
+ linegap=readfword(f),
+ maxadvancewidth=readufword(f),
+ minleftsidebearing=readfword(f),
+ minrightsidebearing=readfword(f),
+ maxextent=readfword(f),
+ caretsloperise=readshort(f),
+ caretsloperun=readshort(f),
+ caretoffset=readshort(f),
+ reserved_1=readshort(f),
+ reserved_2=readshort(f),
+ reserved_3=readshort(f),
+ reserved_4=readshort(f),
+ metricdataformat=readshort(f),
+ nofhmetrics=readushort(f),
+ }
+ else
+ fontdata.horizontalheader={
+ nofhmetrics=0,
+ }
+ end
end
end
-local function otf_format(filename)
- local format,okay=otf.fileformat(filename)
- if not okay then
- report_otf("font %a is actually an %a file",filename,format)
+readers.maxp=function(f,fontdata,specification)
+ if specification.details then
+ local datatable=fontdata.tables.maxp
+ if datatable then
+ setposition(f,datatable.offset)
+ local version=readfixed(f)
+ local nofglyphs=readushort(f)
+ fontdata.nofglyphs=nofglyphs
+ if version==0.5 then
+ fontdata.maximumprofile={
+ version=version,
+ nofglyphs=nofglyphs,
+ }
+ return
+ elseif version==1.0 then
+ fontdata.maximumprofile={
+ version=version,
+ nofglyphs=nofglyphs,
+ points=readushort(f),
+ contours=readushort(f),
+ compositepoints=readushort(f),
+ compositecontours=readushort(f),
+ zones=readushort(f),
+ twilightpoints=readushort(f),
+ storage=readushort(f),
+ functiondefs=readushort(f),
+ instructiondefs=readushort(f),
+ stackelements=readushort(f),
+ sizeofinstructions=readushort(f),
+ componentelements=readushort(f),
+ componentdepth=readushort(f),
+ }
+ return
+ end
+ end
+ fontdata.maximumprofile={
+ version=version,
+ nofglyphs=0,
+ }
end
- return format
end
-local function load_featurefile(raw,featurefile)
- if featurefile and featurefile~="" then
- if trace_loading then
- report_otf("using featurefile %a",featurefile)
+readers.hmtx=function(f,fontdata,specification)
+ if specification.glyphs then
+ local datatable=fontdata.tables.hmtx
+ if datatable then
+ setposition(f,datatable.offset)
+ local nofmetrics=fontdata.horizontalheader.nofhmetrics
+ local glyphs=fontdata.glyphs
+ local nofglyphs=fontdata.nofglyphs
+ local nofrepeated=nofglyphs-nofmetrics
+ local width=0
+ local leftsidebearing=0
+ for i=0,nofmetrics-1 do
+ local glyph=glyphs[i]
+ width=readshort(f)
+ leftsidebearing=readshort(f)
+ if advance~=0 then
+ glyph.width=width
+ end
+ end
+ for i=nofmetrics,nofrepeated do
+ local glyph=glyphs[i]
+ if width~=0 then
+ glyph.width=width
+ end
+ end
+ end
+ end
+end
+readers.post=function(f,fontdata,specification)
+ local datatable=fontdata.tables.post
+ if datatable then
+ setposition(f,datatable.offset)
+ local version=readfixed(f)
+ fontdata.postscript={
+ version=version,
+ italicangle=round(1000*readfixed(f))/1000,
+ underlineposition=readfword(f),
+ underlinethickness=readfword(f),
+ monospaced=readulong(f),
+ minmemtype42=readulong(f),
+ maxmemtype42=readulong(f),
+ minmemtype1=readulong(f),
+ maxmemtype1=readulong(f),
+ }
+ if not specification.glyphs then
+ elseif version==1.0 then
+ for index=0,#standardromanencoding do
+ glyphs[index].name=standardromanencoding[index]
+ end
+ elseif version==2.0 then
+ local glyphs=fontdata.glyphs
+ local nofglyphs=readushort(f)
+ local indices={}
+ local names={}
+ local maxnames=0
+ for i=0,nofglyphs-1 do
+ local nameindex=readushort(f)
+ if nameindex>=258 then
+ maxnames=maxnames+1
+ nameindex=nameindex-257
+ indices[nameindex]=i
+ else
+ glyphs[i].name=standardromanencoding[nameindex]
+ end
+ end
+ for i=1,maxnames do
+ local mapping=indices[i]
+ if not mapping then
+ report("quit post name fetching at %a of %a: %s",i,maxnames,"no index")
+ break
+ else
+ local length=readbyte(f)
+ if length>0 then
+ glyphs[mapping].name=readstring(f,length)
+ else
+ report("quit post name fetching at %a of %a: %s",i,maxnames,"overflow")
+ break
+ end
+ end
+ end
+ elseif version==2.5 then
+ elseif version==3.0 then
end
- apply_featurefile(raw,featurefile)
+ else
+ fontdata.postscript={}
end
end
-local function showfeatureorder(rawdata,filename)
- local sequences=rawdata.resources.sequences
- if sequences and #sequences>0 then
- if trace_loading then
- report_otf("font %a has %s sequences",filename,#sequences)
- report_otf(" ")
- end
- for nos=1,#sequences do
- local sequence=sequences[nos]
- local typ=sequence.type or "no-type"
- local name=sequence.name or "no-name"
- local subtables=sequence.subtables or { "no-subtables" }
- local features=sequence.features
- if trace_loading then
- report_otf("%3i %-15s %-20s [% t]",nos,name,typ,subtables)
+readers.cff=function(f,fontdata,specification)
+ if specification.glyphs then
+ reportskippedtable("cff")
+ end
+end
+local formatreaders={}
+local duplicatestoo=true
+local sequence={
+ { 3,1,4 },
+ { 3,10,12 },
+ { 0,3,4 },
+ { 0,1,4 },
+ { 0,0,6 },
+ { 3,0,6 },
+ { 0,5,14 },
+}
+local supported={}
+for i=1,#sequence do
+ local sp,se,sf=unpack(sequence[i])
+ local p=supported[sp]
+ if not p then
+ p={}
+ supported[sp]=p
+ end
+ local e=p[se]
+ if not e then
+ e={}
+ p[se]=e
+ end
+ e[sf]=true
+end
+formatreaders[4]=function(f,fontdata,offset)
+ setposition(f,offset+2)
+ local length=readushort(f)
+ local language=readushort(f)
+ local nofsegments=readushort(f)/2
+ skipshort(f,3)
+ local endchars={}
+ local startchars={}
+ local deltas={}
+ local offsets={}
+ local indices={}
+ local mapping=fontdata.mapping
+ local glyphs=fontdata.glyphs
+ local duplicates=fontdata.duplicates
+ local nofdone=0
+ for i=1,nofsegments do
+ endchars[i]=readushort(f)
+ end
+ local reserved=readushort(f)
+ for i=1,nofsegments do
+ startchars[i]=readushort(f)
+ end
+ for i=1,nofsegments do
+ deltas[i]=readshort(f)
+ end
+ for i=1,nofsegments do
+ offsets[i]=readushort(f)
+ end
+ local size=(length-2*2-5*2-4*nofsegments*2)/2
+ for i=1,size-1 do
+ indices[i]=readushort(f)
+ end
+ for segment=1,nofsegments do
+ local startchar=startchars[segment]
+ local endchar=endchars[segment]
+ local offset=offsets[segment]
+ local delta=deltas[segment]
+ if startchar==0xFFFF and endchar==0xFFFF then
+ elseif startchar==0xFFFF and offset==0 then
+ elseif offset==0xFFFF then
+ elseif offset==0 then
+ if trace_cmap then
+ report("format 4.%i segment %2i from %C upto %C at index %H",1,segment,startchar,endchar,mod(startchar+delta,65536))
+ end
+ for unicode=startchar,endchar do
+ local index=mod(unicode+delta,65536)
+ if index and index>0 then
+ local glyph=glyphs[index]
+ if glyph then
+ local gu=glyph.unicode
+ if not gu then
+ glyph.unicode=unicode
+ nofdone=nofdone+1
+ elseif gu~=unicode then
+ if duplicatestoo then
+ local d=duplicates[gu]
+ if d then
+ d[unicode]=true
+ else
+ duplicates[gu]={ [unicode]=true }
+ end
+ else
+ report("duplicate case 1: %C %04i %s",unicode,index,glyphs[index].name)
+ end
+ end
+ if not mapping[index] then
+ mapping[index]=unicode
+ end
+ end
+ end
end
- if features then
- for feature,scripts in next,features do
- local tt={}
- if type(scripts)=="table" then
- for script,languages in next,scripts do
- local ttt={}
- for language,_ in next,languages do
- ttt[#ttt+1]=language
+ else
+ local shift=(segment-nofsegments+offset/2)-startchar
+ if trace_cmap then
+ report("format 4.%i segment %2i from %C upto %C at index %H",0,segment,startchar,endchar,mod(startchar+delta,65536))
+ end
+ for unicode=startchar,endchar do
+ local slot=shift+unicode
+ local index=indices[slot]
+ if index and index>0 then
+ index=mod(index+delta,65536)
+ local glyph=glyphs[index]
+ if glyph then
+ local gu=glyph.unicode
+ if not gu then
+ glyph.unicode=unicode
+ nofdone=nofdone+1
+ elseif gu~=unicode then
+ if duplicatestoo then
+ local d=duplicates[gu]
+ if d then
+ d[unicode]=true
+ else
+ duplicates[gu]={ [unicode]=true }
+ end
+ else
+ report("duplicate case 2: %C %04i %s",unicode,index,glyphs[index].name)
end
- tt[#tt+1]=formatters["[%s: % t]"](script,ttt)
end
- if trace_loading then
- report_otf(" %s: % t",feature,tt)
+ if not mapping[index] then
+ mapping[index]=unicode
end
+ end
+ end
+ end
+ end
+ end
+ return nofdone
+end
+formatreaders[6]=function(f,fontdata,offset)
+ setposition(f,offset)
+ local format=readushort(f)
+ local length=readushort(f)
+ local language=readushort(f)
+ local mapping=fontdata.mapping
+ local glyphs=fontdata.glyphs
+ local duplicates=fontdata.duplicates
+ local start=readushort(f)
+ local count=readushort(f)
+ local stop=start+count-1
+ local nofdone=0
+ if trace_cmap then
+ report("format 6 from %C to %C",2,start,stop)
+ end
+ for unicode=start,stop do
+ local index=readushort(f)
+ if index>0 then
+ local glyph=glyphs[index]
+ if glyph then
+ local gu=glyph.unicode
+ if not gu then
+ glyph.unicode=unicode
+ nofdone=nofdone+1
+ elseif gu~=unicode then
+ end
+ if not mapping[index] then
+ mapping[index]=unicode
+ end
+ end
+ end
+ end
+ return nofdone
+end
+formatreaders[12]=function(f,fontdata,offset)
+ setposition(f,offset+2+2+4+4)
+ local mapping=fontdata.mapping
+ local glyphs=fontdata.glyphs
+ local duplicates=fontdata.duplicates
+ local nofgroups=readulong(f)
+ local nofdone=0
+ for i=1,nofgroups do
+ local first=readulong(f)
+ local last=readulong(f)
+ local index=readulong(f)
+ if trace_cmap then
+ report("format 12 from %C to %C",first,last)
+ end
+ for unicode=first,last do
+ local glyph=glyphs[index]
+ if glyph then
+ local gu=glyph.unicode
+ if not gu then
+ glyph.unicode=unicode
+ nofdone=nofdone+1
+ elseif gu~=unicode then
+ local d=duplicates[gu]
+ if d then
+ d[unicode]=true
else
- if trace_loading then
- report_otf(" %s: %S",feature,scripts)
+ duplicates[gu]={ [unicode]=true }
+ end
+ end
+ if not mapping[index] then
+ mapping[index]=unicode
+ end
+ end
+ index=index+1
+ end
+ end
+ return nofdone
+end
+formatreaders[14]=function(f,fontdata,offset)
+ if offset and offset~=0 then
+ setposition(f,offset)
+ local format=readushort(f)
+ local length=readulong(f)
+ local nofrecords=readulong(f)
+ local records={}
+ local variants={}
+ local nofdone=0
+ fontdata.variants=variants
+ for i=1,nofrecords do
+ records[i]={
+ selector=readuint(f),
+ default=readulong(f),
+ other=readulong(f),
+ }
+ end
+ for i=1,nofrecords do
+ local record=records[i]
+ local selector=record.selector
+ local default=record.default
+ local other=record.other
+ local other=record.other
+ if other~=0 then
+ setposition(f,offset+other)
+ local mapping={}
+ local count=readulong(f)
+ for i=1,count do
+ mapping[readuint(f)]=readushort(f)
+ end
+ nofdone=nofdone+count
+ variants[selector]=mapping
+ end
+ end
+ return nofdone
+ else
+ return 0
+ end
+end
+local function checkcmap(f,fontdata,records,platform,encoding,format)
+ local data=records[platform]
+ if not data then
+ return 0
+ end
+ data=data[encoding]
+ if not data then
+ return 0
+ end
+ data=data[format]
+ if not data then
+ return 0
+ end
+ local reader=formatreaders[format]
+ if not reader then
+ return 0
+ end
+ local p=platforms[platform]
+ local e=encodings[p]
+ local n=reader(f,fontdata,data) or 0
+ report("cmap checked: platform %i (%s), encoding %i (%s), format %i, new unicodes %i",platform,p,encoding,e and e[encoding] or "?",format,n)
+ return n
+end
+function readers.cmap(f,fontdata,specification)
+ if specification.glyphs then
+ local datatable=fontdata.tables.cmap
+ if datatable then
+ local tableoffset=datatable.offset
+ setposition(f,tableoffset)
+ local version=readushort(f)
+ local noftables=readushort(f)
+ local records={}
+ local unicodecid=false
+ local variantcid=false
+ local variants={}
+ local duplicates=fontdata.duplicates or {}
+ fontdata.duplicates=duplicates
+ for i=1,noftables do
+ local platform=readushort(f)
+ local encoding=readushort(f)
+ local offset=readulong(f)
+ local record=records[platform]
+ if not record then
+ records[platform]={
+ [encoding]={
+ offsets={ offset },
+ formats={},
+ }
+ }
+ else
+ local subtables=record[encoding]
+ if not subtables then
+ record[encoding]={
+ offsets={ offset },
+ formats={},
+ }
+ else
+ local offsets=subtables.offsets
+ offsets[#offsets+1]=offset
+ end
+ end
+ end
+ report("found cmaps:")
+ for platform,record in sortedhash(records) do
+ local p=platforms[platform]
+ local e=encodings[p]
+ local sp=supported[platform]
+ local ps=p or "?"
+ if sp then
+ report(" platform %i: %s",platform,ps)
+ else
+ report(" platform %i: %s (unsupported)",platform,ps)
+ end
+ for encoding,subtables in sortedhash(record) do
+ local se=sp and sp[encoding]
+ local es=e and e[encoding] or "?"
+ if se then
+ report(" encoding %i: %s",encoding,es)
+ else
+ report(" encoding %i: %s (unsupported)",encoding,es)
+ end
+ local offsets=subtables.offsets
+ local formats=subtables.formats
+ for i=1,#offsets do
+ local offset=tableoffset+offsets[i]
+ setposition(f,offset)
+ formats[readushort(f)]=offset
+ end
+ record[encoding]=formats
+ local list=sortedkeys(formats)
+ for i=1,#list do
+ if not (se and se[list[i]]) then
+ list[i]=list[i].." (unsupported)"
end
end
+ report(" formats: % t",list)
end
end
+ local ok=false
+ for i=1,#sequence do
+ local sp,se,sf=unpack(sequence[i])
+ if checkcmap(f,fontdata,records,sp,se,sf)>0 then
+ ok=true
+ end
+ end
+ if not ok then
+ report("no useable unicode cmap found")
+ end
+ fontdata.cidmaps={
+ version=version,
+ noftables=noftables,
+ records=records,
+ }
+ else
+ fontdata.cidmaps={}
end
- if trace_loading then
- report_otf("\n")
+ end
+end
+function readers.loca(f,fontdata,specification)
+ if specification.glyphs then
+ reportskippedtable("loca")
+ end
+end
+function readers.glyf(f,fontdata,specification)
+ if specification.glyphs then
+ reportskippedtable("glyf")
+ end
+end
+function readers.kern(f,fontdata,specification)
+ if specification.kerns then
+ local datatable=fontdata.tables.kern
+ if datatable then
+ setposition(f,datatable.offset)
+ local version=readushort(f)
+ local noftables=readushort(f)
+ for i=1,noftables do
+ local version=readushort(f)
+ local length=readushort(f)
+ local coverage=readushort(f)
+ local format=bit32.rshift(coverage,8)
+ if format==0 then
+ local nofpairs=readushort(f)
+ local searchrange=readushort(f)
+ local entryselector=readushort(f)
+ local rangeshift=readushort(f)
+ local kerns={}
+ local glyphs=fontdata.glyphs
+ for i=1,nofpairs do
+ local left=readushort(f)
+ local right=readushort(f)
+ local kern=readfword(f)
+ local glyph=glyphs[left]
+ local kerns=glyph.kerns
+ if kerns then
+ kerns[right]=kern
+ else
+ glyph.kerns={ [right]=kern }
+ end
+ end
+ elseif format==2 then
+ report("todo: kern classes")
+ else
+ report("todo: kerns")
+ end
+ end
end
- elseif trace_loading then
- report_otf("font %a has no sequences",filename)
- end
-end
-local valid_fields=table.tohash {
- "ascent",
- "cidinfo",
- "copyright",
- "descent",
- "design_range_bottom",
- "design_range_top",
- "design_size",
- "encodingchanged",
- "extrema_bound",
- "familyname",
- "fontname",
- "fontstyle_id",
- "fontstyle_name",
- "fullname",
- "hasvmetrics",
- "horiz_base",
- "issans",
- "isserif",
- "italicangle",
- "macstyle",
- "onlybitmaps",
- "origname",
- "os2_version",
- "pfminfo",
- "serifcheck",
- "sfd_version",
- "strokedfont",
- "strokewidth",
- "table_version",
- "ttf_tables",
- "uni_interp",
- "uniqueid",
- "units_per_em",
- "upos",
- "use_typo_metrics",
- "uwidth",
- "validation_state",
- "version",
- "vert_base",
- "weight",
- "weight_width_slope_only",
-}
-local ordered_enhancers={
- "prepare tables",
- "prepare glyphs",
- "prepare lookups",
- "analyze glyphs",
- "analyze math",
- "reorganize lookups",
- "reorganize mark classes",
- "reorganize anchor classes",
- "reorganize glyph kerns",
- "reorganize glyph lookups",
- "reorganize glyph anchors",
- "merge kern classes",
- "reorganize features",
- "reorganize subtables",
- "check glyphs",
- "check metadata",
- "prepare tounicode",
- "check encoding",
- "add duplicates",
- "expand lookups",
- "check extra features",
- "cleanup tables",
- "compact lookups",
- "purge names",
-}
-local actions=allocate()
-local before=allocate()
-local after=allocate()
-patches.before=before
-patches.after=after
-local function enhance(name,data,filename,raw)
- local enhancer=actions[name]
- if enhancer then
- if trace_loading then
- report_otf("apply enhancement %a to file %a",name,filename)
- ioflush()
+ end
+end
+function readers.gdef(f,fontdata,specification)
+ if specification.details then
+ reportskippedtable("gdef")
+ end
+end
+function readers.gsub(f,fontdata,specification)
+ if specification.details then
+ reportskippedtable("gsub")
+ end
+end
+function readers.gpos(f,fontdata,specification)
+ if specification.details then
+ reportskippedtable("gpos")
+ end
+end
+function readers.math(f,fontdata,specification)
+ if specification.glyphs then
+ reportskippedtable("math")
+ end
+end
+local function packoutlines(data,makesequence)
+ local subfonts=data.subfonts
+ if subfonts then
+ for i=1,#subfonts do
+ packoutlines(subfonts[i],makesequence)
+ end
+ return
+ end
+ local common=data.segments
+ if common then
+ return
+ end
+ local glyphs=data.glyphs
+ if not glyphs then
+ return
+ end
+ if makesequence then
+ for index=1,#glyphs do
+ local glyph=glyphs[index]
+ local segments=glyph.segments
+ if segments then
+ local sequence={}
+ local nofsequence=0
+ for i=1,#segments do
+ local segment=segments[i]
+ local nofsegment=#segment
+ nofsequence=nofsequence+1
+ sequence[nofsequence]=segment[nofsegment]
+ for i=1,nofsegment-1 do
+ nofsequence=nofsequence+1
+ sequence[nofsequence]=segment[i]
+ end
+ end
+ glyph.sequence=sequence
+ glyph.segments=nil
+ end
end
- enhancer(data,filename,raw)
else
+ local hash={}
+ local common={}
+ local reverse={}
+ local last=0
+ for index=1,#glyphs do
+ local segments=glyphs[index].segments
+ if segments then
+ for i=1,#segments do
+ local h=concat(segments[i]," ")
+ hash[h]=(hash[h] or 0)+1
+ end
+ end
+ end
+ for index=1,#glyphs do
+ local segments=glyphs[index].segments
+ if segments then
+ for i=1,#segments do
+ local segment=segments[i]
+ local h=concat(segment," ")
+ if hash[h]>1 then
+ local idx=reverse[h]
+ if not idx then
+ last=last+1
+ reverse[h]=last
+ common[last]=segment
+ idx=last
+ end
+ segments[i]=idx
+ end
+ end
+ end
+ end
+ if last>0 then
+ data.segments=common
+ end
end
end
-function enhancers.apply(data,filename,raw)
- local basename=file.basename(lower(filename))
- if trace_loading then
- report_otf("%s enhancing file %a","start",filename)
+local function unpackoutlines(data)
+ local subfonts=data.subfonts
+ if subfonts then
+ for i=1,#subfonts do
+ unpackoutlines(subfonts[i])
+ end
+ return
end
- ioflush()
- for e=1,#ordered_enhancers do
- local enhancer=ordered_enhancers[e]
- local b=before[enhancer]
- if b then
- for pattern,action in next,b do
- if find(basename,pattern) then
- action(data,filename,raw)
+ local common=data.segments
+ if not common then
+ return
+ end
+ local glyphs=data.glyphs
+ if not glyphs then
+ return
+ end
+ for index=1,#glyphs do
+ local segments=glyphs[index].segments
+ if segments then
+ for i=1,#segments do
+ local c=common[segments[i]]
+ if c then
+ segments[i]=c
end
end
end
- enhance(enhancer,data,filename,raw)
- local a=after[enhancer]
- if a then
- for pattern,action in next,a do
- if find(basename,pattern) then
- action(data,filename,raw)
+ end
+ data.segments=nil
+end
+otf.packoutlines=packoutlines
+otf.unpackoutlines=unpackoutlines
+local validutf=lpeg.patterns.validutf8
+local function getname(fontdata,key)
+ local names=fontdata.names
+ if names then
+ local value=names[key]
+ if value then
+ local content=value.content
+ return lpegmatch(validutf,content) and content or nil
+ end
+ end
+end
+local function getinfo(maindata,sub)
+ local fontdata=sub and maindata.subfonts and maindata.subfonts[sub] or maindata
+ local names=fontdata.names
+ if names then
+ local metrics=fontdata.windowsmetrics or {}
+ local postscript=fontdata.postscript or {}
+ local fontheader=fontdata.fontheader or {}
+ local cffinfo=fontdata.cffinfo or {}
+ local filename=fontdata.filename
+ local weight=getname(fontdata,"weight") or cffinfo.weight or metrics.weight
+ local width=getname(fontdata,"width") or cffinfo.width or metrics.width
+ return {
+ subfontindex=fontdata.subfontindex or sub or 0,
+ fontname=getname(fontdata,"postscriptname"),
+ fullname=getname(fontdata,"fullname"),
+ familyname=getname(fontdata,"typographicfamily") or getname(fontdata,"family"),
+ subfamily=getname(fontdata,"subfamily"),
+ modifiers=getname(fontdata,"typographicsubfamily"),
+ weight=weight and lower(weight),
+ width=width and lower(width),
+ pfmweight=metrics.weightclass or 400,
+ pfmwidth=metrics.widthclass or 5,
+ panosewidth=metrics.panosewidth,
+ panoseweight=metrics.panoseweight,
+ italicangle=postscript.italicangle or 0,
+ units=fontheader.units or 0,
+ designsize=fontdata.designsize,
+ minsize=fontdata.minsize,
+ maxsize=fontdata.maxsize,
+ monospaced=(tonumber(postscript.monospaced or 0)>0) or metrics.panosewidth=="monospaced",
+ averagewidth=metrics.averagewidth,
+ xheight=metrics.xheight,
+ ascender=metrics.typoascender,
+ descender=metrics.typodescender,
+ }
+ elseif n then
+ return {
+ filename=fontdata.filename,
+ comment="there is no info for subfont "..n,
+ }
+ else
+ return {
+ filename=fontdata.filename,
+ comment="there is no info",
+ }
+ end
+end
+local function loadtables(f,specification,offset)
+ if offset then
+ setposition(f,offset)
+ end
+ local tables={}
+ local basename=file.basename(specification.filename)
+ local filesize=specification.filesize
+ local filetime=specification.filetime
+ local fontdata={
+ filename=basename,
+ filesize=filesize,
+ filetime=filetime,
+ version=readstring(f,4),
+ noftables=readushort(f),
+ searchrange=readushort(f),
+ entryselector=readushort(f),
+ rangeshift=readushort(f),
+ tables=tables,
+ }
+ for i=1,fontdata.noftables do
+ local tag=lower(stripstring(readstring(f,4)))
+ local checksum=readulong(f)
+ local offset=readulong(f)
+ local length=readulong(f)
+ if offset+length>filesize then
+ report("bad %a table in file %a",tag,basename)
+ end
+ tables[tag]={
+ checksum=checksum,
+ offset=offset,
+ length=length,
+ }
+ end
+ if tables.cff then
+ fontdata.format="opentype"
+ else
+ fontdata.format="truetype"
+ end
+ return fontdata
+end
+local function prepareglyps(fontdata)
+ local glyphs=setmetatableindex(function(t,k)
+ local v={
+ index=k,
+ }
+ t[k]=v
+ return v
+ end)
+ fontdata.glyphs=glyphs
+ fontdata.mapping={}
+end
+local function readdata(f,offset,specification)
+ local fontdata=loadtables(f,specification,offset)
+ if specification.glyphs then
+ prepareglyps(fontdata)
+ end
+ readers["name"](f,fontdata,specification)
+ local askedname=specification.askedname
+ if askedname then
+ local fullname=getname(fontdata,"fullname") or ""
+ local cleanname=gsub(askedname,"[^a-zA-Z0-9]","")
+ local foundname=gsub(fullname,"[^a-zA-Z0-9]","")
+ if lower(cleanname)~=lower(foundname) then
+ return
+ end
+ end
+ readers["os/2"](f,fontdata,specification)
+ readers["head"](f,fontdata,specification)
+ readers["maxp"](f,fontdata,specification)
+ readers["hhea"](f,fontdata,specification)
+ readers["hmtx"](f,fontdata,specification)
+ readers["post"](f,fontdata,specification)
+ readers["cff" ](f,fontdata,specification)
+ readers["cmap"](f,fontdata,specification)
+ readers["loca"](f,fontdata,specification)
+ readers["glyf"](f,fontdata,specification)
+ readers["kern"](f,fontdata,specification)
+ readers["gdef"](f,fontdata,specification)
+ readers["gsub"](f,fontdata,specification)
+ readers["gpos"](f,fontdata,specification)
+ readers["math"](f,fontdata,specification)
+ fontdata.locations=nil
+ fontdata.tables=nil
+ fontdata.cidmaps=nil
+ fontdata.dictionaries=nil
+ return fontdata
+end
+local function loadfontdata(specification)
+ local filename=specification.filename
+ local fileattr=lfs.attributes(filename)
+ local filesize=fileattr and fileattr.size or 0
+ local filetime=fileattr and fileattr.modification or 0
+ local f=openfile(filename,true)
+ if not f then
+ report("unable to open %a",filename)
+ elseif filesize==0 then
+ report("empty file %a",filename)
+ closefile(f)
+ else
+ specification.filesize=filesize
+ specification.filetime=filetime
+ local version=readstring(f,4)
+ local fontdata=nil
+ if version=="OTTO" or version=="true" or version=="\0\1\0\0" then
+ fontdata=readdata(f,0,specification)
+ elseif version=="ttcf" then
+ local subfont=tonumber(specification.subfont)
+ local offsets={}
+ local ttcversion=readulong(f)
+ local nofsubfonts=readulong(f)
+ for i=1,nofsubfonts do
+ offsets[i]=readulong(f)
+ end
+ if subfont then
+ if subfont>=1 and subfont<=nofsubfonts then
+ fontdata=readdata(f,offsets[subfont],specification)
+ else
+ report("no subfont %a in file %a",subfont,filename)
+ end
+ else
+ subfont=specification.subfont
+ if type(subfont)=="string" and subfont~="" then
+ specification.askedname=subfont
+ for i=1,nofsubfonts do
+ fontdata=readdata(f,offsets[i],specification)
+ if fontdata then
+ fontdata.subfontindex=i
+ report("subfont named %a has index %a",subfont,i)
+ break
+ end
+ end
+ if not fontdata then
+ report("no subfont named %a",subfont)
+ end
+ else
+ local subfonts={}
+ fontdata={
+ filename=filename,
+ filesize=filesize,
+ filetime=filetime,
+ version=version,
+ subfonts=subfonts,
+ ttcversion=ttcversion,
+ nofsubfonts=nofsubfonts,
+ }
+ for i=1,fontdata.nofsubfonts do
+ subfonts[i]=readdata(f,offsets[i],specification)
+ end
end
end
+ else
+ report("unknown version %a in file %a",version,filename)
end
- ioflush()
+ closefile(f)
+ return fontdata or {}
end
- if trace_loading then
- report_otf("%s enhancing file %a","stop",filename)
+end
+local function loadfont(specification,n)
+ if type(specification)=="string" then
+ specification={
+ filename=specification,
+ info=true,
+ details=true,
+ glyphs=true,
+ shapes=true,
+ kerns=true,
+ globalkerns=true,
+ lookups=true,
+ subfont=n or true,
+ tounicode=false,
+ }
+ end
+ if specification.shapes or specification.lookups or specification.kerns then
+ specification.glyphs=true
+ end
+ if specification.glyphs then
+ specification.details=true
+ end
+ if specification.details then
+ specification.info=true
+ end
+ local function message(str)
+ report("fatal error in file %a: %s\n%s",specification.filename,str,debug.traceback())
+ end
+ local ok,result=xpcall(loadfontdata,message,specification)
+ if ok then
+ return result
end
- ioflush()
end
-function patches.register(what,where,pattern,action)
- local pw=patches[what]
- if pw then
- local ww=pw[where]
- if ww then
- ww[pattern]=action
+function readers.loadshapes(filename,n)
+ local fontdata=loadfont {
+ filename=filename,
+ shapes=true,
+ subfont=n,
+ }
+ return fontdata and {
+ filename=filename,
+ format=fontdata.format,
+ glyphs=fontdata.glyphs,
+ units=fontdata.fontheader.units,
+ } or {
+ filename=filename,
+ format="unknown",
+ glyphs={},
+ units=0,
+ }
+end
+function readers.loadfont(filename,n)
+ local fontdata=loadfont {
+ filename=filename,
+ glyphs=true,
+ shapes=false,
+ lookups=true,
+ subfont=n,
+ }
+ if fontdata then
+ return {
+ tableversion=tableversion,
+ creator="context mkiv",
+ size=fontdata.filesize,
+ time=fontdata.filetime,
+ glyphs=fontdata.glyphs,
+ descriptions=fontdata.descriptions,
+ format=fontdata.format,
+ goodies={},
+ metadata=getinfo(fontdata,n),
+ properties={
+ hasitalics=fontdata.hasitalics or false,
+ },
+ resources={
+ filename=filename,
+ private=privateoffset,
+ duplicates=fontdata.duplicates or {},
+ features=fontdata.features or {},
+ sublookups=fontdata.sublookups or {},
+ marks=fontdata.marks or {},
+ markclasses=fontdata.markclasses or {},
+ marksets=fontdata.marksets or {},
+ sequences=fontdata.sequences or {},
+ variants=fontdata.variants,
+ version=getname(fontdata,"version"),
+ cidinfo=fontdata.cidinfo,
+ mathconstants=fontdata.mathconstants,
+ },
+ }
+ end
+end
+function readers.getinfo(filename,n,details)
+ local fontdata=loadfont {
+ filename=filename,
+ details=true,
+ }
+ if fontdata then
+ local subfonts=fontdata.subfonts
+ if not subfonts then
+ return getinfo(fontdata)
+ elseif type(n)~="number" then
+ local info={}
+ for i=1,#subfonts do
+ info[i]=getinfo(fontdata,i)
+ end
+ return info
+ elseif n>1 and n<=subfonts then
+ return getinfo(fontdata,n)
else
- pw[where]={ [pattern]=action}
+ return {
+ filename=filename,
+ comment="there is no subfont "..n.." in this file"
+ }
end
+ else
+ return {
+ filename=filename,
+ comment="the file cannot be opened for reading",
+ }
end
end
-function patches.report(fmt,...)
- if trace_loading then
- report_otf("patching: %s",formatters[fmt](...))
+function readers.rehash(fontdata,hashmethod)
+ report("the %a helper is not yet implemented","rehash")
+end
+function readers.checkhash(fontdata)
+ report("the %a helper is not yet implemented","checkhash")
+end
+function readers.pack(fontdata,hashmethod)
+ report("the %a helper is not yet implemented","pack")
+end
+function readers.unpack(fontdata)
+ report("the %a helper is not yet implemented","unpack")
+end
+function readers.expand(fontdata)
+ report("the %a helper is not yet implemented","unpack")
+end
+function readers.compact(fontdata)
+ report("the %a helper is not yet implemented","compact")
+end
+local extenders={}
+function readers.registerextender(extender)
+ extenders[#extenders+1]=extender
+end
+function readers.extend(fontdata)
+ for i=1,#extenders do
+ local extender=extenders[i]
+ local name=extender.name or "unknown"
+ local action=extender.action
+ if action then
+ action(fontdata)
+ end
end
end
-function enhancers.register(what,action)
- actions[what]=action
+if fonts.hashes then
+ local identifiers=fonts.hashes.identifiers
+ local loadshapes=readers.loadshapes
+ readers.version=0.006
+ readers.cache=containers.define("fonts","shapes",readers.version,true)
+ local function load(filename,sub)
+ local base=file.basename(filename)
+ local name=file.removesuffix(base)
+ local kind=file.suffix(filename)
+ local attr=lfs.attributes(filename)
+ local size=attr and attr.size or 0
+ local time=attr and attr.modification or 0
+ local sub=tonumber(sub)
+ if size>0 and (kind=="otf" or kind=="ttf" or kind=="tcc") then
+ local hash=containers.cleanname(base)
+ if sub then
+ hash=hash.."-"..sub
+ end
+ data=containers.read(readers.cache,hash)
+ if not data or data.time~=time or data.size~=size then
+ data=loadshapes(filename,sub)
+ if data then
+ data.size=size
+ data.format=data.format or (kind=="otf" and "opentype") or "truetype"
+ data.time=time
+ packoutlines(data)
+ containers.write(readers.cache,hash,data)
+ data=containers.read(readers.cache,hash)
+ end
+ end
+ unpackoutlines(data)
+ else
+ data={
+ filename=filename,
+ size=0,
+ time=time,
+ format="unknown",
+ units=1000,
+ glyphs={}
+ }
+ end
+ return data
+ end
+ fonts.hashes.shapes=table.setmetatableindex(function(t,k)
+ local d=identifiers[k]
+ local v=load(d.properties.filename,d.subindex)
+ t[k]=v
+ return v
+ end)
end
-function otf.load(filename,sub,featurefile)
- local base=file.basename(file.removesuffix(filename))
- local name=file.removesuffix(base)
- local attr=lfs.attributes(filename)
- local size=attr and attr.size or 0
- local time=attr and attr.modification or 0
- if featurefile then
- name=name.."@"..file.removesuffix(file.basename(featurefile))
+
+end -- closure
+
+do -- begin closure to overcome local limits and interference
+
+if not modules then modules={} end modules ['font-cff']={
+ version=1.001,
+ comment="companion to font-ini.mkiv",
+ author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright="PRAGMA ADE / ConTeXt Development Team",
+ license="see context related readme files"
+}
+local next,type,tonumber=next,type,tonumber
+local byte=string.byte
+local concat,remove=table.concat,table.remove
+local floor,abs,round,ceil=math.floor,math.abs,math.round,math.ceil
+local P,C,R,S,C,Cs,Ct=lpeg.P,lpeg.C,lpeg.R,lpeg.S,lpeg.C,lpeg.Cs,lpeg.Ct
+local lpegmatch=lpeg.match
+local readers=fonts.handlers.otf.readers
+local streamreader=readers.streamreader
+local readbytes=streamreader.readbytes
+local readstring=streamreader.readstring
+local readbyte=streamreader.readcardinal1
+local readushort=streamreader.readcardinal2
+local readuint=streamreader.readcardinal3
+local readulong=streamreader.readcardinal4
+local setposition=streamreader.setposition
+local getposition=streamreader.getposition
+local setmetatableindex=table.setmetatableindex
+local trace_charstrings=false trackers.register("fonts.cff.charstrings",function(v) trace_charstrings=v end)
+local report=logs.reporter("otf reader","cff")
+local parsedictionaries
+local parsecharstring
+local parsecharstrings
+local resetcharstrings
+local parseprivates
+local defaultstrings={ [0]=
+ ".notdef","space","exclam","quotedbl","numbersign","dollar","percent",
+ "ampersand","quoteright","parenleft","parenright","asterisk","plus",
+ "comma","hyphen","period","slash","zero","one","two","three","four",
+ "five","six","seven","eight","nine","colon","semicolon","less",
+ "equal","greater","question","at","A","B","C","D","E","F","G","H",
+ "I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W",
+ "X","Y","Z","bracketleft","backslash","bracketright","asciicircum",
+ "underscore","quoteleft","a","b","c","d","e","f","g","h","i","j",
+ "k","l","m","n","o","p","q","r","s","t","u","v","w","x","y",
+ "z","braceleft","bar","braceright","asciitilde","exclamdown","cent",
+ "sterling","fraction","yen","florin","section","currency",
+ "quotesingle","quotedblleft","guillemotleft","guilsinglleft",
+ "guilsinglright","fi","fl","endash","dagger","daggerdbl",
+ "periodcentered","paragraph","bullet","quotesinglbase","quotedblbase",
+ "quotedblright","guillemotright","ellipsis","perthousand","questiondown",
+ "grave","acute","circumflex","tilde","macron","breve","dotaccent",
+ "dieresis","ring","cedilla","hungarumlaut","ogonek","caron","emdash",
+ "AE","ordfeminine","Lslash","Oslash","OE","ordmasculine","ae",
+ "dotlessi","lslash","oslash","oe","germandbls","onesuperior",
+ "logicalnot","mu","trademark","Eth","onehalf","plusminus","Thorn",
+ "onequarter","divide","brokenbar","degree","thorn","threequarters",
+ "twosuperior","registered","minus","eth","multiply","threesuperior",
+ "copyright","Aacute","Acircumflex","Adieresis","Agrave","Aring",
+ "Atilde","Ccedilla","Eacute","Ecircumflex","Edieresis","Egrave",
+ "Iacute","Icircumflex","Idieresis","Igrave","Ntilde","Oacute",
+ "Ocircumflex","Odieresis","Ograve","Otilde","Scaron","Uacute",
+ "Ucircumflex","Udieresis","Ugrave","Yacute","Ydieresis","Zcaron",
+ "aacute","acircumflex","adieresis","agrave","aring","atilde",
+ "ccedilla","eacute","ecircumflex","edieresis","egrave","iacute",
+ "icircumflex","idieresis","igrave","ntilde","oacute","ocircumflex",
+ "odieresis","ograve","otilde","scaron","uacute","ucircumflex",
+ "udieresis","ugrave","yacute","ydieresis","zcaron","exclamsmall",
+ "Hungarumlautsmall","dollaroldstyle","dollarsuperior","ampersandsmall",
+ "Acutesmall","parenleftsuperior","parenrightsuperior","twodotenleader",
+ "onedotenleader","zerooldstyle","oneoldstyle","twooldstyle",
+ "threeoldstyle","fouroldstyle","fiveoldstyle","sixoldstyle",
+ "sevenoldstyle","eightoldstyle","nineoldstyle","commasuperior",
+ "threequartersemdash","periodsuperior","questionsmall","asuperior",
+ "bsuperior","centsuperior","dsuperior","esuperior","isuperior",
+ "lsuperior","msuperior","nsuperior","osuperior","rsuperior","ssuperior",
+ "tsuperior","ff","ffi","ffl","parenleftinferior","parenrightinferior",
+ "Circumflexsmall","hyphensuperior","Gravesmall","Asmall","Bsmall",
+ "Csmall","Dsmall","Esmall","Fsmall","Gsmall","Hsmall","Ismall",
+ "Jsmall","Ksmall","Lsmall","Msmall","Nsmall","Osmall","Psmall",
+ "Qsmall","Rsmall","Ssmall","Tsmall","Usmall","Vsmall","Wsmall",
+ "Xsmall","Ysmall","Zsmall","colonmonetary","onefitted","rupiah",
+ "Tildesmall","exclamdownsmall","centoldstyle","Lslashsmall",
+ "Scaronsmall","Zcaronsmall","Dieresissmall","Brevesmall","Caronsmall",
+ "Dotaccentsmall","Macronsmall","figuredash","hypheninferior",
+ "Ogoneksmall","Ringsmall","Cedillasmall","questiondownsmall","oneeighth",
+ "threeeighths","fiveeighths","seveneighths","onethird","twothirds",
+ "zerosuperior","foursuperior","fivesuperior","sixsuperior",
+ "sevensuperior","eightsuperior","ninesuperior","zeroinferior",
+ "oneinferior","twoinferior","threeinferior","fourinferior",
+ "fiveinferior","sixinferior","seveninferior","eightinferior",
+ "nineinferior","centinferior","dollarinferior","periodinferior",
+ "commainferior","Agravesmall","Aacutesmall","Acircumflexsmall",
+ "Atildesmall","Adieresissmall","Aringsmall","AEsmall","Ccedillasmall",
+ "Egravesmall","Eacutesmall","Ecircumflexsmall","Edieresissmall",
+ "Igravesmall","Iacutesmall","Icircumflexsmall","Idieresissmall",
+ "Ethsmall","Ntildesmall","Ogravesmall","Oacutesmall","Ocircumflexsmall",
+ "Otildesmall","Odieresissmall","OEsmall","Oslashsmall","Ugravesmall",
+ "Uacutesmall","Ucircumflexsmall","Udieresissmall","Yacutesmall",
+ "Thornsmall","Ydieresissmall","001.000","001.001","001.002","001.003",
+ "Black","Bold","Book","Light","Medium","Regular","Roman","Semibold",
+}
+local cffreaders={
+ readbyte,
+ readushort,
+ readuint,
+ readulong,
+}
+local function readheader(f)
+ local offset=getposition(f)
+ local header={
+ offset=offset,
+ major=readbyte(f),
+ minor=readbyte(f),
+ size=readbyte(f),
+ osize=readbyte(f),
+ }
+ setposition(f,offset+header.size)
+ return header
+end
+local function readlengths(f)
+ local count=readushort(f)
+ if count==0 then
+ return {}
end
- if sub=="" then
- sub=false
+ local osize=readbyte(f)
+ local read=cffreaders[osize]
+ if not read then
+ report("bad offset size: %i",osize)
+ return {}
end
- local hash=name
- if sub then
- hash=hash.."-"..sub
+ local lengths={}
+ local previous=read(f)
+ for i=1,count do
+ local offset=read(f)
+ lengths[i]=offset-previous
+ previous=offset
end
- hash=containers.cleanname(hash)
- local featurefiles
- if featurefile then
- featurefiles={}
- for s in gmatch(featurefile,"[^,]+") do
- local name=resolvers.findfile(file.addsuffix(s,'fea'),'fea') or ""
- if name=="" then
- report_otf("loading error, no featurefile %a",s)
- else
- local attr=lfs.attributes(name)
- featurefiles[#featurefiles+1]={
- name=name,
- size=attr and attr.size or 0,
- time=attr and attr.modification or 0,
+ return lengths
+end
+local function readfontnames(f)
+ local names=readlengths(f)
+ for i=1,#names do
+ names[i]=readstring(f,names[i])
+ end
+ return names
+end
+local function readtopdictionaries(f)
+ local dictionaries=readlengths(f)
+ for i=1,#dictionaries do
+ dictionaries[i]=readstring(f,dictionaries[i])
+ end
+ return dictionaries
+end
+local function readstrings(f)
+ local lengths=readlengths(f)
+ local strings=setmetatableindex({},defaultstrings)
+ local index=#defaultstrings
+ for i=1,#lengths do
+ index=index+1
+ strings[index]=readstring(f,lengths[i])
+ end
+ return strings
+end
+do
+ local stack={}
+ local top=0
+ local result={}
+ local strings={}
+ local p_single=P("\00")/function()
+ result.version=strings[stack[top]] or "unset"
+ top=0
+ end+P("\01")/function()
+ result.notice=strings[stack[top]] or "unset"
+ top=0
+ end+P("\02")/function()
+ result.fullname=strings[stack[top]] or "unset"
+ top=0
+ end+P("\03")/function()
+ result.familyname=strings[stack[top]] or "unset"
+ top=0
+ end+P("\04")/function()
+ result.weight=strings[stack[top]] or "unset"
+ top=0
+ end+P("\05")/function()
+ result.fontbbox={ unpack(stack,1,4) }
+ top=0
+ end
++P("\13")/function()
+ result.uniqueid=stack[top]
+ top=0
+ end+P("\14")/function()
+ result.xuid=concat(stack,"",1,top)
+ top=0
+ end+P("\15")/function()
+ result.charset=stack[top]
+ top=0
+ end+P("\16")/function()
+ result.encoding=stack[top]
+ top=0
+ end+P("\17")/function()
+ result.charstrings=stack[top]
+ top=0
+ end+P("\18")/function()
+ result.private={
+ size=stack[top-1],
+ offset=stack[top],
+ }
+ top=0
+ end+P("\19")/function()
+ result.subroutines=stack[top]
+ end+P("\20")/function()
+ result.defaultwidthx=stack[top]
+ end+P("\21")/function()
+ result.nominalwidthx=stack[top]
+ end
+ local p_double=P("\12")*(
+ P("\00")/function()
+ result.copyright=stack[top]
+ top=0
+ end+P("\01")/function()
+ result.monospaced=stack[top]==1 and true or false
+ top=0
+ end+P("\02")/function()
+ result.italicangle=stack[top]
+ top=0
+ end+P("\03")/function()
+ result.underlineposition=stack[top]
+ top=0
+ end+P("\04")/function()
+ result.underlinethickness=stack[top]
+ top=0
+ end+P("\05")/function()
+ result.painttype=stack[top]
+ top=0
+ end+P("\06")/function()
+ result.charstringtype=stack[top]
+ top=0
+ end+P("\07")/function()
+ result.fontmatrix={ unpack(stack,1,6) }
+ top=0
+ end+P("\08")/function()
+ result.strokewidth=stack[top]
+ top=0
+ end+P("\20")/function()
+ result.syntheticbase=stack[top]
+ top=0
+ end+P("\21")/function()
+ result.postscript=strings[stack[top]] or "unset"
+ top=0
+ end+P("\22")/function()
+ result.basefontname=strings[stack[top]] or "unset"
+ top=0
+ end+P("\21")/function()
+ result.basefontblend=stack[top]
+ top=0
+ end+P("\30")/function()
+ result.cid.registry=strings[stack[top-2]] or "unset"
+ result.cid.ordering=strings[stack[top-1]] or "unset"
+ result.cid.supplement=stack[top]
+ top=0
+ end+P("\31")/function()
+ result.cid.fontversion=stack[top]
+ top=0
+ end+P("\32")/function()
+ result.cid.fontrevision=stack[top]
+ top=0
+ end+P("\33")/function()
+ result.cid.fonttype=stack[top]
+ top=0
+ end+P("\34")/function()
+ result.cid.count=stack[top]
+ top=0
+ end+P("\35")/function()
+ result.cid.uidbase=stack[top]
+ top=0
+ end+P("\36")/function()
+ result.cid.fdarray=stack[top]
+ top=0
+ end+P("\37")/function()
+ result.cid.fdselect=stack[top]
+ top=0
+ end+P("\38")/function()
+ result.cid.fontname=strings[stack[top]] or "unset"
+ top=0
+ end
+ )
+ local p_last=P("\x0F")/"0"+P("\x1F")/"1"+P("\x2F")/"2"+P("\x3F")/"3"+P("\x4F")/"4"+P("\x5F")/"5"+P("\x6F")/"6"+P("\x7F")/"7"+P("\x8F")/"8"+P("\x9F")/"9"+P("\xAF")/""+P("\xBF")/""+P("\xCF")/""+P("\xDF")/""+P("\xEF")/""+R("\xF0\xFF")/""
+ local remap={
+ ["\x00"]="00",["\x01"]="01",["\x02"]="02",["\x03"]="03",["\x04"]="04",["\x05"]="05",["\x06"]="06",["\x07"]="07",["\x08"]="08",["\x09"]="09",["\x0A"]="0.",["\x0B"]="0E",["\x0C"]="0E-",["\x0D"]="0",["\x0E"]="0-",["\x0F"]="0",
+ ["\x10"]="10",["\x11"]="11",["\x12"]="12",["\x13"]="13",["\x14"]="14",["\x15"]="15",["\x16"]="16",["\x17"]="17",["\x18"]="18",["\x19"]="19",["\x1A"]="0.",["\x1B"]="0E",["\x1C"]="0E-",["\x1D"]="0",["\x1E"]="0-",["\x1F"]="0",
+ ["\x20"]="20",["\x21"]="21",["\x22"]="22",["\x23"]="23",["\x24"]="24",["\x25"]="25",["\x26"]="26",["\x27"]="27",["\x28"]="28",["\x29"]="29",["\x2A"]="0.",["\x2B"]="0E",["\x2C"]="0E-",["\x2D"]="0",["\x2E"]="0-",["\x2F"]="0",
+ ["\x30"]="30",["\x31"]="31",["\x32"]="32",["\x33"]="33",["\x34"]="34",["\x35"]="35",["\x36"]="36",["\x37"]="37",["\x38"]="38",["\x39"]="39",["\x3A"]="0.",["\x3B"]="0E",["\x3C"]="0E-",["\x3D"]="0",["\x3E"]="0-",["\x3F"]="0",
+ ["\x40"]="40",["\x41"]="41",["\x42"]="42",["\x43"]="43",["\x44"]="44",["\x45"]="45",["\x46"]="46",["\x47"]="47",["\x48"]="48",["\x49"]="49",["\x4A"]="0.",["\x4B"]="0E",["\x4C"]="0E-",["\x4D"]="0",["\x4E"]="0-",["\x4F"]="0",
+ ["\x50"]="50",["\x51"]="51",["\x52"]="52",["\x53"]="53",["\x54"]="54",["\x55"]="55",["\x56"]="56",["\x57"]="57",["\x58"]="58",["\x59"]="59",["\x5A"]="0.",["\x5B"]="0E",["\x5C"]="0E-",["\x5D"]="0",["\x5E"]="0-",["\x5F"]="0",
+ ["\x60"]="60",["\x61"]="61",["\x62"]="62",["\x63"]="63",["\x64"]="64",["\x65"]="65",["\x66"]="66",["\x67"]="67",["\x68"]="68",["\x69"]="69",["\x6A"]="0.",["\x6B"]="0E",["\x6C"]="0E-",["\x6D"]="0",["\x6E"]="0-",["\x6F"]="0",
+ ["\x70"]="70",["\x71"]="71",["\x72"]="72",["\x73"]="73",["\x74"]="74",["\x75"]="75",["\x76"]="76",["\x77"]="77",["\x78"]="78",["\x79"]="79",["\x7A"]="0.",["\x7B"]="0E",["\x7C"]="0E-",["\x7D"]="0",["\x7E"]="0-",["\x7F"]="0",
+ ["\x80"]="80",["\x81"]="81",["\x82"]="82",["\x83"]="83",["\x84"]="84",["\x85"]="85",["\x86"]="86",["\x87"]="87",["\x88"]="88",["\x89"]="89",["\x8A"]="0.",["\x8B"]="0E",["\x8C"]="0E-",["\x8D"]="0",["\x8E"]="0-",["\x8F"]="0",
+ ["\x90"]="90",["\x91"]="91",["\x92"]="92",["\x93"]="93",["\x94"]="94",["\x95"]="95",["\x96"]="96",["\x97"]="97",["\x98"]="98",["\x99"]="99",["\x9A"]="0.",["\x9B"]="0E",["\x9C"]="0E-",["\x9D"]="0",["\x9E"]="0-",["\x9F"]="0",
+ ["\xA0"]=".0",["\xA1"]=".1",["\xA2"]=".2",["\xA3"]=".3",["\xA4"]=".4",["\xA5"]=".5",["\xA6"]=".6",["\xA7"]=".7",["\xA8"]=".8",["\xA9"]=".9",["\xAA"]="..",["\xAB"]=".E",["\xAC"]=".E-",["\xAD"]=".",["\xAE"]=".-",["\xAF"]=".",
+ ["\xB0"]="E0",["\xB1"]="E1",["\xB2"]="E2",["\xB3"]="E3",["\xB4"]="E4",["\xB5"]="E5",["\xB6"]="E6",["\xB7"]="E7",["\xB8"]="E8",["\xB9"]="E9",["\xBA"]="E.",["\xBB"]="EE",["\xBC"]="EE-",["\xBD"]="E",["\xBE"]="E-",["\xBF"]="E",
+ ["\xC0"]="E-0",["\xC1"]="E-1",["\xC2"]="E-2",["\xC3"]="E-3",["\xC4"]="E-4",["\xC5"]="E-5",["\xC6"]="E-6",["\xC7"]="E-7",["\xC8"]="E-8",["\xC9"]="E-9",["\xCA"]="E-.",["\xCB"]="E-E",["\xCC"]="E-E-",["\xCD"]="E-",["\xCE"]="E--",["\xCF"]="E-",
+ ["\xD0"]="-0",["\xD1"]="-1",["\xD2"]="-2",["\xD3"]="-3",["\xD4"]="-4",["\xD5"]="-5",["\xD6"]="-6",["\xD7"]="-7",["\xD8"]="-8",["\xD9"]="-9",["\xDA"]="-.",["\xDB"]="-E",["\xDC"]="-E-",["\xDD"]="-",["\xDE"]="--",["\xDF"]="-",
+ }
+ local p_nibbles=P("\30")*Cs(((1-p_last)/remap)^0+p_last)/function(n)
+ top=top+1
+ stack[top]=tonumber(n) or 0
+ end
+ local p_byte=C(R("\32\246"))/function(b0)
+ top=top+1
+ stack[top]=byte(b0)-139
+ end
+ local p_positive=C(R("\247\250"))*C(1)/function(b0,b1)
+ top=top+1
+ stack[top]=(byte(b0)-247)*256+byte(b1)+108
+ end
+ local p_negative=C(R("\251\254"))*C(1)/function(b0,b1)
+ top=top+1
+ stack[top]=-(byte(b0)-251)*256-byte(b1)-108
+ end
+ local p_short=P("\28")*C(1)*C(1)/function(b1,b2)
+ top=top+1
+ local n=0x100*byte(b1)+byte(b2)
+ if n>=0x8000 then
+ stack[top]=n-0xFFFF-1
+ else
+ stack[top]=n
+ end
+ end
+ local p_long=P("\29")*C(1)*C(1)*C(1)*C(1)/function(b1,b2,b3,b4)
+ top=top+1
+ local n=0x1000000*byte(b1)+0x10000*byte(b2)+0x100*byte(b3)+byte(b4)
+ if n>=0x8000000 then
+ stack[top]=n-0xFFFFFFFF-1
+ else
+ stack[top]=n
+ end
+ end
+ local p_unsupported=P(1)/function(detail)
+ top=0
+ end
+ local p_dictionary=(
+ p_byte+p_positive+p_negative+p_short+p_long+p_nibbles+p_single+p_double+p_unsupported
+ )^1
+ parsedictionaries=function(data,dictionaries)
+ stack={}
+ strings=data.strings
+ for i=1,#dictionaries do
+ top=0
+ result={
+ monospaced=false,
+ italicangle=0,
+ underlineposition=-100,
+ underlinethickness=50,
+ painttype=0,
+ charstringtype=2,
+ fontmatrix={ 0.001,0,0,0.001,0,0 },
+ fontbbox={ 0,0,0,0 },
+ strokewidth=0,
+ charset=0,
+ encoding=0,
+ cid={
+ fontversion=0,
+ fontrevision=0,
+ fonttype=0,
+ count=8720,
+ }
+ }
+ lpegmatch(p_dictionary,dictionaries[i])
+ dictionaries[i]=result
+ end
+ result={}
+ top=0
+ stack={}
+ end
+ parseprivates=function(data,dictionaries)
+ stack={}
+ strings=data.strings
+ for i=1,#dictionaries do
+ local private=dictionaries[i].private
+ if private and private.data then
+ top=0
+ result={
+ forcebold=false,
+ languagegroup=0,
+ expansionfactor=0.06,
+ initialrandomseed=0,
+ subroutines=0,
+ defaultwidthx=0,
+ nominalwidthx=0,
+ cid={
+ },
}
+ lpegmatch(p_dictionary,private.data)
+ private.data=result
+ end
+ end
+ result={}
+ top=0
+ stack={}
+ end
+ local x=0
+ local y=0
+ local width=false
+ local r=0
+ local stems=0
+ local globalbias=0
+ local localbias=0
+ local globals=false
+ local locals=false
+ local depth=1
+ local xmin=0
+ local xmax=0
+ local ymin=0
+ local ymax=0
+ local checked=false
+ local keepcurve=false
+ local function showstate(where)
+ report("%w%-10s : [%s] n=%i",depth*2,where,concat(stack," ",1,top),top)
+ end
+ local function showvalue(where,value,showstack)
+ if showstack then
+ report("%w%-10s : %s : [%s] n=%i",depth*2,where,tostring(value),concat(stack," ",1,top),top)
+ else
+ report("%w%-10s : %s",depth*2,where,tostring(value))
+ end
+ end
+ local function moveto(x,y)
+ if keepcurve then
+ r=r+1
+ result[r]={ x,y,"m" }
+ end
+ if checked then
+ if x<xmin then xmin=x elseif x>xmax then xmax=x end
+ if y<ymin then ymin=y elseif y>ymax then ymax=y end
+ else
+ xmin=x
+ ymin=y
+ xmax=x
+ ymax=y
+ checked=true
+ end
+ end
+ local function lineto(x,y)
+ if keepcurve then
+ r=r+1
+ result[r]={ x,y,"l" }
+ end
+ if checked then
+ if x<xmin then xmin=x elseif x>xmax then xmax=x end
+ if y<ymin then ymin=y elseif y>ymax then ymax=y end
+ else
+ xmin=x
+ ymin=y
+ xmax=x
+ ymax=y
+ checked=true
+ end
+ end
+ local function curveto(x1,y1,x2,y2,x3,y3)
+ if keepcurve then
+ r=r+1
+ result[r]={ x1,y1,x2,y2,x3,y3,"c" }
+ end
+ if checked then
+ if x1<xmin then xmin=x1 elseif x1>xmax then xmax=x1 end
+ if y1<ymin then ymin=y1 elseif y1>ymax then ymax=y1 end
+ else
+ xmin=x1
+ ymin=y1
+ xmax=x1
+ ymax=y1
+ checked=true
+ end
+ if x2<xmin then xmin=x2 elseif x2>xmax then xmax=x2 end
+ if y2<ymin then ymin=y2 elseif y2>ymax then ymax=y2 end
+ if x3<xmin then xmin=x3 elseif x3>xmax then xmax=x3 end
+ if y3<ymin then ymin=y3 elseif y3>ymax then ymax=y3 end
+ end
+ local function rmoveto()
+ if top>2 then
+ if not width then
+ width=stack[1]
+ if trace_charstrings then
+ showvalue("width",width)
+ end
end
+ elseif not width then
+ width=true
end
- if #featurefiles==0 then
- featurefiles=nil
+ if trace_charstrings then
+ showstate("rmoveto")
end
+ x=x+stack[top-1]
+ y=y+stack[top]
+ top=0
+ moveto(x,y)
end
- local data=containers.read(otf.cache,hash)
- local reload=not data or data.size~=size or data.time~=time
- if forceload then
- report_otf("forced reload of %a due to hard coded flag",filename)
- reload=true
+ local function hmoveto()
+ if top>1 then
+ if not width then
+ width=stack[1]
+ if trace_charstrings then
+ showvalue("width",width)
+ end
+ end
+ elseif not width then
+ width=true
+ end
+ if trace_charstrings then
+ showstate("hmoveto")
+ end
+ x=x+stack[top]
+ top=0
+ moveto(x,y)
end
- if not reload then
- local featuredata=data.featuredata
- if featurefiles then
- if not featuredata or #featuredata~=#featurefiles then
- reload=true
- else
- for i=1,#featurefiles do
- local fi,fd=featurefiles[i],featuredata[i]
- if fi.name~=fd.name or fi.size~=fd.size or fi.time~=fd.time then
- reload=true
- break
- end
+ local function vmoveto()
+ if top>1 then
+ if not width then
+ width=stack[1]
+ if trace_charstrings then
+ showvalue("width",width)
end
end
- elseif featuredata then
- reload=true
+ elseif not width then
+ width=true
end
- if reload then
- report_otf("loading: forced reload due to changed featurefile specification %a",featurefile)
+ if trace_charstrings then
+ showstate("vmoveto")
end
- end
- if reload then
- starttiming("fontloader")
- report_otf("loading %a, hash %a",filename,hash)
- local fontdata,messages
- if sub then
- fontdata,messages=open_font(filename,sub)
+ y=y+stack[top]
+ top=0
+ moveto(x,y)
+ end
+ local function rlineto()
+ if trace_charstrings then
+ showstate("rlineto")
+ end
+ for i=1,top,2 do
+ x=x+stack[i]
+ y=y+stack[i+1]
+ lineto(x,y)
+ end
+ top=0
+ end
+ local function xlineto(swap)
+ for i=1,top do
+ if swap then
+ x=x+stack[i]
+ swap=false
+ else
+ y=y+stack[i]
+ swap=true
+ end
+ lineto(x,y)
+ end
+ top=0
+ end
+ local function hlineto()
+ if trace_charstrings then
+ showstate("hlineto")
+ end
+ xlineto(true)
+ end
+ local function vlineto()
+ if trace_charstrings then
+ showstate("vlineto")
+ end
+ xlineto(false)
+ end
+ local function rrcurveto()
+ if trace_charstrings then
+ showstate("rrcurveto")
+ end
+ for i=1,top,6 do
+ local ax=x+stack[i]
+ local ay=y+stack[i+1]
+ local bx=ax+stack[i+2]
+ local by=ay+stack[i+3]
+ x=bx+stack[i+4]
+ y=by+stack[i+5]
+ curveto(ax,ay,bx,by,x,y)
+ end
+ top=0
+ end
+ local function hhcurveto()
+ if trace_charstrings then
+ showstate("hhcurveto")
+ end
+ local s=1
+ if top%2~=0 then
+ y=y+stack[1]
+ s=2
+ end
+ for i=s,top,4 do
+ local ax=x+stack[i]
+ local ay=y
+ local bx=ax+stack[i+1]
+ local by=ay+stack[i+2]
+ x=bx+stack[i+3]
+ y=by
+ curveto(ax,ay,bx,by,x,y)
+ end
+ top=0
+ end
+ local function vvcurveto()
+ if trace_charstrings then
+ showstate("vvcurveto")
+ end
+ local s=1
+ local d=0
+ if top%2~=0 then
+ d=stack[1]
+ s=2
+ end
+ for i=s,top,4 do
+ local ax=x+d
+ local ay=y+stack[i]
+ local bx=ax+stack[i+1]
+ local by=ay+stack[i+2]
+ x=bx
+ y=by+stack[i+3]
+ curveto(ax,ay,bx,by,x,y)
+ d=0
+ end
+ top=0
+ end
+ local function xxcurveto(swap)
+ local last=top%4~=0 and stack[top]
+ if last then
+ top=top-1
+ end
+ local sw=swap
+ for i=1,top,4 do
+ local ax,ay,bx,by
+ if swap then
+ ax=x+stack[i]
+ ay=y
+ bx=ax+stack[i+1]
+ by=ay+stack[i+2]
+ y=by+stack[i+3]
+ if last and i+3==top then
+ x=bx+last
+ else
+ x=bx
+ end
+ swap=false
+ else
+ ax=x
+ ay=y+stack[i]
+ bx=ax+stack[i+1]
+ by=ay+stack[i+2]
+ x=bx+stack[i+3]
+ if last and i+3==top then
+ y=by+last
+ else
+ y=by
+ end
+ swap=true
+ end
+ curveto(ax,ay,bx,by,x,y)
+ end
+ top=0
+ end
+ local function hvcurveto()
+ if trace_charstrings then
+ showstate("hvcurveto")
+ end
+ xxcurveto(true)
+ end
+ local function vhcurveto()
+ if trace_charstrings then
+ showstate("vhcurveto")
+ end
+ xxcurveto(false)
+ end
+ local function rcurveline()
+ if trace_charstrings then
+ showstate("rcurveline")
+ end
+ for i=1,top-2,6 do
+ local ax=x+stack[i]
+ local ay=y+stack[i+1]
+ local bx=ax+stack[i+2]
+ local by=ay+stack[i+3]
+ x=bx+stack[i+4]
+ y=by+stack[i+5]
+ curveto(ax,ay,bx,by,x,y)
+ end
+ x=x+stack[top-1]
+ y=y+stack[top]
+ lineto(x,y)
+ top=0
+ end
+ local function rlinecurve()
+ if trace_charstrings then
+ showstate("rlinecurve")
+ end
+ if top>6 then
+ for i=1,top-6,2 do
+ x=x+stack[i]
+ y=y+stack[i+1]
+ lineto(x,y)
+ end
+ end
+ local ax=x+stack[top-5]
+ local ay=y+stack[top-4]
+ local bx=ax+stack[top-3]
+ local by=ay+stack[top-2]
+ x=bx+stack[top-1]
+ y=by+stack[top]
+ curveto(ax,ay,bx,by,x,y)
+ top=0
+ end
+ local function flex()
+ if trace_charstrings then
+ showstate("flex")
+ end
+ local ax=x+stack[1]
+ local ay=y+stack[2]
+ local bx=ax+stack[3]
+ local by=ay+stack[4]
+ local cx=bx+stack[5]
+ local cy=by+stack[6]
+ curveto(ax,ay,bx,by,cx,cy)
+ local dx=cx+stack[7]
+ local dy=cy+stack[8]
+ local ex=dx+stack[9]
+ local ey=dy+stack[10]
+ x=ex+stack[11]
+ y=ey+stack[12]
+ curveto(dx,dy,ex,ey,x,y)
+ top=0
+ end
+ local function hflex()
+ if trace_charstrings then
+ showstate("hflex")
+ end
+ local ax=x+stack[1]
+ local ay=y
+ local bx=ax+stack[2]
+ local by=ay+stack[3]
+ local cx=bx+stack[4]
+ local cy=by
+ curveto(ax,ay,bx,by,cx,cy)
+ local dx=cx+stack[5]
+ local dy=by
+ local ex=dx+stack[6]
+ local ey=y
+ x=ex+stack[7]
+ curveto(dx,dy,ex,ey,x,y)
+ top=0
+ end
+ local function hflex1()
+ if trace_charstrings then
+ showstate("hflex1")
+ end
+ local ax=x+stack[1]
+ local ay=y+stack[2]
+ local bx=ax+stack[3]
+ local by=ay+stack[4]
+ local cx=bx+stack[5]
+ local cy=by
+ curveto(ax,ay,bx,by,cx,cy)
+ local dx=cx+stack[6]
+ local dy=by
+ local ex=dx+stack[7]
+ local ey=dy+stack[8]
+ x=ex+stack[9]
+ curveto(dx,dy,ex,ey,x,y)
+ top=0
+ end
+ local function flex1()
+ if trace_charstrings then
+ showstate("flex1")
+ end
+ local ax=x+stack[1]
+ local ay=y+stack[2]
+ local bx=ax+stack[3]
+ local by=ay+stack[4]
+ local cx=bx+stack[5]
+ local cy=by+stack[6]
+ curveto(ax,ay,bx,by,cx,cy)
+ local dx=cx+stack[7]
+ local dy=cy+stack[8]
+ local ex=dx+stack[9]
+ local ey=dy+stack[10]
+ if abs(ex-x)>abs(ey-y) then
+ x=ex+stack[11]
else
- fontdata,messages=open_font(filename)
+ y=ey+stack[11]
end
- if fontdata then
- mainfields=mainfields or (font_fields and font_fields(fontdata))
+ curveto(dx,dy,ex,ey,x,y)
+ top=0
+ end
+ local function getstem()
+ if top==0 then
+ elseif top%2~=0 then
+ if width then
+ remove(stack,1)
+ else
+ width=remove(stack,1)
+ if trace_charstrings then
+ showvalue("width",width)
+ end
+ end
+ top=top-1
+ end
+ if trace_charstrings then
+ showstate("stem")
end
- if trace_loading and messages and #messages>0 then
- if type(messages)=="string" then
- report_otf("warning: %s",messages)
+ stems=stems+top/2
+ top=0
+ end
+ local function getmask()
+ if top==0 then
+ elseif top%2~=0 then
+ if width then
+ remove(stack,1)
else
- for m=1,#messages do
- report_otf("warning: %S",messages[m])
+ width=remove(stack,1)
+ if trace_charstrings then
+ showvalue("width",width)
end
end
+ top=top-1
+ end
+ if trace_charstrings then
+ showstate(operator==19 and "hintmark" or "cntrmask")
+ end
+ stems=stems+top/2
+ top=0
+ if stems==0 then
+ elseif stems<=8 then
+ return 1
else
- report_otf("loading done")
+ return floor((stems+7)/8)
+ end
+ end
+ local function unsupported()
+ if trace_charstrings then
+ showstate("unsupported")
+ end
+ top=0
+ end
+ local actions={ [0]=unsupported,
+ getstem,
+ unsupported,
+ getstem,
+ vmoveto,
+ rlineto,
+ hlineto,
+ vlineto,
+ rrcurveto,
+ unsupported,
+ unsupported,
+ unsupported,
+ unsupported,
+ unsupported,
+ unsupported,
+ unsupported,
+ unsupported,
+ unsupported,
+ getstem,
+ getmask,
+ getmask,
+ rmoveto,
+ hmoveto,
+ getstem,
+ rcurveline,
+ rlinecurve,
+ vvcurveto,
+ hhcurveto,
+ unsupported,
+ unsupported,
+ vhcurveto,
+ hvcurveto,
+ }
+ local subactions={
+ [034]=hflex,
+ [035]=flex,
+ [036]=hflex1,
+ [037]=flex1,
+ }
+ local p_bytes=Ct((P(1)/byte)^0)
+ local function call(scope,list,bias,process)
+ local index=stack[top]+bias
+ top=top-1
+ if trace_charstrings then
+ showvalue(scope,index,true)
+ end
+ local str=list[index]
+ if str then
+ if type(str)=="string" then
+ str=lpegmatch(p_bytes,str)
+ list[index]=str
+ end
+ depth=depth+1
+ process(str)
+ depth=depth-1
+ else
+ report("unknown %s %i",scope,index)
end
- if fontdata then
- if featurefiles then
- for i=1,#featurefiles do
- load_featurefile(fontdata,featurefiles[i].name)
+ end
+ local function process(tab)
+ local i=1
+ local n=#tab
+ while i<=n do
+ local t=tab[i]
+ if t>=32 and t<=246 then
+ top=top+1
+ stack[top]=t-139
+ i=i+1
+ elseif t>=247 and t<=250 then
+ top=top+1
+ stack[top]=(t-247)*256+tab[i+1]+108
+ i=i+2
+ elseif t>=251 and t<=254 then
+ top=top+1
+ stack[top]=-(t-251)*256-tab[i+1]-108
+ i=i+2
+ elseif t==28 then
+ top=top+1
+ local n=0x100*tab[i+1]+tab[i+2]
+ if n>=0x8000 then
+ stack[top]=n-0xFFFF-1
+ else
+ stack[top]=n
+ end
+ i=i+3
+ elseif t==255 then
+ local n=0x100*tab[i+1]+tab[i+2]
+ top=top+1
+ if n>=0x8000 then
+ stack[top]=n-0xFFFF-1+(0x100*tab[i+3]+tab[i+4])/0xFFFF
+ else
+ stack[top]=n+(0x100*tab[i+3]+tab[i+4])/0xFFFF
+ end
+ i=i+5
+ elseif t==11 then
+ if trace_charstrings then
+ showstate("return")
+ end
+ return
+ elseif t==10 then
+ call("local",locals,localbias,process)
+ i=i+1
+ elseif t==14 then
+ if width then
+ elseif top>0 then
+ width=stack[1]
+ if trace_charstrings then
+ showvalue("width",width)
+ end
+ else
+ width=true
end
+ if trace_charstrings then
+ showstate("endchar")
+ end
+ return
+ elseif t==29 then
+ call("global",globals,globalbias,process)
+ i=i+1
+ elseif t==12 then
+ i=i+1
+ local t=tab[i]
+ local a=subactions[t]
+ if a then
+ a()
+ else
+ if trace_charstrings then
+ showvalue("<subaction>",t)
+ end
+ top=0
+ end
+ i=i+1
+ else
+ local a=actions[t]
+ if a then
+ local s=a()
+ if s then
+ i=i+s
+ end
+ else
+ if trace_charstrings then
+ showvalue("<action>",t)
+ end
+ top=0
+ end
+ i=i+1
end
- local unicodes={
- }
- local splitter=lpeg.splitter(" ",unicodes)
- data={
- size=size,
- time=time,
- subfont=sub,
- format=otf_format(filename),
- featuredata=featurefiles,
- resources={
- filename=resolvers.unresolve(filename),
- version=otf.version,
- creator="context mkiv",
- unicodes=unicodes,
- indices={
- },
- duplicates={
- },
- variants={
- },
- lookuptypes={},
- },
- warnings={},
- metadata={
- },
- properties={
- },
- descriptions={},
- goodies={},
- helpers={
- tounicodelist=splitter,
- tounicodetable=Ct(splitter),
- },
+ end
+ end
+ parsecharstrings=function(data,glyphs,doshapes)
+ local dictionary=data.dictionaries[1]
+ local charstrings=dictionary.charstrings
+ local charset=dictionary.charset
+ keepcurve=doshapes
+ stack={}
+ glyphs=glyphs or {}
+ strings=data.strings
+ locals=dictionary.subroutines
+ globals=data.routines
+ globalbias=#globals
+ localbias=#locals
+ globalbias=((globalbias<1240 and 107) or (globalbias<33900 and 1131) or 32768)+1
+ localbias=((localbias<1240 and 107) or (localbias<33900 and 1131) or 32768)+1
+ local nominalwidth=dictionary.private.data.nominalwidthx or 0
+ local defaultwidth=dictionary.private.data.defaultwidthx or 0
+ for i=1,#charstrings do
+ local str=charstrings[i]
+ local tab=lpegmatch(p_bytes,str)
+ local index=i-1
+ x=0
+ y=0
+ width=false
+ r=0
+ top=0
+ stems=0
+ result={}
+ xmin=0
+ xmax=0
+ ymin=0
+ ymax=0
+ checked=false
+ if trace_charstrings then
+ report("glyph: %i",index)
+ report("data: % t",tab)
+ end
+ process(tab)
+ local boundingbox={ round(xmin),round(ymin),round(xmax),round(ymax) }
+ if width==true or width==false then
+ width=defaultwidth
+ else
+ width=nominalwidth+width
+ end
+ local glyph=glyphs[index]
+ if not glyph then
+ glyphs[index]={
+ segments=doshapes~=false and result or nil,
+ boundingbox=boundingbox,
+ width=width,
+ name=charset[index],
+ }
+ else
+ glyph.segments=doshapes~=false and result or nil
+ glyph.boundingbox=boundingbox
+ if not glyph.width then
+ glyph.width=width
+ end
+ if charset and not glyph.name then
+ glyph.name=charset[index]
+ end
+ end
+ if trace_charstrings then
+ report("width: %s",tostring(width))
+ report("boundingbox: % t",boundingbox)
+ end
+ charstrings[i]=nil
+ end
+ return glyphs
+ end
+ parsecharstring=function(data,dictionary,charstring,glyphs,index,doshapes)
+ local private=dictionary.private
+ keepcurve=doshapes
+ strings=data.strings
+ locals=dictionary.subroutines or {}
+ globals=data.routines or {}
+ globalbias=#globals
+ localbias=#locals
+ globalbias=((globalbias<1240 and 107) or (globalbias<33900 and 1131) or 32768)+1
+ localbias=((localbias<1240 and 107) or (localbias<33900 and 1131) or 32768)+1
+ local nominalwidth=private and private.data.nominalwidthx or 0
+ local defaultwidth=private and private.data.defaultwidthx or 0
+ local tab=lpegmatch(p_bytes,charstring)
+ x=0
+ y=0
+ width=false
+ r=0
+ top=0
+ stems=0
+ result={}
+ xmin=0
+ xmax=0
+ ymin=0
+ ymax=0
+ checked=false
+ if trace_charstrings then
+ report("glyph: %i",index)
+ report("data: % t",tab)
+ end
+ process(tab)
+ local boundingbox={ xmin,ymin,xmax,ymax }
+ if width==true or width==false then
+ width=defaultwidth
+ else
+ width=nominalwidth+width
+ end
+index=index-1
+ local glyph=glyphs[index]
+ if not glyph then
+ glyphs[index]={
+ segments=doshapes~=false and result or nil,
+ boundingbox=boundingbox,
+ width=width,
+ name=charset[index],
}
- report_otf("file size: %s",size)
- enhancers.apply(data,filename,fontdata)
- local packtime={}
- if packdata then
- if cleanup>0 then
- collectgarbage("collect")
+ else
+ glyph.segments=doshapes~=false and result or nil
+ glyph.boundingbox=boundingbox
+ if not glyph.width then
+ glyph.width=width
+ end
+ if charset and not glyph.name then
+ glyph.name=charset[index]
+ end
+ end
+ if trace_charstrings then
+ report("width: %s",tostring(width))
+ report("boundingbox: % t",boundingbox)
+ end
+ return charstring
+ end
+ resetcharstrings=function()
+ result={}
+ top=0
+ stack={}
+ end
+end
+local function readglobals(f,data)
+ local routines=readlengths(f)
+ for i=1,#routines do
+ routines[i]=readstring(f,routines[i])
+ end
+ data.routines=routines
+end
+local function readencodings(f,data)
+ data.encodings={}
+end
+local function readcharsets(f,data,dictionary)
+ local header=data.header
+ local strings=data.strings
+ local nofglyphs=data.nofglyphs
+ local charsetoffset=dictionary.charset
+ if charsetoffset~=0 then
+ setposition(f,header.offset+charsetoffset)
+ local format=readbyte(f)
+ local charset={ [0]=".notdef" }
+ dictionary.charset=charset
+ if format==0 then
+ for i=1,nofglyphs do
+ charset[i]=strings[readushort(f)]
+ end
+ elseif format==1 or format==2 then
+ local readcount=format==1 and readbyte or readushort
+ local i=1
+ while i<=nofglyphs do
+ local sid=readushort(f)
+ local n=readcount(f)
+ for s=sid,sid+n do
+ charset[i]=strings[s]
+ i=i+1
+ if i>nofglyphs then
+ break
+ end
end
- starttiming(packtime)
- enhance("pack",data,filename,nil)
- stoptiming(packtime)
end
- report_otf("saving %a in cache",filename)
- data=containers.write(otf.cache,hash,data)
- if cleanup>1 then
- collectgarbage("collect")
+ else
+ report("cff parser: unsupported charset format %a",format)
+ end
+ end
+end
+local function readprivates(f,data)
+ local header=data.header
+ local dictionaries=data.dictionaries
+ local private=dictionaries[1].private
+ if private then
+ setposition(f,header.offset+private.offset)
+ private.data=readstring(f,private.size)
+ end
+end
+local function readlocals(f,data,dictionary)
+ local header=data.header
+ local private=dictionary.private
+ if private then
+ local subroutineoffset=private.data.subroutines
+ if subroutineoffset~=0 then
+ setposition(f,header.offset+private.offset+subroutineoffset)
+ local subroutines=readlengths(f)
+ for i=1,#subroutines do
+ subroutines[i]=readstring(f,subroutines[i])
end
- stoptiming("fontloader")
- if elapsedtime then
- report_otf("loading, optimizing, packing and caching time %s, pack time %s",
- elapsedtime("fontloader"),packdata and elapsedtime(packtime) or 0)
+ dictionary.subroutines=subroutines
+ private.data.subroutines=nil
+ else
+ dictionary.subroutines={}
+ end
+ else
+ dictionary.subroutines={}
+ end
+end
+local function readcharstrings(f,data)
+ local header=data.header
+ local dictionaries=data.dictionaries
+ local dictionary=dictionaries[1]
+ local type=dictionary.charstringtype
+ local offset=dictionary.charstrings
+ if type==2 then
+ setposition(f,header.offset+offset)
+ local charstrings=readlengths(f)
+ local nofglyphs=#charstrings
+ for i=1,nofglyphs do
+ charstrings[i]=readstring(f,charstrings[i])
+ end
+ data.nofglyphs=nofglyphs
+ dictionary.charstrings=charstrings
+ else
+ report("unsupported charstr type %i",type)
+ data.nofglyphs=0
+ dictionary.charstrings={}
+ end
+end
+local function readcidprivates(f,data)
+ local header=data.header
+ local dictionaries=data.dictionaries[1].cid.dictionaries
+ for i=1,#dictionaries do
+ local dictionary=dictionaries[i]
+ local private=dictionary.private
+ if private then
+ setposition(f,header.offset+private.offset)
+ private.data=readstring(f,private.size)
+ end
+ end
+ parseprivates(data,dictionaries)
+end
+local function readnoselect(f,data,glyphs,doshapes)
+ local dictionaries=data.dictionaries
+ local dictionary=dictionaries[1]
+ readglobals(f,data)
+ readcharstrings(f,data)
+ readencodings(f,data)
+ readcharsets(f,data,dictionary)
+ readprivates(f,data)
+ parseprivates(data,data.dictionaries)
+ readlocals(f,data,dictionary)
+ parsecharstrings(data,glyphs,doshapes)
+ resetcharstrings()
+end
+local function readfdselect(f,data,glyphs,doshapes)
+ local header=data.header
+ local dictionaries=data.dictionaries
+ local dictionary=dictionaries[1]
+ local cid=dictionary.cid
+ local cidselect=cid and cid.fdselect
+ readglobals(f,data)
+ readcharstrings(f,data)
+ readencodings(f,data)
+ local charstrings=dictionary.charstrings
+ local fdindex={}
+ local nofglyphs=data.nofglyphs
+ local maxindex=-1
+ setposition(f,header.offset+cidselect)
+ local format=readbyte(f)
+ if format==1 then
+ for i=0,nofglyphs do
+ local index=readbyte(i)
+ fdindex[i]=index
+ if index>maxindex then
+ maxindex=index
+ end
+ end
+ elseif format==3 then
+ local nofranges=readushort(f)
+ local first=readushort(f)
+ local index=readbyte(f)
+ while true do
+ local last=readushort(f)
+ if index>maxindex then
+ maxindex=index
end
- close_font(fontdata)
- if cleanup>3 then
- collectgarbage("collect")
+ for i=first,last do
+ fdindex[i]=index
end
- data=containers.read(otf.cache,hash)
- if cleanup>2 then
- collectgarbage("collect")
+ if last>=nofglyphs then
+ break
+ else
+ first=last+1
+ index=readbyte(f)
end
- else
- stoptiming("fontloader")
- data=nil
- report_otf("loading failed due to read error")
end
+ else
end
- if data then
- if trace_defining then
- report_otf("loading from cache using hash %a",hash)
- end
- enhance("unpack",data,filename,nil,false)
- local resources=data.resources
- local lookuptags=resources.lookuptags
- local unicodes=resources.unicodes
- if not lookuptags then
- lookuptags={}
- resources.lookuptags=lookuptags
+ if maxindex>=0 then
+ local cidarray=cid.fdarray
+ setposition(f,header.offset+cidarray)
+ local dictionaries=readlengths(f)
+ for i=1,#dictionaries do
+ dictionaries[i]=readstring(f,dictionaries[i])
+ end
+ parsedictionaries(data,dictionaries)
+ cid.dictionaries=dictionaries
+ readcidprivates(f,data)
+ for i=1,#dictionaries do
+ readlocals(f,data,dictionaries[i])
+ end
+ for i=1,#charstrings do
+ parsecharstring(data,dictionaries[fdindex[i]+1],charstrings[i],glyphs,i,doshapes)
+ end
+ resetcharstrings()
+ end
+end
+function readers.cff(f,fontdata,specification)
+ if specification.details then
+ local datatable=fontdata.tables.cff
+ if datatable then
+ local offset=datatable.offset
+ local glyphs=fontdata.glyphs
+ if not f then
+ report("invalid filehandle")
+ return
+ end
+ if offset then
+ setposition(f,offset)
+ end
+ local header=readheader(f)
+ if header.major>1 then
+ report("version mismatch")
+ return
+ end
+ local names=readfontnames(f)
+ local dictionaries=readtopdictionaries(f)
+ local strings=readstrings(f)
+ local data={
+ header=header,
+ names=names,
+ dictionaries=dictionaries,
+ strings=strings,
+ nofglyphs=fontdata.nofglyphs,
+ }
+ parsedictionaries(data,data.dictionaries)
+ local d=dictionaries[1]
+ local c=d.cid
+ fontdata.cffinfo={
+ familynamename=d.familyname,
+ fullname=d.fullname,
+ boundingbox=d.boundingbox,
+ weight=d.weight,
+ italicangle=d.italicangle,
+ underlineposition=d.underlineposition,
+ underlinethickness=d.underlinethickness,
+ monospaced=d.monospaced,
+ }
+ fontdata.cidinfo=c and {
+ registry=c.registry,
+ ordering=c.ordering,
+ supplement=c.supplement,
+ }
+ if not specification.glyphs then
+ else
+ local cid=d.cid
+ if cid and cid.fdselect then
+ readfdselect(f,data,glyphs,specification.shapes or false)
+ else
+ readnoselect(f,data,glyphs,specification.shapes or false)
+ end
+ end
end
- setmetatableindex(lookuptags,function(t,k)
- local v=type(k)=="number" and ("lookup "..k) or k
- t[k]=v
- return v
- end)
- if not unicodes then
- unicodes={}
- resources.unicodes=unicodes
- setmetatableindex(unicodes,function(t,k)
- setmetatableindex(unicodes,nil)
- for u,d in next,data.descriptions do
- local n=d.name
- if n then
- t[n]=u
- else
+ end
+end
+
+end -- closure
+
+do -- begin closure to overcome local limits and interference
+
+if not modules then modules={} end modules ['font-ttf']={
+ version=1.001,
+ comment="companion to font-ini.mkiv",
+ author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright="PRAGMA ADE / ConTeXt Development Team",
+ license="see context related readme files"
+}
+local next,type,unpack=next,type,unpack
+local bittest=bit32.btest
+local sqrt=math.sqrt
+local report=logs.reporter("otf reader","ttf")
+local readers=fonts.handlers.otf.readers
+local streamreader=readers.streamreader
+local setposition=streamreader.setposition
+local getposition=streamreader.getposition
+local skipbytes=streamreader.skip
+local readbyte=streamreader.readcardinal1
+local readushort=streamreader.readcardinal2
+local readulong=streamreader.readcardinal4
+local readchar=streamreader.readinteger1
+local readshort=streamreader.readinteger2
+local read2dot14=streamreader.read2dot14
+local function mergecomposites(glyphs,shapes)
+ local function merge(index,shape,components)
+ local contours={}
+ local nofcontours=0
+ for i=1,#components do
+ local component=components[i]
+ local subindex=component.index
+ local subshape=shapes[subindex]
+ local subcontours=subshape.contours
+ if not subcontours then
+ local subcomponents=subshape.components
+ if subcomponents then
+ subcontours=merge(subindex,subshape,subcomponents)
+ end
+ end
+ if subcontours then
+ local matrix=component.matrix
+ local xscale=matrix[1]
+ local xrotate=matrix[2]
+ local yrotate=matrix[3]
+ local yscale=matrix[4]
+ local xoffset=matrix[5]
+ local yoffset=matrix[6]
+ for i=1,#subcontours do
+ local points=subcontours[i]
+ local result={}
+ for i=1,#points do
+ local p=points[i]
+ local x=p[1]
+ local y=p[2]
+ result[i]={
+ xscale*x+xrotate*y+xoffset,
+ yscale*y+yrotate*x+yoffset,
+ p[3]
+ }
end
+ nofcontours=nofcontours+1
+ contours[nofcontours]=result
end
- return rawget(t,k)
- end)
+ else
+ report("missing contours composite %s, component %s of %s, glyph %s",index,i,#components,subindex)
+ end
end
- constructors.addcoreunicodes(unicodes)
- if applyruntimefixes then
- applyruntimefixes(filename,data)
+ shape.contours=contours
+ shape.components=nil
+ return contours
+ end
+ for index=1,#glyphs do
+ local shape=shapes[index]
+ local components=shape.components
+ if components then
+ merge(index,shape,components)
end
- enhance("add dimensions",data,filename,nil,false)
- if trace_sequences then
- showfeatureorder(data,filename)
+ end
+end
+local function readnothing(f,nofcontours)
+ return {
+ type="nothing",
+ }
+end
+local function curveto(m_x,m_y,l_x,l_y,r_x,r_y)
+ return {
+ l_x+2/3*(m_x-l_x),l_y+2/3*(m_y-l_y),
+ r_x+2/3*(m_x-r_x),r_y+2/3*(m_y-r_y),
+ r_x,r_y,"c"
+ }
+end
+local function contours2outlines(glyphs,shapes)
+ local quadratic=true
+ for index=1,#glyphs do
+ local glyph=glyphs[index]
+ local shape=shapes[index]
+ local contours=shape.contours
+ if contours then
+ local nofcontours=#contours
+ local segments={}
+ local nofsegments=0
+ glyph.segments=segments
+ if nofcontours>0 then
+ for i=1,nofcontours do
+ local contour=contours[i]
+ local nofcontour=#contour
+ if nofcontour>0 then
+ local first_pt=contour[1]
+ local first_on=first_pt[3]
+ if nofcontour==1 then
+ first_pt[3]="m"
+ nofsegments=nofsegments+1
+ segments[nofsegments]=first_pt
+ else
+ local first_on=first_pt[3]
+ local last_pt=contour[nofcontour]
+ local last_on=last_pt[3]
+ local start=1
+ local control_pt=false
+ if first_on then
+ start=2
+ else
+ if last_on then
+ first_pt=last_pt
+ else
+ first_pt={ (first_pt[1]+last_pt[1])/2,(first_pt[2]+last_pt[2])/2,false }
+ end
+ control_pt=first_pt
+ end
+ nofsegments=nofsegments+1
+ segments[nofsegments]={ first_pt[1],first_pt[2],"m" }
+ local previous_pt=first_pt
+ for i=start,nofcontour do
+ local current_pt=contour[i]
+ local current_on=current_pt[3]
+ local previous_on=previous_pt[3]
+ if previous_on then
+ if current_on then
+ nofsegments=nofsegments+1
+ segments[nofsegments]={ current_pt[1],current_pt[2],"l" }
+ else
+ control_pt=current_pt
+ end
+ elseif current_on then
+ local ps=segments[nofsegments]
+ nofsegments=nofsegments+1
+ if quadratic then
+ segments[nofsegments]={ control_pt[1],control_pt[2],current_pt[1],current_pt[2],"q" }
+ else
+ local p=segments[nofsegments-1] local n=#p
+ segments[nofsegments]=curveto(control_pt[1],control_pt[2],p[n-2],p[n-1],current_pt[1],current_pt[2])
+ end
+ control_pt=false
+ else
+ nofsegments=nofsegments+1
+ local halfway_x=(previous_pt[1]+current_pt[1])/2
+ local halfway_y=(previous_pt[2]+current_pt[2])/2
+ if quadratic then
+ segments[nofsegments]={ control_pt[1],control_pt[2],halfway_x,halfway_y,"q" }
+ else
+ local p=segments[nofsegments-1] local n=#p
+ segments[nofsegments]=curveto(control_pt[1],control_pt[2],p[n-2],p[n-1],halfway_x,halfway_y)
+ end
+ control_pt=current_pt
+ end
+ previous_pt=current_pt
+ end
+ if first_pt==last_pt then
+ else
+ nofsegments=nofsegments+1
+ if not control_pt then
+ segments[nofsegments]={ first_pt[1],first_pt[2],"l" }
+ elseif quadratic then
+ segments[nofsegments]={ control_pt[1],control_pt[2],first_pt[1],first_pt[2],"q" }
+ else
+ local p=last_pt local n=#p
+ segments[nofsegments]=curveto(control_pt[1],control_pt[2],p[n-2],p[n-1],first_pt[1],first_pt[2])
+ end
+ end
+ end
+ end
+ end
+ end
end
end
- return data
end
-local mt={
- __index=function(t,k)
- if k=="height" then
- local ht=t.boundingbox[4]
- return ht<0 and 0 or ht
- elseif k=="depth" then
- local dp=-t.boundingbox[2]
- return dp<0 and 0 or dp
- elseif k=="width" then
- return 0
- elseif k=="name" then
- return forcenotdef and ".notdef"
+local function readglyph(f,nofcontours)
+ local points={}
+ local endpoints={}
+ local instructions={}
+ local flags={}
+ for i=1,nofcontours do
+ endpoints[i]=readshort(f)+1
+ end
+ local nofpoints=endpoints[nofcontours]
+ local nofinstructions=readushort(f)
+ skipbytes(f,nofinstructions)
+ local i=1
+ while i<=nofpoints do
+ local flag=readbyte(f)
+ flags[i]=flag
+ if bittest(flag,0x0008) then
+ for j=1,readbyte(f) do
+ i=i+1
+ flags[i]=flag
+ end
end
+ i=i+1
end
-}
-actions["prepare tables"]=function(data,filename,raw)
- data.properties.hasitalics=false
+ local x=0
+ for i=1,nofpoints do
+ local flag=flags[i]
+ local short=bittest(flag,0x0002)
+ local same=bittest(flag,0x0010)
+ if short then
+ if same then
+ x=x+readbyte(f)
+ else
+ x=x-readbyte(f)
+ end
+ elseif same then
+ else
+ x=x+readshort(f)
+ end
+ points[i]={ x,y,bittest(flag,0x0001) }
+ end
+ local y=0
+ for i=1,nofpoints do
+ local flag=flags[i]
+ local short=bittest(flag,0x0004)
+ local same=bittest(flag,0x0020)
+ if short then
+ if same then
+ y=y+readbyte(f)
+ else
+ y=y-readbyte(f)
+ end
+ elseif same then
+ else
+ y=y+readshort(f)
+ end
+ points[i][2]=y
+ end
+ local first=1
+ for i=1,#endpoints do
+ local last=endpoints[i]
+ endpoints[i]={ unpack(points,first,last) }
+ first=last+1
+ end
+ return {
+ type="glyph",
+ contours=endpoints,
+ }
end
-actions["add dimensions"]=function(data,filename)
- if data then
- local descriptions=data.descriptions
- local resources=data.resources
- local defaultwidth=resources.defaultwidth or 0
- local defaultheight=resources.defaultheight or 0
- local defaultdepth=resources.defaultdepth or 0
- local basename=trace_markwidth and file.basename(filename)
- for _,d in next,descriptions do
- local bb,wd=d.boundingbox,d.width
- if not wd then
- d.width=defaultwidth
- elseif trace_markwidth and wd~=0 and d.class=="mark" then
- report_otf("mark %a with width %b found in %a",d.name or "<noname>",wd,basename)
+local function readcomposite(f)
+ local components={}
+ local nofcomponents=0
+ local instructions=false
+ while true do
+ local flags=readushort(f)
+ local index=readushort(f)
+ local f_xyarg=bittest(flags,0x0002)
+ local f_offset=bittest(flags,0x0800)
+ local xscale=1
+ local xrotate=0
+ local yrotate=0
+ local yscale=1
+ local xoffset=0
+ local yoffset=0
+ local base=false
+ local reference=false
+ if f_xyarg then
+ if bittest(flags,0x0001) then
+ xoffset=readshort(f)
+ yoffset=readshort(f)
+ else
+ xoffset=readchar(f)
+ yoffset=readchar(f)
end
- if bb then
- local ht=bb[4]
- local dp=-bb[2]
- if ht==0 or ht<0 then
- else
- d.height=ht
- end
- if dp==0 or dp<0 then
+ else
+ if bittest(flags,0x0001) then
+ base=readshort(f)
+ reference=readshort(f)
+ else
+ base=readchar(f)
+ reference=readchar(f)
+ end
+ end
+ if bittest(flags,0x0008) then
+ xscale=read2dot14(f)
+ yscale=xscale
+ if f_xyarg and f_offset then
+ xoffset=xoffset*xscale
+ yoffset=yoffset*yscale
+ end
+ elseif bittest(flags,0x0040) then
+ xscale=read2dot14(f)
+ yscale=read2dot14(f)
+ if f_xyarg and f_offset then
+ xoffset=xoffset*xscale
+ yoffset=yoffset*yscale
+ end
+ elseif bittest(flags,0x0080) then
+ xscale=read2dot14(f)
+ xrotate=read2dot14(f)
+ yrotate=read2dot14(f)
+ yscale=read2dot14(f)
+ if f_xyarg and f_offset then
+ xoffset=xoffset*sqrt(xscale^2+xrotate^2)
+ yoffset=yoffset*sqrt(yrotate^2+yscale^2)
+ end
+ end
+ nofcomponents=nofcomponents+1
+ components[nofcomponents]={
+ index=index,
+ usemine=bittest(flags,0x0200),
+ round=bittest(flags,0x0006),
+ base=base,
+ reference=reference,
+ matrix={ xscale,xrotate,yrotate,yscale,xoffset,yoffset },
+ }
+ if bittest(flags,0x0100) then
+ instructions=true
+ end
+ if not bittest(flags,0x0020) then
+ break
+ end
+ end
+ return {
+ type="composite",
+ components=components,
+ }
+end
+function readers.loca(f,fontdata,specification)
+ if specification.glyphs then
+ local datatable=fontdata.tables.loca
+ if datatable then
+ local offset=fontdata.tables.glyf.offset
+ local format=fontdata.fontheader.indextolocformat
+ local locations={}
+ setposition(f,datatable.offset)
+ if format==1 then
+ local nofglyphs=datatable.length/4-1
+ -1
+ for i=0,nofglyphs do
+ locations[i]=offset+readulong(f)
+ end
+ fontdata.nofglyphs=nofglyphs
+ else
+ local nofglyphs=datatable.length/2-1
+ -1
+ for i=0,nofglyphs do
+ locations[i]=offset+readushort(f)*2
+ end
+ fontdata.nofglyphs=nofglyphs
+ end
+ fontdata.locations=locations
+ end
+ end
+end
+function readers.glyf(f,fontdata,specification)
+ if specification.glyphs then
+ local datatable=fontdata.tables.glyf
+ if datatable then
+ local locations=fontdata.locations
+ if locations then
+ local glyphs=fontdata.glyphs
+ local nofglyphs=fontdata.nofglyphs
+ local filesize=fontdata.filesize
+ local nothing={ 0,0,0,0 }
+ local shapes={}
+ local loadshapes=specification.shapes
+ for index=0,nofglyphs do
+ local location=locations[index]
+ if location>=filesize then
+ report("discarding %s glyphs due to glyph location bug",nofglyphs-index+1)
+ fontdata.nofglyphs=index-1
+ fontdata.badfont=true
+ break
+ elseif location>0 then
+ setposition(f,location)
+ local nofcontours=readshort(f)
+ glyphs[index].boundingbox={
+ readshort(f),
+ readshort(f),
+ readshort(f),
+ readshort(f),
+ }
+ if not loadshapes then
+ elseif nofcontours==0 then
+ shapes[index]=readnothing(f,nofcontours)
+ elseif nofcontours>0 then
+ shapes[index]=readglyph(f,nofcontours)
+ else
+ shapes[index]=readcomposite(f,nofcontours)
+ end
else
- d.depth=dp
+ if loadshapes then
+ shapes[index]={}
+ end
+ glyphs[index].boundingbox=nothing
end
+ end
+ if loadshapes then
+ mergecomposites(glyphs,shapes)
+ contours2outlines(glyphs,shapes)
+ end
end
end
end
end
-local function somecopy(old)
- if old then
- local new={}
- if type(old)=="table" then
- for k,v in next,old do
- if k=="glyphs" then
- elseif type(v)=="table" then
- new[k]=somecopy(v)
- else
- new[k]=v
- end
+
+end -- closure
+
+do -- begin closure to overcome local limits and interference
+
+if not modules then modules={} end modules ['font-dsp']={
+ version=1.001,
+ comment="companion to font-ini.mkiv",
+ author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright="PRAGMA ADE / ConTeXt Development Team",
+ license="see context related readme files"
+}
+local next,type=next,type
+local bittest=bit32.btest
+local rshift=bit32.rshift
+local concat=table.concat
+local lower=string.lower
+local sub=string.sub
+local strip=string.strip
+local tohash=table.tohash
+local reversed=table.reversed
+local setmetatableindex=table.setmetatableindex
+local formatters=string.formatters
+local sortedkeys=table.sortedkeys
+local sortedhash=table.sortedhash
+local report=logs.reporter("otf reader")
+local readers=fonts.handlers.otf.readers
+local streamreader=readers.streamreader
+local setposition=streamreader.setposition
+local skipbytes=streamreader.skip
+local skipshort=streamreader.skipshort
+local readushort=streamreader.readcardinal2
+local readulong=streamreader.readcardinal4
+local readshort=streamreader.readinteger2
+local readfword=readshort
+local readstring=streamreader.readstring
+local readtag=streamreader.readtag
+local gsubhandlers={}
+local gposhandlers={}
+local lookupidoffset=-1
+local classes={
+ "base",
+ "ligature",
+ "mark",
+ "component",
+}
+local gsubtypes={
+ "single",
+ "multiple",
+ "alternate",
+ "ligature",
+ "context",
+ "chainedcontext",
+ "extension",
+ "reversechainedcontextsingle",
+}
+local gpostypes={
+ "single",
+ "pair",
+ "cursive",
+ "marktobase",
+ "marktoligature",
+ "marktomark",
+ "context",
+ "chainedcontext",
+ "extension",
+}
+local chaindirections={
+ context=0,
+ chainedcontext=1,
+ reversechainedcontextsingle=-1,
+}
+local lookupnames={
+ gsub={
+ single="gsub_single",
+ multiple="gsub_multiple",
+ alternate="gsub_alternate",
+ ligature="gsub_ligature",
+ context="gsub_context",
+ chainedcontext="gsub_contextchain",
+ reversechainedcontextsingle="gsub_reversecontextchain",
+ },
+ gpos={
+ single="gpos_single",
+ pair="gpos_pair",
+ cursive="gpos_cursive",
+ marktobase="gpos_mark2base",
+ marktoligature="gpos_mark2ligature",
+ marktomark="gpos_mark2mark",
+ context="gpos_context",
+ chainedcontext="gpos_contextchain",
+ }
+}
+local lookupflags=setmetatableindex(function(t,k)
+ local v={
+ bittest(k,0x0008) and true or false,
+ bittest(k,0x0004) and true or false,
+ bittest(k,0x0002) and true or false,
+ bittest(k,0x0001) and true or false,
+ }
+ t[k]=v
+ return v
+end)
+local function readcoverage(f,offset,simple)
+ setposition(f,offset)
+ local coverageformat=readushort(f)
+ local coverage={}
+ if coverageformat==1 then
+ local nofcoverage=readushort(f)
+ if simple then
+ for i=1,nofcoverage do
+ coverage[i]=readushort(f)
end
else
- for i=1,#mainfields do
- local k=mainfields[i]
- local v=old[k]
- if k=="glyphs" then
- elseif type(v)=="table" then
- new[k]=somecopy(v)
- else
- new[k]=v
+ for i=0,nofcoverage-1 do
+ coverage[readushort(f)]=i
+ end
+ end
+ elseif coverageformat==2 then
+ local nofranges=readushort(f)
+ local n=simple and 1 or 0
+ for i=1,nofranges do
+ local firstindex=readushort(f)
+ local lastindex=readushort(f)
+ local coverindex=readushort(f)
+ if simple then
+ for i=firstindex,lastindex do
+ coverage[n]=i
+ n=n+1
+ end
+ else
+ for i=firstindex,lastindex do
+ coverage[i]=n
+ n=n+1
end
end
end
- return new
else
- return {}
+ report("unknown coverage format %a ",coverageformat)
+ end
+ return coverage
+end
+local function readclassdef(f,offset)
+ setposition(f,offset)
+ local classdefformat=readushort(f)
+ local classdef={}
+ if classdefformat==1 then
+ local index=readushort(f)
+ local nofclassdef=readushort(f)
+ for i=1,nofclassdef do
+ classdef[index]=readushort(f)+1
+ index=index+1
+ end
+ elseif classdefformat==2 then
+ local nofranges=readushort(f)
+ local n=0
+ for i=1,nofranges do
+ local firstindex=readushort(f)
+ local lastindex=readushort(f)
+ local class=readushort(f)+1
+ for i=firstindex,lastindex do
+ classdef[i]=class
+ end
+ end
+ else
+ report("unknown classdef format %a ",classdefformat)
end
+ return classdef
end
-actions["prepare glyphs"]=function(data,filename,raw)
- local tableversion=tonumber(raw.table_version) or 0
- local rawglyphs=raw.glyphs
- local rawsubfonts=raw.subfonts
- local rawcidinfo=raw.cidinfo
- local criterium=constructors.privateoffset
- local private=criterium
- local resources=data.resources
- local metadata=data.metadata
- local properties=data.properties
- local descriptions=data.descriptions
- local unicodes=resources.unicodes
- local indices=resources.indices
- local duplicates=resources.duplicates
- local variants=resources.variants
- if rawsubfonts then
- metadata.subfonts=includesubfonts and {}
- properties.cidinfo=rawcidinfo
- if rawcidinfo.registry then
- local cidmap=fonts.cid.getmap(rawcidinfo)
- if cidmap then
- rawcidinfo.usedname=cidmap.usedname
- local nofnames=0
- local nofunicodes=0
- local cidunicodes=cidmap.unicodes
- local cidnames=cidmap.names
- local cidtotal=0
- local unique=trace_subfonts and {}
- for cidindex=1,#rawsubfonts do
- local subfont=rawsubfonts[cidindex]
- local cidglyphs=subfont.glyphs
- if includesubfonts then
- metadata.subfonts[cidindex]=somecopy(subfont)
- end
- local cidcnt,cidmin,cidmax
- if tableversion>0.3 then
- cidcnt=subfont.glyphcnt
- cidmin=subfont.glyphmin
- cidmax=subfont.glyphmax
- else
- cidcnt=subfont.glyphcnt
- cidmin=0
- cidmax=cidcnt-1
- end
- if trace_subfonts then
- local cidtot=cidmax-cidmin+1
- cidtotal=cidtotal+cidtot
- report_otf("subfont: %i, min: %i, max: %i, cnt: %i, n: %i",cidindex,cidmin,cidmax,cidtot,cidcnt)
- end
- if cidcnt>0 then
- for cidslot=cidmin,cidmax do
- local glyph=cidglyphs[cidslot]
- if glyph then
- local index=tableversion>0.3 and glyph.orig_pos or cidslot
- if trace_subfonts then
- unique[index]=true
- end
- local unicode=glyph.unicode
- if unicode>=0x00E000 and unicode<=0x00F8FF then
- unicode=-1
- elseif unicode>=0x0F0000 and unicode<=0x0FFFFD then
- unicode=-1
- elseif unicode>=0x100000 and unicode<=0x10FFFD then
- unicode=-1
+local function classtocoverage(defs)
+ if defs then
+ local list={}
+ for index,class in next,defs do
+ local c=list[class]
+ if c then
+ c[#c+1]=index
+ else
+ list[class]={ index }
+ end
+ end
+ return list
+ end
+end
+local function readposition(f,format)
+ if format==0 then
+ return nil
+ end
+ local x=bittest(format,0x0001) and readshort(f) or 0
+ local y=bittest(format,0x0002) and readshort(f) or 0
+ local h=bittest(format,0x0004) and readshort(f) or 0
+ local v=bittest(format,0x0008) and readshort(f) or 0
+ if x==0 and y==0 and h==0 and v==0 then
+ return nil
+ else
+ return { x,y,h,v }
+ end
+end
+local function readanchor(f,offset)
+ if not offset or offset==0 then
+ return nil
+ end
+ setposition(f,offset)
+ local format=readshort(f)
+ if format==0 then
+ report("invalid anchor format %i @ position %i",format,offset)
+ return false
+ elseif format>3 then
+ report("unsupported anchor format %i @ position %i",format,offset)
+ return false
+ end
+ return { readshort(f),readshort(f) }
+end
+local function readfirst(f,offset)
+ if offset then
+ setposition(f,offset)
+ end
+ return { readushort(f) }
+end
+local function readarray(f,offset,first)
+ if offset then
+ setposition(f,offset)
+ end
+ local n=readushort(f)
+ if first then
+ local t={ first }
+ for i=2,n do
+ t[i]=readushort(f)
+ end
+ return t,n
+ elseif n>0 then
+ local t={}
+ for i=1,n do
+ t[i]=readushort(f)
+ end
+ return t,n
+ end
+end
+local function readcoveragearray(f,offset,t,simple)
+ if not t then
+ return nil
+ end
+ local n=#t
+ if n==0 then
+ return nil
+ end
+ for i=1,n do
+ t[i]=readcoverage(f,offset+t[i],simple)
+ end
+ return t
+end
+local function covered(subset,all)
+ local used,u
+ for i=1,#subset do
+ local s=subset[i]
+ if all[s] then
+ if used then
+ u=u+1
+ used[u]=s
+ else
+ u=1
+ used={ s }
+ end
+ end
+ end
+ return used
+end
+local function unchainedcontext(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,what)
+ local tableoffset=lookupoffset+offset
+ setposition(f,tableoffset)
+ local subtype=readushort(f)
+ if subtype==1 then
+ local coverage=readushort(f)
+ local subclasssets=readarray(f)
+ local rules={}
+ if subclasssets then
+ coverage=readcoverage(f,tableoffset+coverage,true)
+ for i=1,#subclasssets do
+ local offset=subclasssets[i]
+ if offset>0 then
+ local firstcoverage=coverage[i]
+ local rulesoffset=tableoffset+offset
+ local subclassrules=readarray(f,rulesoffset)
+ for rule=1,#subclassrules do
+ setposition(f,rulesoffset+subclassrules[rule])
+ local nofcurrent=readushort(f)
+ local noflookups=readushort(f)
+ local current={ { firstcoverage } }
+ for i=2,nofcurrent do
+ current[i]={ readushort(f) }
+ end
+ local lookups={}
+ for i=1,noflookups do
+ lookups[readushort(f)+1]=readushort(f)+1
+ end
+ rules[#rules+1]={
+ current=current,
+ lookups=lookups
+ }
+ end
+ end
+ end
+ else
+ report("empty subclassset in %a subtype %i","unchainedcontext",subtype)
+ end
+ return {
+ format="glyphs",
+ rules=rules,
+ }
+ elseif subtype==2 then
+ local coverage=readushort(f)
+ local currentclassdef=readushort(f)
+ local subclasssets=readarray(f)
+ local rules={}
+ if subclasssets then
+ coverage=readcoverage(f,tableoffset+coverage)
+ currentclassdef=readclassdef(f,tableoffset+currentclassdef)
+ local currentclasses=classtocoverage(currentclassdef,fontdata.glyphs)
+ for class=1,#subclasssets do
+ local offset=subclasssets[class]
+ if offset>0 then
+ local firstcoverage=currentclasses[class]
+ if firstcoverage then
+ firstcoverage=covered(firstcoverage,coverage)
+ if firstcoverage then
+ local rulesoffset=tableoffset+offset
+ local subclassrules=readarray(f,rulesoffset)
+ for rule=1,#subclassrules do
+ setposition(f,rulesoffset+subclassrules[rule])
+ local nofcurrent=readushort(f)
+ local noflookups=readushort(f)
+ local current={ firstcoverage }
+ for i=2,nofcurrent do
+ current[i]=currentclasses[readushort(f)+1]
end
- local name=glyph.name or cidnames[index]
- if not unicode or unicode==-1 then
- unicode=cidunicodes[index]
+ local lookups={}
+ for i=1,noflookups do
+ lookups[readushort(f)+1]=readushort(f)+1
end
- if unicode and descriptions[unicode] then
- if trace_private then
- report_otf("preventing glyph %a at index %H to overload unicode %U",name or "noname",index,unicode)
+ rules[#rules+1]={
+ current=current,
+ lookups=lookups
+ }
+ end
+ else
+ report("no coverage")
+ end
+ else
+ report("no coverage class")
+ end
+ end
+ end
+ else
+ report("empty subclassset in %a subtype %i","unchainedcontext",subtype)
+ end
+ return {
+ format="class",
+ rules=rules,
+ }
+ elseif subtype==3 then
+ local current=readarray(f)
+ local noflookups=readushort(f)
+ local lookups={}
+ for i=1,noflookups do
+ lookups[readushort(f)+1]=readushort(f)+1
+ end
+ current=readcoveragearray(f,tableoffset,current,true)
+ return {
+ format="coverage",
+ rules={
+ {
+ current=current,
+ lookups=lookups,
+ }
+ }
+ }
+ else
+ report("unsupported subtype %a in %a %s",subtype,"unchainedcontext",what)
+ end
+end
+local function chainedcontext(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,what)
+ local tableoffset=lookupoffset+offset
+ setposition(f,tableoffset)
+ local subtype=readushort(f)
+ if subtype==1 then
+ local coverage=readushort(f)
+ local subclasssets=readarray(f)
+ local rules={}
+ if subclasssets then
+ coverage=readcoverage(f,tableoffset+coverage,true)
+ for i=1,#subclasssets do
+ local offset=subclasssets[i]
+ if offset>0 then
+ local firstcoverage=coverage[i]
+ local rulesoffset=tableoffset+offset
+ local subclassrules=readarray(f,rulesoffset)
+ for rule=1,#subclassrules do
+ setposition(f,rulesoffset+subclassrules[rule])
+ local nofbefore=readushort(f)
+ local before
+ if nofbefore>0 then
+ before={}
+ for i=1,nofbefore do
+ before[i]={ readushort(f) }
+ end
+ end
+ local nofcurrent=readushort(f)
+ local current={ { firstcoverage } }
+ for i=2,nofcurrent do
+ current[i]={ readushort(f) }
+ end
+ local nofafter=readushort(f)
+ local after
+ if nofafter>0 then
+ after={}
+ for i=1,nofafter do
+ after[i]={ readushort(f) }
+ end
+ end
+ local noflookups=readushort(f)
+ local lookups={}
+ for i=1,noflookups do
+ lookups[readushort(f)+1]=readushort(f)+1
+ end
+ rules[#rules+1]={
+ before=before,
+ current=current,
+ after=after,
+ lookups=lookups,
+ }
+ end
+ end
+ end
+ else
+ report("empty subclassset in %a subtype %i","chainedcontext",subtype)
+ end
+ return {
+ format="glyphs",
+ rules=rules,
+ }
+ elseif subtype==2 then
+ local coverage=readushort(f)
+ local beforeclassdef=readushort(f)
+ local currentclassdef=readushort(f)
+ local afterclassdef=readushort(f)
+ local subclasssets=readarray(f)
+ local rules={}
+ if subclasssets then
+ local coverage=readcoverage(f,tableoffset+coverage)
+ local beforeclassdef=readclassdef(f,tableoffset+beforeclassdef)
+ local currentclassdef=readclassdef(f,tableoffset+currentclassdef)
+ local afterclassdef=readclassdef(f,tableoffset+afterclassdef)
+ local beforeclasses=classtocoverage(beforeclassdef,fontdata.glyphs)
+ local currentclasses=classtocoverage(currentclassdef,fontdata.glyphs)
+ local afterclasses=classtocoverage(afterclassdef,fontdata.glyphs)
+ for class=1,#subclasssets do
+ local offset=subclasssets[class]
+ if offset>0 then
+ local firstcoverage=currentclasses[class]
+ if firstcoverage then
+ firstcoverage=covered(firstcoverage,coverage)
+ if firstcoverage then
+ local rulesoffset=tableoffset+offset
+ local subclassrules=readarray(f,rulesoffset)
+ for rule=1,#subclassrules do
+ setposition(f,rulesoffset+subclassrules[rule])
+ local nofbefore=readushort(f)
+ local before
+ if nofbefore>0 then
+ before={}
+ for i=1,nofbefore do
+ before[i]=beforeclasses[readushort(f)+1]
end
- unicode=-1
end
- if not unicode or unicode==-1 then
- if not name then
- name=formatters["u%06X.ctx"](private)
- end
- unicode=private
- unicodes[name]=private
- if trace_private then
- report_otf("glyph %a at index %H is moved to private unicode slot %U",name,index,private)
- end
- private=private+1
- nofnames=nofnames+1
- else
- if not name then
- name=formatters["u%06X.ctx"](unicode)
- end
- unicodes[name]=unicode
- nofunicodes=nofunicodes+1
+ local nofcurrent=readushort(f)
+ local current={ firstcoverage }
+ for i=2,nofcurrent do
+ current[i]=currentclasses[readushort(f)+1]
end
- indices[index]=unicode
- local description={
- boundingbox=glyph.boundingbox,
- name=name or "unknown",
- cidindex=cidindex,
- index=cidslot,
- glyph=glyph,
- }
- descriptions[unicode]=description
- local altuni=glyph.altuni
- if altuni then
- for i=1,#altuni do
- local a=altuni[i]
- local u=a.unicode
- if u~=unicode then
- local v=a.variant
- if v then
- local vv=variants[v]
- if vv then
- vv[u]=unicode
- else
- vv={ [u]=unicode }
- variants[v]=vv
- end
- end
- end
+ local nofafter=readushort(f)
+ local after
+ if nofafter>0 then
+ after={}
+ for i=1,nofafter do
+ after[i]=afterclasses[readushort(f)+1]
end
end
+ local noflookups=readushort(f)
+ local lookups={}
+ for i=1,noflookups do
+ lookups[readushort(f)+1]=readushort(f)+1
+ end
+ rules[#rules+1]={
+ before=before,
+ current=current,
+ after=after,
+ lookups=lookups,
+ }
end
+ else
+ report("no coverage")
end
else
- report_otf("potential problem: no glyphs found in subfont %i",cidindex)
+ report("class is not covered")
+ end
+ end
+ end
+ else
+ report("empty subclassset in %a subtype %i","chainedcontext",subtype)
+ end
+ return {
+ format="class",
+ rules=rules,
+ }
+ elseif subtype==3 then
+ local before=readarray(f)
+ local current=readarray(f)
+ local after=readarray(f)
+ local noflookups=readushort(f)
+ local lookups={}
+ for i=1,noflookups do
+ lookups[readushort(f)+1]=readushort(f)+1
+ end
+ before=readcoveragearray(f,tableoffset,before,true)
+ current=readcoveragearray(f,tableoffset,current,true)
+ after=readcoveragearray(f,tableoffset,after,true)
+ return {
+ format="coverage",
+ rules={
+ {
+ before=before,
+ current=current,
+ after=after,
+ lookups=lookups,
+ }
+ }
+ }
+ else
+ report("unsupported subtype %a in %a %s",subtype,"chainedcontext",what)
+ end
+end
+local function extension(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,types,handlers,what)
+ local tableoffset=lookupoffset+offset
+ setposition(f,tableoffset)
+ local subtype=readushort(f)
+ if subtype==1 then
+ local lookuptype=types[readushort(f)]
+ local faroffset=readulong(f)
+ local handler=handlers[lookuptype]
+ if handler then
+ return handler(f,fontdata,lookupid,tableoffset+faroffset,0,glyphs,nofglyphs),lookuptype
+ else
+ report("no handler for lookuptype %a subtype %a in %s %s",lookuptype,subtype,what,"extension")
+ end
+ else
+ report("unsupported subtype %a in %s %s",subtype,what,"extension")
+ end
+end
+function gsubhandlers.single(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
+ local tableoffset=lookupoffset+offset
+ setposition(f,tableoffset)
+ local subtype=readushort(f)
+ if subtype==1 then
+ local coverage=readushort(f)
+ local delta=readshort(f)
+ local coverage=readcoverage(f,tableoffset+coverage)
+ for index in next,coverage do
+ local newindex=index+delta
+ if index>nofglyphs or newindex>nofglyphs then
+ report("invalid index in %s format %i: %i -> %i (max %i)","single",subtype,index,newindex,nofglyphs)
+ coverage[index]=nil
+ else
+ coverage[index]=newindex
+ end
+ end
+ return {
+ coverage=coverage
+ }
+ elseif subtype==2 then
+ local coverage=readushort(f)
+ local nofreplacements=readushort(f)
+ local replacements={}
+ for i=1,nofreplacements do
+ replacements[i]=readushort(f)
+ end
+ local coverage=readcoverage(f,tableoffset+coverage)
+ for index,newindex in next,coverage do
+ newindex=newindex+1
+ if index>nofglyphs or newindex>nofglyphs then
+ report("invalid index in %s format %i: %i -> %i (max %i)","single",subtype,index,newindex,nofglyphs)
+ coverage[index]=nil
+ else
+ coverage[index]=replacements[newindex]
+ end
+ end
+ return {
+ coverage=coverage
+ }
+ else
+ report("unsupported subtype %a in %a substitution",subtype,"single")
+ end
+end
+local function sethandler(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,what)
+ local tableoffset=lookupoffset+offset
+ setposition(f,tableoffset)
+ local subtype=readushort(f)
+ if subtype==1 then
+ local coverage=readushort(f)
+ local nofsequence=readushort(f)
+ local sequences={}
+ for i=1,nofsequence do
+ sequences[i]=readushort(f)
+ end
+ for i=1,nofsequence do
+ setposition(f,tableoffset+sequences[i])
+ local n=readushort(f)
+ local s={}
+ for i=1,n do
+ s[i]=readushort(f)
+ end
+ sequences[i]=s
+ end
+ local coverage=readcoverage(f,tableoffset+coverage)
+ for index,newindex in next,coverage do
+ newindex=newindex+1
+ if index>nofglyphs or newindex>nofglyphs then
+ report("invalid index in %s format %i: %i -> %i (max %i)",what,subtype,index,newindex,nofglyphs)
+ coverage[index]=nil
+ else
+ coverage[index]=sequences[newindex]
+ end
+ end
+ return {
+ coverage=coverage
+ }
+ else
+ report("unsupported subtype %a in %a substitution",subtype,what)
+ end
+end
+function gsubhandlers.multiple(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
+ return sethandler(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,"multiple")
+end
+function gsubhandlers.alternate(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
+ return sethandler(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,"alternate")
+end
+function gsubhandlers.ligature(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
+ local tableoffset=lookupoffset+offset
+ setposition(f,tableoffset)
+ local subtype=readushort(f)
+ if subtype==1 then
+ local coverage=readushort(f)
+ local nofsets=readushort(f)
+ local ligatures={}
+ for i=1,nofsets do
+ ligatures[i]=readushort(f)
+ end
+ for i=1,nofsets do
+ local offset=lookupoffset+offset+ligatures[i]
+ setposition(f,offset)
+ local n=readushort(f)
+ local l={}
+ for i=1,n do
+ l[i]=offset+readushort(f)
+ end
+ ligatures[i]=l
+ end
+ local coverage=readcoverage(f,tableoffset+coverage)
+ for index,newindex in next,coverage do
+ local hash={}
+ local ligatures=ligatures[newindex+1]
+ for i=1,#ligatures do
+ local offset=ligatures[i]
+ setposition(f,offset)
+ local lig=readushort(f)
+ local cnt=readushort(f)
+ local hsh=hash
+ for i=2,cnt do
+ local c=readushort(f)
+ local h=hsh[c]
+ if not h then
+ h={}
+ hsh[c]=h
end
+ hsh=h
end
- if trace_subfonts then
- report_otf("nofglyphs: %i, unique: %i",cidtotal,table.count(unique))
+ hsh.ligature=lig
+ end
+ coverage[index]=hash
+ end
+ return {
+ coverage=coverage
+ }
+ else
+ report("unsupported subtype %a in %a substitution",subtype,"ligature")
+ end
+end
+function gsubhandlers.context(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
+ return unchainedcontext(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,"substitution"),"context"
+end
+function gsubhandlers.chainedcontext(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
+ return chainedcontext(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,"substitution"),"chainedcontext"
+end
+function gsubhandlers.extension(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
+ return extension(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,gsubtypes,gsubhandlers,"substitution")
+end
+function gsubhandlers.reversechainedcontextsingle(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
+ local tableoffset=lookupoffset+offset
+ setposition(f,tableoffset)
+ local subtype=readushort(f)
+ if subtype==1 then
+ local current=readfirst(f)
+ local before=readarray(f)
+ local after=readarray(f)
+ local replacements=readarray(f)
+ current=readcoveragearray(f,tableoffset,current,true)
+ before=readcoveragearray(f,tableoffset,before,true)
+ after=readcoveragearray(f,tableoffset,after,true)
+ return {
+ coverage={
+ format="reversecoverage",
+ before=before,
+ current=current,
+ after=after,
+ replacements=replacements,
+ }
+ },"reversechainedcontextsingle"
+ else
+ report("unsupported subtype %a in %a substitution",subtype,"reversechainedcontextsingle")
+ end
+end
+local function readpairsets(f,tableoffset,sets,format1,format2)
+ local done={}
+ for i=1,#sets do
+ local offset=sets[i]
+ local reused=done[offset]
+ if not reused then
+ setposition(f,tableoffset+offset)
+ local n=readushort(f)
+ reused={}
+ for i=1,n do
+ reused[i]={
+ readushort(f),
+ readposition(f,format1),
+ readposition(f,format2)
+ }
+ end
+ done[offset]=reused
+ end
+ sets[i]=reused
+ end
+ return sets
+end
+local function readpairclasssets(f,nofclasses1,nofclasses2,format1,format2)
+ local classlist1={}
+ for i=1,nofclasses1 do
+ local classlist2={}
+ classlist1[i]=classlist2
+ for j=1,nofclasses2 do
+ local one=readposition(f,format1)
+ local two=readposition(f,format2)
+ if one or two then
+ classlist2[j]={ one,two }
+ else
+ classlist2[j]=false
+ end
+ end
+ end
+ return classlist1
+end
+function gposhandlers.single(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
+ local tableoffset=lookupoffset+offset
+ setposition(f,tableoffset)
+ local subtype=readushort(f)
+ if subtype==1 then
+ local coverage=readushort(f)
+ local format=readushort(f)
+ local value=readposition(f,format)
+ local coverage=readcoverage(f,tableoffset+coverage)
+ for index,newindex in next,coverage do
+ coverage[index]=value
+ end
+ return {
+ format="pair",
+ coverage=coverage
+ }
+ elseif subtype==2 then
+ local coverage=readushort(f)
+ local format=readushort(f)
+ local values={}
+ local nofvalues=readushort(f)
+ for i=1,nofvalues do
+ values[i]=readposition(f,format)
+ end
+ local coverage=readcoverage(f,tableoffset+coverage)
+ for index,newindex in next,coverage do
+ coverage[index]=values[newindex+1]
+ end
+ return {
+ format="pair",
+ coverage=coverage
+ }
+ else
+ report("unsupported subtype %a in %a positioning",subtype,"single")
+ end
+end
+function gposhandlers.pair(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
+ local tableoffset=lookupoffset+offset
+ setposition(f,tableoffset)
+ local subtype=readushort(f)
+ if subtype==1 then
+ local coverage=readushort(f)
+ local format1=readushort(f)
+ local format2=readushort(f)
+ local sets=readarray(f)
+ sets=readpairsets(f,tableoffset,sets,format1,format2)
+ coverage=readcoverage(f,tableoffset+coverage)
+ for index,newindex in next,coverage do
+ local set=sets[newindex+1]
+ local hash={}
+ for i=1,#set do
+ local value=set[i]
+ if value then
+ local other=value[1]
+ local first=value[2]
+ local second=value[3]
+ if first or second then
+ hash[other]={ first,second }
+ else
+ hash[other]=nil
+ end
end
- if trace_loading then
- report_otf("cid font remapped, %s unicode points, %s symbolic names, %s glyphs",nofunicodes,nofnames,nofunicodes+nofnames)
+ end
+ coverage[index]=hash
+ end
+ return {
+ format="pair",
+ coverage=coverage
+ }
+ elseif subtype==2 then
+ local coverage=readushort(f)
+ local format1=readushort(f)
+ local format2=readushort(f)
+ local classdef1=readushort(f)
+ local classdef2=readushort(f)
+ local nofclasses1=readushort(f)
+ local nofclasses2=readushort(f)
+ local classlist=readpairclasssets(f,nofclasses1,nofclasses2,format1,format2)
+ coverage=readcoverage(f,tableoffset+coverage)
+ classdef1=readclassdef(f,tableoffset+classdef1)
+ classdef2=readclassdef(f,tableoffset+classdef2)
+ local usedcoverage={}
+ for g1,c1 in next,classdef1 do
+ if coverage[g1] then
+ local l1=classlist[c1]
+ if l1 then
+ local hash={}
+ for paired,class in next,classdef2 do
+ local offsets=l1[class]
+ if offsets then
+ local first=offsets[1]
+ local second=offsets[2]
+ if first or second then
+ hash[paired]={ first,second }
+ else
+ end
+ end
+ end
+ usedcoverage[g1]=hash
end
- elseif trace_loading then
- report_otf("unable to remap cid font, missing cid file for %a",filename)
end
- elseif trace_loading then
- report_otf("font %a has no glyphs",filename)
end
+ return {
+ format="pair",
+ coverage=usedcoverage
+ }
+ elseif subtype==3 then
+ report("yet unsupported subtype %a in %a positioning",subtype,"pair")
else
- local cnt=raw.glyphcnt or 0
- local min=tableversion>0.3 and raw.glyphmin or 0
- local max=tableversion>0.3 and raw.glyphmax or (raw.glyphcnt-1)
- if cnt>0 then
- for index=min,max do
- local glyph=rawglyphs[index]
- if glyph then
- local unicode=glyph.unicode
- local name=glyph.name
- if not unicode or unicode==-1 then
- unicode=private
- unicodes[name]=private
- if trace_private then
- report_otf("glyph %a at index %H is moved to private unicode slot %U",name,index,private)
+ report("unsupported subtype %a in %a positioning",subtype,"pair")
+ end
+end
+function gposhandlers.cursive(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
+ local tableoffset=lookupoffset+offset
+ setposition(f,tableoffset)
+ local subtype=readushort(f)
+ if subtype==1 then
+ local coverage=tableoffset+readushort(f)
+ local nofrecords=readushort(f)
+ local records={}
+ for i=1,nofrecords do
+ local entry=readushort(f)
+ local exit=readushort(f)
+ records[i]={
+ entry=entry~=0 and (tableoffset+entry) or false,
+ exit=exit~=0 and (tableoffset+exit ) or false,
+ }
+ end
+ coverage=readcoverage(f,coverage)
+ for i=1,nofrecords do
+ local r=records[i]
+ records[i]={
+ 1,
+ readanchor(f,r.entry) or nil,
+ readanchor(f,r.exit ) or nil,
+ }
+ end
+ for index,newindex in next,coverage do
+ coverage[index]=records[newindex+1]
+ end
+ return {
+ coverage=coverage
+ }
+ else
+ report("unsupported subtype %a in %a positioning",subtype,"cursive")
+ end
+end
+local function handlemark(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,ligature)
+ local tableoffset=lookupoffset+offset
+ setposition(f,tableoffset)
+ local subtype=readushort(f)
+ if subtype==1 then
+ local markcoverage=tableoffset+readushort(f)
+ local basecoverage=tableoffset+readushort(f)
+ local nofclasses=readushort(f)
+ local markoffset=tableoffset+readushort(f)
+ local baseoffset=tableoffset+readushort(f)
+ local markcoverage=readcoverage(f,markcoverage)
+ local basecoverage=readcoverage(f,basecoverage,true)
+ setposition(f,markoffset)
+ local markclasses={}
+ local nofmarkclasses=readushort(f)
+ local lastanchor=fontdata.lastanchor or 0
+ local usedanchors={}
+ for i=1,nofmarkclasses do
+ local class=readushort(f)+1
+ local offset=readushort(f)
+ if offset==0 then
+ markclasses[i]=false
+ else
+ markclasses[i]={ class,markoffset+offset }
+ end
+ usedanchors[class]=true
+ end
+ for i=1,nofmarkclasses do
+ local mc=markclasses[i]
+ if mc then
+ mc[2]=readanchor(f,mc[2])
+ end
+ end
+ setposition(f,baseoffset)
+ local nofbaserecords=readushort(f)
+ local baserecords={}
+ if ligature then
+ for i=1,nofbaserecords do
+ local offset=readushort(f)
+ if offset==0 then
+ baserecords[i]=false
+ else
+ baserecords[i]=baseoffset+offset
+ end
+ end
+ for i=1,nofbaserecords do
+ local recordoffset=baserecords[i]
+ if recordoffset then
+ setposition(f,recordoffset)
+ local nofcomponents=readushort(f)
+ local components={}
+ for i=1,nofcomponents do
+ local classes={}
+ for i=1,nofclasses do
+ local offset=readushort(f)
+ if offset~=0 then
+ classes[i]=recordoffset+offset
+ else
+ classes[i]=false
+ end
end
- private=private+1
- else
- if unicode>criterium then
- local taken=descriptions[unicode]
- if taken then
- if unicode>=private then
- private=unicode+1
+ components[i]=classes
+ end
+ baserecords[i]=components
+ end
+ end
+ local baseclasses={}
+ for i=1,nofclasses do
+ baseclasses[i]={}
+ end
+ for i=1,nofbaserecords do
+ local components=baserecords[i]
+ local b=basecoverage[i]
+ if components then
+ for c=1,#components do
+ local classes=components[i]
+ if classes then
+ for i=1,nofclasses do
+ local anchor=readanchor(f,classes[i])
+ local bclass=baseclasses[i]
+ local bentry=bclass[b]
+ if bentry then
+ bentry[c]=anchor
else
- private=private+1
+ bclass[b]={ [c]=anchor }
end
- descriptions[private]=taken
- unicodes[taken.name]=private
- indices[taken.index]=private
- if trace_private then
- report_otf("slot %U is moved to %U due to private in font",unicode)
+ end
+ end
+ components[i]=classes
+ end
+ end
+ end
+ for index,newindex in next,markcoverage do
+ markcoverage[index]=markclasses[newindex+1] or nil
+ end
+ return {
+ format="ligature",
+ baseclasses=baseclasses,
+ coverage=markcoverage,
+ }
+ else
+ for i=1,nofbaserecords do
+ local r={}
+ for j=1,nofclasses do
+ local offset=readushort(f)
+ if offset==0 then
+ r[j]=false
+ else
+ r[j]=baseoffset+offset
+ end
+ end
+ baserecords[i]=r
+ end
+ local baseclasses={}
+ for i=1,nofclasses do
+ baseclasses[i]={}
+ end
+ for i=1,nofbaserecords do
+ local r=baserecords[i]
+ local b=basecoverage[i]
+ for j=1,nofclasses do
+ baseclasses[j][b]=readanchor(f,r[j])
+ end
+ end
+ for index,newindex in next,markcoverage do
+ markcoverage[index]=markclasses[newindex+1] or nil
+ end
+ return {
+ format="base",
+ baseclasses=baseclasses,
+ coverage=markcoverage,
+ }
+ end
+ else
+ report("unsupported subtype %a in",subtype)
+ end
+end
+function gposhandlers.marktobase(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
+ return handlemark(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
+end
+function gposhandlers.marktoligature(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
+ return handlemark(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,true)
+end
+function gposhandlers.marktomark(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
+ return handlemark(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
+end
+function gposhandlers.context(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
+ return unchainedcontext(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,"positioning"),"context"
+end
+function gposhandlers.chainedcontext(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
+ return chainedcontext(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,"positioning"),"chainedcontext"
+end
+function gposhandlers.extension(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
+ return extension(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,gpostypes,gposhandlers,"positioning")
+end
+do
+ local plugins={}
+ function plugins.size(f,fontdata,tableoffset,parameters)
+ if not fontdata.designsize then
+ setposition(f,tableoffset+parameters)
+ local designsize=readushort(f)
+ if designsize>0 then
+ fontdata.designsize=designsize
+ skipshort(f,2)
+ fontdata.minsize=readushort(f)
+ fontdata.maxsize=readushort(f)
+ end
+ end
+ end
+ local function reorderfeatures(fontdata,scripts,features)
+ local scriptlangs={}
+ local featurehash={}
+ local featureorder={}
+ for script,languages in next,scripts do
+ for language,record in next,languages do
+ local hash={}
+ local list=record.featureindices
+ for k=1,#list do
+ local index=list[k]
+ local feature=features[index]
+ local lookups=feature.lookups
+ local tag=feature.tag
+ if tag then
+ hash[tag]=true
+ end
+ if lookups then
+ for i=1,#lookups do
+ local lookup=lookups[i]
+ local o=featureorder[lookup]
+ if o then
+ local okay=true
+ for i=1,#o do
+ if o[i]==tag then
+ okay=false
+ break
+ end
end
- else
- if unicode>=private then
- private=unicode+1
+ if okay then
+ o[#o+1]=tag
end
+ else
+ featureorder[lookup]={ tag }
end
- end
- unicodes[name]=unicode
- end
- indices[index]=unicode
- descriptions[unicode]={
- boundingbox=glyph.boundingbox,
- name=name,
- index=index,
- glyph=glyph,
- }
- local altuni=glyph.altuni
- if altuni then
- for i=1,#altuni do
- local a=altuni[i]
- local u=a.unicode
- if u~=unicode then
- local v=a.variant
- if v then
- local vv=variants[v]
- if vv then
- vv[u]=unicode
- else
- vv={ [u]=unicode }
- variants[v]=vv
+ local f=featurehash[lookup]
+ if f then
+ local h=f[tag]
+ if h then
+ local s=h[script]
+ if s then
+ s[language]=true
+ else
+ h[script]={ [language]=true }
end
+ else
+ f[tag]={ [script]={ [language]=true } }
+ end
+ else
+ featurehash[lookup]={ [tag]={ [script]={ [language]=true } } }
+ end
+ local h=scriptlangs[tag]
+ if h then
+ local s=h[script]
+ if s then
+ s[language]=true
+ else
+ h[script]={ [language]=true }
end
+ else
+ scriptlangs[tag]={ [script]={ [language]=true } }
end
end
end
- else
- report_otf("potential problem: glyph %U is used but empty",index)
end
end
- else
- report_otf("potential problem: no glyphs found")
end
+ return scriptlangs,featurehash,featureorder
end
- resources.private=private
-end
-actions["check encoding"]=function(data,filename,raw)
- local descriptions=data.descriptions
- local resources=data.resources
- local properties=data.properties
- local unicodes=resources.unicodes
- local indices=resources.indices
- local duplicates=resources.duplicates
- local mapdata=raw.map or {}
- local unicodetoindex=mapdata and mapdata.map or {}
- local indextounicode=mapdata and mapdata.backmap or {}
- local encname=lower(data.enc_name or mapdata.enc_name or "")
- local criterium=0xFFFF
- local privateoffset=constructors.privateoffset
- if find(encname,"unicode") then
- if trace_loading then
- report_otf("checking embedded unicode map %a",encname)
+ local function readscriplan(f,fontdata,scriptoffset)
+ setposition(f,scriptoffset)
+ local nofscripts=readushort(f)
+ local scripts={}
+ for i=1,nofscripts do
+ scripts[readtag(f)]=scriptoffset+readushort(f)
end
- local reported={}
- for maybeunicode,index in next,unicodetoindex do
- if descriptions[maybeunicode] then
- else
- local unicode=indices[index]
- if not unicode then
- elseif maybeunicode==unicode then
- elseif unicode>privateoffset then
- else
- local d=descriptions[unicode]
- if d then
- local c=d.copies
- if c then
- c[maybeunicode]=true
- else
- d.copies={ [maybeunicode]=true }
- end
- elseif index and not reported[index] then
- report_otf("missing index %i",index)
- reported[index]=true
- end
+ local languagesystems=setmetatableindex("table")
+ for script,offset in next,scripts do
+ setposition(f,offset)
+ local defaultoffset=readushort(f)
+ local noflanguages=readushort(f)
+ local languages={}
+ if defaultoffset>0 then
+ languages.dflt=languagesystems[offset+defaultoffset]
+ end
+ for i=1,noflanguages do
+ local language=readtag(f)
+ local offset=offset+readushort(f)
+ languages[language]=languagesystems[offset]
+ end
+ scripts[script]=languages
+ end
+ for offset,usedfeatures in next,languagesystems do
+ if offset>0 then
+ setposition(f,offset)
+ local featureindices={}
+ usedfeatures.featureindices=featureindices
+ usedfeatures.lookuporder=readushort(f)
+ usedfeatures.requiredindex=readushort(f)
+ local noffeatures=readushort(f)
+ for i=1,noffeatures do
+ featureindices[i]=readushort(f)+1
end
end
end
- for unicode,data in next,descriptions do
- local d=data.copies
- if d then
- duplicates[unicode]=sortedkeys(d)
- data.copies=nil
+ return scripts
+ end
+ local function readfeatures(f,fontdata,featureoffset)
+ setposition(f,featureoffset)
+ local features={}
+ local noffeatures=readushort(f)
+ for i=1,noffeatures do
+ features[i]={
+ tag=readtag(f),
+ offset=readushort(f)
+ }
+ end
+ for i=1,noffeatures do
+ local feature=features[i]
+ local offset=featureoffset+feature.offset
+ setposition(f,offset)
+ local parameters=readushort(f)
+ local noflookups=readushort(f)
+ if noflookups>0 then
+ local lookups={}
+ feature.lookups=lookups
+ for j=1,noflookups do
+ lookups[j]=readushort(f)+1
+ end
+ end
+ if parameters>0 then
+ feature.parameters=parameters
+ local plugin=plugins[feature.tag]
+ if plugin then
+ plugin(f,fontdata,offset,parameters)
+ end
end
end
- elseif properties.cidinfo then
- report_otf("warning: no unicode map, used cidmap %a",properties.cidinfo.usedname)
- else
- report_otf("warning: non unicode map %a, only using glyph unicode data",encname or "whatever")
+ return features
end
- if mapdata then
- mapdata.map={}
- mapdata.backmap={}
+ local function readlookups(f,lookupoffset,lookuptypes,featurehash,featureorder)
+ setposition(f,lookupoffset)
+ local lookups={}
+ local noflookups=readushort(f)
+ for i=1,noflookups do
+ lookups[i]=readushort(f)
+ end
+ for lookupid=1,noflookups do
+ local index=lookups[lookupid]
+ setposition(f,lookupoffset+index)
+ local subtables={}
+ local typebits=readushort(f)
+ local flagbits=readushort(f)
+ local lookuptype=lookuptypes[typebits]
+ local lookupflags=lookupflags[flagbits]
+ local nofsubtables=readushort(f)
+ for j=1,nofsubtables do
+ local offset=readushort(f)
+ subtables[j]=offset+index
+ end
+ local markclass=bittest(flagbits,0x0010)
+ if markclass then
+ markclass=readushort(f)
+ end
+ local markset=rshift(flagbits,8)
+ if markset>0 then
+ markclass=markset
+ end
+ lookups[lookupid]={
+ type=lookuptype,
+ flags=lookupflags,
+ name=lookupid,
+ subtables=subtables,
+ markclass=markclass,
+ features=featurehash[lookupid],
+ order=featureorder[lookupid],
+ }
+ end
+ return lookups
end
-end
-actions["add duplicates"]=function(data,filename,raw)
- local descriptions=data.descriptions
- local resources=data.resources
- local properties=data.properties
- local unicodes=resources.unicodes
- local indices=resources.indices
- local duplicates=resources.duplicates
- for unicode,d in next,duplicates do
- local nofduplicates=#d
- if nofduplicates>4 then
- if trace_loading then
- report_otf("ignoring excessive duplicates of %U (n=%s)",unicode,nofduplicates)
- end
- else
- for i=1,nofduplicates do
- local u=d[i]
- if not descriptions[u] then
- local description=descriptions[unicode]
- local n=0
- for _,description in next,descriptions do
- local kerns=description.kerns
- if kerns then
- for _,k in next,kerns do
- local ku=k[unicode]
- if ku then
- k[u]=ku
- n=n+1
+ local function readscriptoffsets(f,fontdata,tableoffset)
+ if not tableoffset then
+ return
+ end
+ setposition(f,tableoffset)
+ local version=readulong(f)
+ if version~=0x00010000 then
+ report("table version %a of %a is not supported (yet), maybe font %s is bad",version,what,fontdata.filename)
+ return
+ end
+ return tableoffset+readushort(f),tableoffset+readushort(f),tableoffset+readushort(f)
+ end
+ local f_lookupname=formatters["%s_%s_%s"]
+ local function resolvelookups(f,lookupoffset,fontdata,lookups,lookuptypes,lookuphandlers,what)
+ local sequences=fontdata.sequences or {}
+ local sublookuplist=fontdata.sublookups or {}
+ fontdata.sequences=sequences
+ fontdata.sublookups=sublookuplist
+ local nofsublookups=#sublookuplist
+ local nofsequences=#sequences
+ local lastsublookup=nofsublookups
+ local lastsequence=nofsequences
+ local lookupnames=lookupnames[what]
+ local sublookuphash={}
+ local sublookupcheck={}
+ local glyphs=fontdata.glyphs
+ local nofglyphs=fontdata.nofglyphs or #glyphs
+ local noflookups=#lookups
+ local lookupprefix=sub(what,2,2)
+ for lookupid=1,noflookups do
+ local lookup=lookups[lookupid]
+ local lookuptype=lookup.type
+ local subtables=lookup.subtables
+ local features=lookup.features
+ local handler=lookuphandlers[lookuptype]
+ if handler then
+ local nofsubtables=#subtables
+ local order=lookup.order
+ local flags=lookup.flags
+ if flags[1] then flags[1]="mark" end
+ if flags[2] then flags[2]="ligature" end
+ if flags[3] then flags[3]="base" end
+ local markclass=lookup.markclass
+ if nofsubtables>0 then
+ local steps={}
+ local nofsteps=0
+ local oldtype=nil
+ for s=1,nofsubtables do
+ local step,lt=handler(f,fontdata,lookupid,lookupoffset,subtables[s],glyphs,nofglyphs)
+ if lt then
+ lookuptype=lt
+ if oldtype and lt~=oldtype then
+ report("messy %s lookup type %a and %a",what,lookuptype,oldtype)
+ end
+ oldtype=lookuptype
+ end
+ if not step then
+ report("unsupported %s lookup type %a",what,lookuptype)
+ else
+ nofsteps=nofsteps+1
+ steps[nofsteps]=step
+ local rules=step.rules
+ if rules then
+ for i=1,#rules do
+ local rule=rules[i]
+ local before=rule.before
+ local current=rule.current
+ local after=rule.after
+ if before then
+ for i=1,#before do
+ before[i]=tohash(before[i])
+ end
+ rule.before=reversed(before)
+ end
+ if current then
+ for i=1,#current do
+ current[i]=tohash(current[i])
+ end
+ end
+ if after then
+ for i=1,#after do
+ after[i]=tohash(after[i])
+ end
+ end
end
end
end
end
- if u>0 then
- local duplicate=table.copy(description)
- duplicate.comment=formatters["copy of %U"](unicode)
- descriptions[u]=duplicate
- if trace_loading then
- report_otf("duplicating %U to %U with index %H (%s kerns)",unicode,u,description.index,n)
+ if nofsteps~=nofsubtables then
+ report("bogus subtables removed in %s lookup type %a",what,lookuptype)
+ end
+ lookuptype=lookupnames[lookuptype] or lookuptype
+ if features then
+ nofsequences=nofsequences+1
+ local l={
+ index=nofsequences,
+ name=f_lookupname(lookupprefix,"s",lookupid+lookupidoffset),
+ steps=steps,
+ nofsteps=nofsteps,
+ type=lookuptype,
+ markclass=markclass or nil,
+ flags=flags,
+ order=order,
+ features=features,
+ }
+ sequences[nofsequences]=l
+ lookup.done=l
+ else
+ nofsublookups=nofsublookups+1
+ local l={
+ index=nofsublookups,
+ name=f_lookupname(lookupprefix,"l",lookupid+lookupidoffset),
+ steps=steps,
+ nofsteps=nofsteps,
+ type=lookuptype,
+ markclass=markclass or nil,
+ flags=flags,
+ }
+ sublookuplist[nofsublookups]=l
+ sublookuphash[lookupid]=nofsublookups
+ sublookupcheck[lookupid]=0
+ lookup.done=l
+ end
+ else
+ report("no subtables for lookup %a",lookupid)
+ end
+ else
+ report("no handler for lookup %a with type %a",lookupid,lookuptype)
+ end
+ end
+ local reported={}
+ for i=lastsequence+1,nofsequences do
+ local sequence=sequences[i]
+ local steps=sequence.steps
+ for i=1,#steps do
+ local step=steps[i]
+ local rules=step.rules
+ if rules then
+ for i=1,#rules do
+ local rule=rules[i]
+ local rlookups=rule.lookups
+ if not rlookups then
+ local name=sequence.name
+ if not reported[name] then
+ report("rule %i in %s lookup %a has %s lookups",i,what,name,"no")
+ reported[name]=true
+ end
+ elseif not next(rlookups) then
+ local name=sequence.name
+ if not reported[name] then
+ report("rule %i in %s lookup %a has %s lookups",i,what,name,"empty")
+ reported[name]=true
+ end
+ rule.lookups=nil
+ else
+ for index,lookupid in sortedhash(rlookups) do
+ local h=sublookuphash[lookupid]
+ if not h then
+ nofsublookups=nofsublookups+1
+ local d=lookups[lookupid].done
+ h={
+ index=nofsublookups,
+ name=f_lookupname(lookupprefix,"d",lookupid+lookupidoffset),
+ derived=true,
+ steps=d.steps,
+ nofsteps=d.nofsteps,
+ type=d.lookuptype,
+ markclass=d.markclass or nil,
+ flags=d.flags,
+ }
+ sublookuplist[nofsublookups]=h
+ sublookuphash[lookupid]=nofsublookups
+ sublookupcheck[lookupid]=1
+ else
+ sublookupcheck[lookupid]=sublookupcheck[lookupid]+1
+ end
+ rlookups[index]=h
+ end
end
end
end
end
end
+ for i,n in sortedhash(sublookupcheck) do
+ local l=lookups[i]
+ local t=l.type
+ if n==0 and t~="extension" then
+ local d=l.done
+ report("%s lookup %s of type %a is not used",what,d and d.name or l.name,t)
+ end
+ end
end
-end
-actions["analyze glyphs"]=function(data,filename,raw)
- local descriptions=data.descriptions
- local resources=data.resources
- local metadata=data.metadata
- local properties=data.properties
- local hasitalics=false
- local widths={}
- local marks={}
- for unicode,description in next,descriptions do
- local glyph=description.glyph
- local italic=glyph.italic_correction
- if not italic then
- elseif italic==0 then
+ local function readscripts(f,fontdata,what,lookuptypes,lookuphandlers,lookupstoo)
+ local datatable=fontdata.tables[what]
+ if not datatable then
+ return
+ end
+ local tableoffset=datatable.offset
+ if not tableoffset then
+ return
+ end
+ local scriptoffset,featureoffset,lookupoffset=readscriptoffsets(f,fontdata,tableoffset)
+ if not scriptoffset then
+ return
+ end
+ local scripts=readscriplan(f,fontdata,scriptoffset)
+ local features=readfeatures(f,fontdata,featureoffset)
+ local scriptlangs,featurehash,featureorder=reorderfeatures(fontdata,scripts,features)
+ if fontdata.features then
+ fontdata.features[what]=scriptlangs
else
- description.italic=italic
- hasitalics=true
+ fontdata.features={ [what]=scriptlangs }
end
- local width=glyph.width
- widths[width]=(widths[width] or 0)+1
- local class=glyph.class
- if class then
- if class=="mark" then
- marks[unicode]=true
- end
- description.class=class
+ if not lookupstoo then
+ return
end
- end
- properties.hasitalics=hasitalics
- resources.marks=marks
- local wd,most=0,1
- for k,v in next,widths do
- if v>most then
- wd,most=k,v
+ local lookups=readlookups(f,lookupoffset,lookuptypes,featurehash,featureorder)
+ if lookups then
+ resolvelookups(f,lookupoffset,fontdata,lookups,lookuptypes,lookuphandlers,what)
end
end
- if most>1000 then
- if trace_loading then
- report_otf("most common width: %s (%s times), sharing (cjk font)",wd,most)
+ local function checkkerns(f,fontdata,specification)
+ local datatable=fontdata.tables.kern
+ if not datatable then
+ return
+ end
+ local features=fontdata.features
+ local gposfeatures=features and features.gpos
+ local name
+ if not gposfeatures or not gposfeatures.kern then
+ name="kern"
+ elseif specification.globalkerns then
+ name="globalkern"
+ else
+ report("ignoring global kern table using gpos kern feature")
+ return
end
- for unicode,description in next,descriptions do
- if description.width==wd then
+ report("adding global kern table as gpos feature %a",name)
+ setposition(f,datatable.offset)
+ local version=readushort(f)
+ local noftables=readushort(f)
+ local kerns=setmetatableindex("table")
+ for i=1,noftables do
+ local version=readushort(f)
+ local length=readushort(f)
+ local coverage=readushort(f)
+ local format=bit32.rshift(coverage,8)
+ if format==0 then
+ local nofpairs=readushort(f)
+ local searchrange=readushort(f)
+ local entryselector=readushort(f)
+ local rangeshift=readushort(f)
+ for i=1,nofpairs do
+ kerns[readushort(f)][readushort(f)]=readfword(f)
+ end
+ elseif format==2 then
else
- description.width=description.glyph.width
end
end
- resources.defaultwidth=wd
- else
- for unicode,description in next,descriptions do
- description.width=description.glyph.width
+ local feature={ dflt={ dflt=true } }
+ if not features then
+ fontdata.features={ gpos={ [name]=feature } }
+ elseif not gposfeatures then
+ fontdata.features.gpos={ [name]=feature }
+ else
+ gposfeatures[name]=feature
+ end
+ local sequences=fontdata.sequences
+ if not sequences then
+ sequences={}
+ fontdata.sequences=sequences
end
+ local nofsequences=#sequences+1
+ sequences[nofsequences]={
+ index=nofsequences,
+ name=name,
+ steps={
+ {
+ coverage=kerns,
+ format="kern",
+ },
+ },
+ nofsteps=1,
+ type="gpos_pair",
+ flags={ false,false,false,false },
+ order={ name },
+ features={ [name]=feature },
+ }
end
-end
-actions["reorganize mark classes"]=function(data,filename,raw)
- local mark_classes=raw.mark_classes
- if mark_classes then
- local resources=data.resources
- local unicodes=resources.unicodes
- local markclasses={}
- resources.markclasses=markclasses
- for name,class in next,mark_classes do
- local t={}
- for s in gmatch(class,"[^ ]+") do
- t[unicodes[s]]=true
- end
- markclasses[name]=t
- end
- end
-end
-actions["reorganize features"]=function(data,filename,raw)
- local features={}
- data.resources.features=features
- for k=1,#otf.glists do
- local what=otf.glists[k]
- local dw=raw[what]
- if dw then
- local f={}
- features[what]=f
- for i=1,#dw do
- local d=dw[i]
- local dfeatures=d.features
- if dfeatures then
- for i=1,#dfeatures do
- local df=dfeatures[i]
- local tag=strip(lower(df.tag))
- local ft=f[tag]
- if not ft then
- ft={}
- f[tag]=ft
- end
- local dscripts=df.scripts
- for i=1,#dscripts do
- local d=dscripts[i]
- local languages=d.langs
- local script=strip(lower(d.script))
- local fts=ft[script] if not fts then fts={} ft[script]=fts end
- for i=1,#languages do
- fts[strip(lower(languages[i]))]=true
+ function readers.gsub(f,fontdata,specification)
+ if specification.details then
+ readscripts(f,fontdata,"gsub",gsubtypes,gsubhandlers,specification.lookups)
+ end
+ end
+ function readers.gpos(f,fontdata,specification)
+ if specification.details then
+ readscripts(f,fontdata,"gpos",gpostypes,gposhandlers,specification.lookups)
+ if specification.lookups then
+ checkkerns(f,fontdata,specification)
+ end
+ end
+ end
+end
+function readers.gdef(f,fontdata,specification)
+ if specification.glyphs then
+ local datatable=fontdata.tables.gdef
+ if datatable then
+ local tableoffset=datatable.offset
+ setposition(f,tableoffset)
+ local version=readulong(f)
+ local classoffset=tableoffset+readushort(f)
+ local attachmentoffset=tableoffset+readushort(f)
+ local ligaturecarets=tableoffset+readushort(f)
+ local markclassoffset=tableoffset+readushort(f)
+ local marksetsoffset=version==0x00010002 and (tableoffset+readushort(f))
+ local glyphs=fontdata.glyphs
+ local marks={}
+ local markclasses=setmetatableindex("table")
+ local marksets=setmetatableindex("table")
+ fontdata.marks=marks
+ fontdata.markclasses=markclasses
+ fontdata.marksets=marksets
+ setposition(f,classoffset)
+ local classformat=readushort(f)
+ if classformat==1 then
+ local firstindex=readushort(f)
+ local lastindex=firstindex+readushort(f)-1
+ for index=firstindex,lastindex do
+ local class=classes[readushort(f)]
+ if class=="mark" then
+ marks[index]=true
+ end
+ glyphs[index].class=class
+ end
+ elseif classformat==2 then
+ local nofranges=readushort(f)
+ for i=1,nofranges do
+ local firstindex=readushort(f)
+ local lastindex=readushort(f)
+ local class=classes[readushort(f)]
+ if class then
+ for index=firstindex,lastindex do
+ glyphs[index].class=class
+ if class=="mark" then
+ marks[index]=true
end
end
end
end
end
+ setposition(f,markclassoffset)
+ local classformat=readushort(f)
+ if classformat==1 then
+ local firstindex=readushort(f)
+ local lastindex=firstindex+readushort(f)-1
+ for index=firstindex,lastindex do
+ markclasses[readushort(f)][index]=true
+ end
+ elseif classformat==2 then
+ local nofranges=readushort(f)
+ for i=1,nofranges do
+ local firstindex=readushort(f)
+ local lastindex=readushort(f)
+ local class=markclasses[readushort(f)]
+ for index=firstindex,lastindex do
+ class[index]=true
+ end
+ end
+ end
+ if marksetsoffset then
+ setposition(f,marksetsoffset)
+ local format=readushort(f)
+ if format==1 then
+ local nofsets=readushort(f)
+ local sets={}
+ for i=1,nofsets do
+ sets[i]=readulong(f)
+ end
+ for i=1,nofsets do
+ local offset=sets[i]
+ if offset~=0 then
+ marksets[i]=readcoverage(f,marksetsoffset+offset)
+ end
+ end
+ end
+ end
end
end
end
-actions["reorganize anchor classes"]=function(data,filename,raw)
- local resources=data.resources
- local anchor_to_lookup={}
- local lookup_to_anchor={}
- resources.anchor_to_lookup=anchor_to_lookup
- resources.lookup_to_anchor=lookup_to_anchor
- local classes=raw.anchor_classes
- if classes then
- for c=1,#classes do
- local class=classes[c]
- local anchor=class.name
- local lookups=class.lookup
- if type(lookups)~="table" then
- lookups={ lookups }
- end
- local a=anchor_to_lookup[anchor]
- if not a then
- a={}
- anchor_to_lookup[anchor]=a
- end
- for l=1,#lookups do
- local lookup=lookups[l]
- local l=lookup_to_anchor[lookup]
- if l then
- l[anchor]=true
+local function readmathvalue(f)
+ local v=readshort(f)
+ skipshort(f,1)
+ return v
+end
+local function readmathconstants(f,fontdata,offset)
+ setposition(f,offset)
+ fontdata.mathconstants={
+ ScriptPercentScaleDown=readshort(f),
+ ScriptScriptPercentScaleDown=readshort(f),
+ DelimitedSubFormulaMinHeight=readushort(f),
+ DisplayOperatorMinHeight=readushort(f),
+ MathLeading=readmathvalue(f),
+ AxisHeight=readmathvalue(f),
+ AccentBaseHeight=readmathvalue(f),
+ FlattenedAccentBaseHeight=readmathvalue(f),
+ SubscriptShiftDown=readmathvalue(f),
+ SubscriptTopMax=readmathvalue(f),
+ SubscriptBaselineDropMin=readmathvalue(f),
+ SuperscriptShiftUp=readmathvalue(f),
+ SuperscriptShiftUpCramped=readmathvalue(f),
+ SuperscriptBottomMin=readmathvalue(f),
+ SuperscriptBaselineDropMax=readmathvalue(f),
+ SubSuperscriptGapMin=readmathvalue(f),
+ SuperscriptBottomMaxWithSubscript=readmathvalue(f),
+ SpaceAfterScript=readmathvalue(f),
+ UpperLimitGapMin=readmathvalue(f),
+ UpperLimitBaselineRiseMin=readmathvalue(f),
+ LowerLimitGapMin=readmathvalue(f),
+ LowerLimitBaselineDropMin=readmathvalue(f),
+ StackTopShiftUp=readmathvalue(f),
+ StackTopDisplayStyleShiftUp=readmathvalue(f),
+ StackBottomShiftDown=readmathvalue(f),
+ StackBottomDisplayStyleShiftDown=readmathvalue(f),
+ StackGapMin=readmathvalue(f),
+ StackDisplayStyleGapMin=readmathvalue(f),
+ StretchStackTopShiftUp=readmathvalue(f),
+ StretchStackBottomShiftDown=readmathvalue(f),
+ StretchStackGapAboveMin=readmathvalue(f),
+ StretchStackGapBelowMin=readmathvalue(f),
+ FractionNumeratorShiftUp=readmathvalue(f),
+ FractionNumeratorDisplayStyleShiftUp=readmathvalue(f),
+ FractionDenominatorShiftDown=readmathvalue(f),
+ FractionDenominatorDisplayStyleShiftDown=readmathvalue(f),
+ FractionNumeratorGapMin=readmathvalue(f),
+ FractionNumeratorDisplayStyleGapMin=readmathvalue(f),
+ FractionRuleThickness=readmathvalue(f),
+ FractionDenominatorGapMin=readmathvalue(f),
+ FractionDenominatorDisplayStyleGapMin=readmathvalue(f),
+ SkewedFractionHorizontalGap=readmathvalue(f),
+ SkewedFractionVerticalGap=readmathvalue(f),
+ OverbarVerticalGap=readmathvalue(f),
+ OverbarRuleThickness=readmathvalue(f),
+ OverbarExtraAscender=readmathvalue(f),
+ UnderbarVerticalGap=readmathvalue(f),
+ UnderbarRuleThickness=readmathvalue(f),
+ UnderbarExtraDescender=readmathvalue(f),
+ RadicalVerticalGap=readmathvalue(f),
+ RadicalDisplayStyleVerticalGap=readmathvalue(f),
+ RadicalRuleThickness=readmathvalue(f),
+ RadicalExtraAscender=readmathvalue(f),
+ RadicalKernBeforeDegree=readmathvalue(f),
+ RadicalKernAfterDegree=readmathvalue(f),
+ RadicalDegreeBottomRaisePercent=readshort(f),
+ }
+end
+local function readmathglyphinfo(f,fontdata,offset)
+ setposition(f,offset)
+ local italics=readushort(f)
+ local accents=readushort(f)
+ local extensions=readushort(f)
+ local kerns=readushort(f)
+ local glyphs=fontdata.glyphs
+ if italics~=0 then
+ setposition(f,offset+italics)
+ local coverage=readushort(f)
+ local nofglyphs=readushort(f)
+ coverage=readcoverage(f,offset+italics+coverage,true)
+ setposition(f,offset+italics+4)
+ for i=1,nofglyphs do
+ local italic=readmathvalue(f)
+ if italic~=0 then
+ local glyph=glyphs[coverage[i]]
+ local math=glyph.math
+ if not math then
+ glyph.math={ italic=italic }
else
- l={ [anchor]=true }
- lookup_to_anchor[lookup]=l
+ math.italic=italic
end
- a[lookup]=true
end
end
+ fontdata.hasitalics=true
end
-end
-actions["prepare tounicode"]=function(data,filename,raw)
- fonts.mappings.addtounicode(data,filename)
-end
-local g_directions={
- gsub_contextchain=1,
- gpos_contextchain=1,
- gsub_reversecontextchain=-1,
- gpos_reversecontextchain=-1,
-}
-actions["reorganize subtables"]=function(data,filename,raw)
- local resources=data.resources
- local sequences={}
- local lookups={}
- local chainedfeatures={}
- resources.sequences=sequences
- resources.lookups=lookups
- for k=1,#otf.glists do
- local what=otf.glists[k]
- local dw=raw[what]
- if dw then
- for k=1,#dw do
- local gk=dw[k]
- local features=gk.features
- local typ=gk.type
- local chain=g_directions[typ] or 0
- local subtables=gk.subtables
- if subtables then
- local t={}
- for s=1,#subtables do
- t[s]=subtables[s].name
- end
- subtables=t
+ if accents~=0 then
+ setposition(f,offset+accents)
+ local coverage=readushort(f)
+ local nofglyphs=readushort(f)
+ coverage=readcoverage(f,offset+accents+coverage,true)
+ setposition(f,offset+accents+4)
+ for i=1,nofglyphs do
+ local accent=readmathvalue(f)
+ if accent~=0 then
+ local glyph=glyphs[coverage[i]]
+ local math=glyph.math
+ if not math then
+ glyph.math={ accent=accent }
+ else
+ math.accent=accent
+ end
+ end
+ end
+ end
+ if extensions~=0 then
+ setposition(f,offset+extensions)
+ end
+ if kerns~=0 then
+ local kernoffset=offset+kerns
+ setposition(f,kernoffset)
+ local coverage=readushort(f)
+ local nofglyphs=readushort(f)
+ if nofglyphs>0 then
+ local function get(offset)
+ setposition(f,kernoffset+offset)
+ local n=readushort(f)
+ if n>0 then
+ local l={}
+ for i=1,n do
+ l[i]={ height=readmathvalue(f) }
end
- local flags,markclass=gk.flags,nil
- if flags then
- local t={
- (flags.ignorecombiningmarks and "mark") or false,
- (flags.ignoreligatures and "ligature") or false,
- (flags.ignorebaseglyphs and "base") or false,
- flags.r2l or false,
- }
- markclass=flags.mark_class
- if markclass then
- markclass=resources.markclasses[markclass]
+ for i=1,n do
+ l[i].kern=readmathvalue(f)
+ end
+ l[n+1]={ kern=readmathvalue(f) }
+ return l
+ end
+ end
+ local kernsets={}
+ for i=1,nofglyphs do
+ local topright=readushort(f)
+ local topleft=readushort(f)
+ local bottomright=readushort(f)
+ local bottomleft=readushort(f)
+ kernsets[i]={
+ topright=topright~=0 and topright or nil,
+ topleft=topleft~=0 and topleft or nil,
+ bottomright=bottomright~=0 and bottomright or nil,
+ bottomleft=bottomleft~=0 and bottomleft or nil,
+ }
+ end
+ coverage=readcoverage(f,kernoffset+coverage,true)
+ for i=1,nofglyphs do
+ local kernset=kernsets[i]
+ if next(kernset) then
+ local k=kernset.topright if k then kernset.topright=get(k) end
+ local k=kernset.topleft if k then kernset.topleft=get(k) end
+ local k=kernset.bottomright if k then kernset.bottomright=get(k) end
+ local k=kernset.bottomleft if k then kernset.bottomleft=get(k) end
+ if next(kernset) then
+ local glyph=glyphs[coverage[i]]
+ local math=glyph.math
+ if not math then
+ glyph.math={ kerns=kernset }
+ else
+ math.kerns=kernset
end
- flags=t
- end
- local name=gk.name
- if not name then
- report_otf("skipping weird lookup number %s",k)
- elseif features then
- local f={}
- local o={}
- for i=1,#features do
- local df=features[i]
- local tag=strip(lower(df.tag))
- local ft=f[tag]
- if not ft then
- ft={}
- f[tag]=ft
- o[#o+1]=tag
+ end
+ end
+ end
+ end
+ end
+end
+local function readmathvariants(f,fontdata,offset)
+ setposition(f,offset)
+ local glyphs=fontdata.glyphs
+ local minoverlap=readushort(f)
+ local vcoverage=readushort(f)
+ local hcoverage=readushort(f)
+ local vnofglyphs=readushort(f)
+ local hnofglyphs=readushort(f)
+ local vconstruction={}
+ local hconstruction={}
+ for i=1,vnofglyphs do
+ vconstruction[i]=readushort(f)
+ end
+ for i=1,hnofglyphs do
+ hconstruction[i]=readushort(f)
+ end
+ fontdata.mathconstants.MinConnectorOverlap=minoverlap
+ local function get(offset,coverage,nofglyphs,construction,kvariants,kparts,kitalic)
+ if coverage~=0 and nofglyphs>0 then
+ local coverage=readcoverage(f,offset+coverage,true)
+ for i=1,nofglyphs do
+ local c=construction[i]
+ if c~=0 then
+ local index=coverage[i]
+ local glyph=glyphs[index]
+ local math=glyph.math
+ setposition(f,offset+c)
+ local assembly=readushort(f)
+ local nofvariants=readushort(f)
+ if nofvariants>0 then
+ local variants,v=nil,0
+ for i=1,nofvariants do
+ local variant=readushort(f)
+ if variant==index then
+ elseif variants then
+ v=v+1
+ variants[v]=variant
+ else
+ v=1
+ variants={ variant }
end
- local dscripts=df.scripts
- for i=1,#dscripts do
- local d=dscripts[i]
- local languages=d.langs
- local script=strip(lower(d.script))
- local fts=ft[script] if not fts then fts={} ft[script]=fts end
- for i=1,#languages do
- fts[strip(lower(languages[i]))]=true
- end
+ skipshort(f)
+ end
+ if not variants then
+ elseif not math then
+ math={ [kvariants]=variants }
+ glyph.math=math
+ else
+ math[kvariants]=variants
+ end
+ end
+ if assembly~=0 then
+ setposition(f,offset+c+assembly)
+ local italic=readmathvalue(f)
+ local nofparts=readushort(f)
+ local parts={}
+ for i=1,nofparts do
+ local p={
+ glyph=readushort(f),
+ start=readushort(f),
+ ["end"]=readushort(f),
+ advance=readushort(f),
+ }
+ local flags=readushort(f)
+ if bittest(flags,0x0001) then
+ p.extender=1
end
+ parts[i]=p
+ end
+ if not math then
+ math={
+ [kparts]=parts
+ }
+ glyph.math=math
+ else
+ math[kparts]=parts
+ end
+ if italic and italic~=0 then
+ math[kitalic]=italic
end
- sequences[#sequences+1]={
- type=typ,
- chain=chain,
- flags=flags,
- name=name,
- subtables=subtables,
- markclass=markclass,
- features=f,
- order=o,
- }
- else
- lookups[name]={
- type=typ,
- chain=chain,
- flags=flags,
- subtables=subtables,
- markclass=markclass,
- }
end
+ end
end
end
end
+ get(offset,vcoverage,vnofglyphs,vconstruction,"vvariants","vparts","vitalic")
+ get(offset,hcoverage,hnofglyphs,hconstruction,"hvariants","hparts","hitalic")
end
-actions["prepare lookups"]=function(data,filename,raw)
- local lookups=raw.lookups
- if lookups then
- data.lookups=lookups
+function readers.math(f,fontdata,specification)
+ if specification.glyphs then
+ local datatable=fontdata.tables.math
+ if datatable then
+ local tableoffset=datatable.offset
+ setposition(f,tableoffset)
+ local version=readulong(f)
+ if version~=0x00010000 then
+ report("table version %a of %a is not supported (yet), maybe font %s is bad",version,what,fontdata.filename)
+ return
+ end
+ local constants=readushort(f)
+ local glyphinfo=readushort(f)
+ local variants=readushort(f)
+ if constants==0 then
+ report("the math table of %a has no constants",fontdata.filename)
+ else
+ readmathconstants(f,fontdata,tableoffset+constants)
+ end
+ if glyphinfo~=0 then
+ readmathglyphinfo(f,fontdata,tableoffset+glyphinfo)
+ end
+ if variants~=0 then
+ readmathvariants(f,fontdata,tableoffset+variants)
+ end
+ end
end
end
-local function t_uncover(splitter,cache,covers)
- local result={}
- for n=1,#covers do
- local cover=covers[n]
- local uncovered=cache[cover]
- if not uncovered then
- uncovered=lpegmatch(splitter,cover)
- cache[cover]=uncovered
+
+end -- closure
+
+do -- begin closure to overcome local limits and interference
+
+if not modules then modules={} end modules ['font-oup']={
+ version=1.001,
+ comment="companion to font-ini.mkiv",
+ author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright="PRAGMA ADE / ConTeXt Development Team",
+ license="see context related readme files"
+}
+local next,type=next,type
+local P,R,S=lpeg.P,lpeg.R,lpeg.S
+local lpegmatch=lpeg.match
+local insert,remove,copy=table.insert,table.remove,table.copy
+local formatters=string.formatters
+local sortedkeys=table.sortedkeys
+local sortedhash=table.sortedhash
+local tohash=table.tohash
+local report=logs.reporter("otf reader")
+local trace_markwidth=false trackers.register("otf.markwidth",function(v) trace_markwidth=v end)
+local readers=fonts.handlers.otf.readers
+local privateoffset=fonts.constructors and fonts.constructors.privateoffset or 0xF0000
+local f_private=formatters["P%05X"]
+local f_unicode=formatters["U%05X"]
+local f_index=formatters["I%05X"]
+local f_character_y=formatters["%C"]
+local f_character_n=formatters["[ %C ]"]
+local doduplicates=true
+local function replaced(list,index,replacement)
+ if type(list)=="number" then
+ return replacement
+ elseif type(replacement)=="table" then
+ local t={}
+ local n=index-1
+ for i=1,n do
+ t[i]=list[i]
+ end
+ for i=1,#replacement do
+ n=n+1
+ t[n]=replacement[i]
end
- result[n]=uncovered
+ for i=index+1,#list do
+ n=n+1
+ t[n]=list[i]
+ end
+ else
+ list[index]=replacement
+ return list
end
- return result
end
-local function s_uncover(splitter,cache,cover)
- if cover=="" then
- return nil
- else
- local uncovered=cache[cover]
- if not uncovered then
- uncovered=lpegmatch(splitter,cover)
- cache[cover]=uncovered
+local function unifyresources(fontdata,indices)
+ local descriptions=fontdata.descriptions
+ local resources=fontdata.resources
+ if not descriptions or not resources then
+ return
+ end
+ local variants=fontdata.resources.variants
+ if variants then
+ for selector,unicodes in next,variants do
+ for unicode,index in next,unicodes do
+ unicodes[unicode]=indices[index]
+ end
end
- return { uncovered }
end
-end
-local function t_hashed(t,cache)
- if t then
- local ht={}
- for i=1,#t do
- local ti=t[i]
- local tih=cache[ti]
- if not tih then
- local tn=#ti
- if tn==1 then
- tih={ [ti[1]]=true }
+ local function remark(marks)
+ if marks then
+ local newmarks={}
+ for k,v in next,marks do
+ local u=indices[k]
+ if u then
+ newmarks[u]=v
else
- tih={}
- for i=1,tn do
- tih[ti[i]]=true
- end
+ report("discarding mark %i",k)
end
- cache[ti]=tih
end
- ht[i]=tih
+ return newmarks
end
- return ht
- else
- return nil
end
-end
-local function s_hashed(t,cache)
- if t then
- local tf=t[1]
- local nf=#tf
- if nf==1 then
- return { [tf[1]]=true }
- else
- local ht={}
- for i=1,nf do
- ht[i]={ [tf[i]]=true }
+ local marks=resources.marks
+ if marks then
+ resources.marks=remark(marks)
+ end
+ local markclasses=resources.markclasses
+ if markclasses then
+ for class,marks in next,markclasses do
+ markclasses[class]=remark(marks)
+ end
+ end
+ local marksets=resources.marksets
+ if marksets then
+ for class,marks in next,marksets do
+ marksets[class]=remark(marks)
+ end
+ end
+ local done={}
+ local duplicates=doduplicates and resources.duplicates
+ if duplicates and not next(duplicates) then
+ duplicates=false
+ end
+ local function recover(cover)
+ for i=1,#cover do
+ local c=cover[i]
+ if not done[c] then
+ local t={}
+ for k,v in next,c do
+ t[indices[k]]=v
+ end
+ cover[i]=t
+ done[c]=d
end
- return ht
end
- else
- return nil
end
-end
-local function r_uncover(splitter,cache,cover,replacements)
- if cover=="" then
- return nil
- else
- local uncovered=cover[1]
- local replaced=cache[replacements]
- if not replaced then
- replaced=lpegmatch(splitter,replacements)
- cache[replacements]=replaced
- end
- local nu,nr=#uncovered,#replaced
- local r={}
- if nu==nr then
- for i=1,nu do
- r[uncovered[i]]=replaced[i]
- end
- end
- return r
- end
-end
-actions["reorganize lookups"]=function(data,filename,raw)
- if data.lookups then
- local helpers=data.helpers
- local duplicates=data.resources.duplicates
- local splitter=helpers.tounicodetable
- local t_u_cache={}
- local s_u_cache=t_u_cache
- local t_h_cache={}
- local s_h_cache=t_h_cache
- local r_u_cache={}
- helpers.matchcache=t_h_cache
- for _,lookup in next,data.lookups do
- local rules=lookup.rules
- if rules then
- local format=lookup.format
- if format=="class" then
- local before_class=lookup.before_class
- if before_class then
- before_class=t_uncover(splitter,t_u_cache,reversed(before_class))
- end
- local current_class=lookup.current_class
- if current_class then
- current_class=t_uncover(splitter,t_u_cache,current_class)
- end
- local after_class=lookup.after_class
- if after_class then
- after_class=t_uncover(splitter,t_u_cache,after_class)
- end
- for i=1,#rules do
- local rule=rules[i]
- local class=rule.class
- local before=class.before
- if before then
- for i=1,#before do
- before[i]=before_class[before[i]] or {}
+ local function recursed(c)
+ local t={}
+ for g,d in next,c do
+ if type(d)=="table" then
+ t[indices[g]]=recursed(d)
+ else
+ t[g]=indices[d]
+ end
+ end
+ return t
+ end
+ local function unifythem(sequences)
+ if not sequences then
+ return
+ end
+ for i=1,#sequences do
+ local sequence=sequences[i]
+ local kind=sequence.type
+ local steps=sequence.steps
+ local features=sequence.features
+ if steps then
+ for i=1,#steps do
+ local step=steps[i]
+ if kind=="gsub_single" then
+ local c=step.coverage
+ if c then
+ local t1=done[c]
+ if not t1 then
+ t1={}
+ if duplicates then
+ for g1,d1 in next,c do
+ local ug1=indices[g1]
+ local ud1=indices[d1]
+ t1[ug1]=ud1
+ local dg1=duplicates[ug1]
+ if dg1 then
+ for u in next,dg1 do
+ t1[u]=ud1
+ end
+ end
+ end
+ else
+ for g1,d1 in next,c do
+ t1[indices[g1]]=indices[d1]
+ end
+ end
+ done[c]=t1
end
- rule.before=t_hashed(before,t_h_cache)
+ step.coverage=t1
end
- local current=class.current
- local lookups=rule.lookups
- if current then
- for i=1,#current do
- current[i]=current_class[current[i]] or {}
- if lookups and not lookups[i] then
- lookups[i]=""
+ elseif kind=="gpos_pair" then
+ local c=step.coverage
+ if c then
+ local t1=done[c]
+ if not t1 then
+ t1={}
+ for g1,d1 in next,c do
+ local t2=done[d1]
+ if not t2 then
+ t2={}
+ for g2,d2 in next,d1 do
+ t2[indices[g2]]=d2
+ end
+ done[d1]=t2
+ end
+ t1[indices[g1]]=t2
end
+ done[c]=t1
end
- rule.current=t_hashed(current,t_h_cache)
+ step.coverage=t1
end
- local after=class.after
- if after then
- for i=1,#after do
- after[i]=after_class[after[i]] or {}
- end
- rule.after=t_hashed(after,t_h_cache)
+ elseif kind=="gsub_ligature" then
+ local c=step.coverage
+ if c then
+ step.coverage=recursed(c)
end
- rule.class=nil
- end
- lookup.before_class=nil
- lookup.current_class=nil
- lookup.after_class=nil
- lookup.format="coverage"
- elseif format=="coverage" then
- for i=1,#rules do
- local rule=rules[i]
- local coverage=rule.coverage
- if coverage then
- local before=coverage.before
- if before then
- before=t_uncover(splitter,t_u_cache,reversed(before))
- rule.before=t_hashed(before,t_h_cache)
- end
- local current=coverage.current
- if current then
- current=t_uncover(splitter,t_u_cache,current)
- local lookups=rule.lookups
- if lookups then
- for i=1,#current do
- if not lookups[i] then
- lookups[i]=""
+ elseif kind=="gsub_alternate" or kind=="gsub_multiple" then
+ local c=step.coverage
+ if c then
+ local t1=done[c]
+ if not t1 then
+ t1={}
+ if duplicates then
+ for g1,d1 in next,c do
+ for i=1,#d1 do
+ d1[i]=indices[d1[i]]
+ end
+ local ug1=indices[g1]
+ t1[ug1]=d1
+ local dg1=duplicates[ug1]
+ if dg1 then
+ for u in next,dg1 do
+ t1[u]=copy(d1)
+ end
end
end
+ else
+ for g1,d1 in next,c do
+ for i=1,#d1 do
+ d1[i]=indices[d1[i]]
+ end
+ t1[indices[g1]]=d1
+ end
end
- rule.current=t_hashed(current,t_h_cache)
+ done[c]=t1
end
- local after=coverage.after
- if after then
- after=t_uncover(splitter,t_u_cache,after)
- rule.after=t_hashed(after,t_h_cache)
- end
- rule.coverage=nil
+ step.coverage=t1
end
- end
- elseif format=="reversecoverage" then
- for i=1,#rules do
- local rule=rules[i]
- local reversecoverage=rule.reversecoverage
- if reversecoverage then
- local before=reversecoverage.before
- if before then
- before=t_uncover(splitter,t_u_cache,reversed(before))
- rule.before=t_hashed(before,t_h_cache)
+ elseif kind=="gpos_mark2base" or kind=="gpos_mark2mark" or kind=="gpos_mark2ligature" then
+ local c=step.coverage
+ if c then
+ local t1=done[c]
+ if not t1 then
+ t1={}
+ for g1,d1 in next,c do
+ t1[indices[g1]]=d1
+ end
+ done[c]=t1
end
- local current=reversecoverage.current
- if current then
- current=t_uncover(splitter,t_u_cache,current)
- rule.current=t_hashed(current,t_h_cache)
+ step.coverage=t1
+ end
+ local c=step.baseclasses
+ if c then
+ local t1=done[c]
+ if not t1 then
+ for g1,d1 in next,c do
+ local t2=done[d1]
+ if not t2 then
+ t2={}
+ for g2,d2 in next,d1 do
+ t2[indices[g2]]=d2
+ end
+ done[d1]=t2
+ end
+ c[g1]=t2
+ end
+ done[c]=c
end
- local after=reversecoverage.after
- if after then
- after=t_uncover(splitter,t_u_cache,after)
- rule.after=t_hashed(after,t_h_cache)
+ end
+ elseif kind=="gpos_single" then
+ local c=step.coverage
+ if c then
+ local t1=done[c]
+ if not t1 then
+ t1={}
+ if duplicates then
+ for g1,d1 in next,c do
+ local ug1=indices[g1]
+ t1[ug1]=d1
+ local dg1=duplicates[ug1]
+ if dg1 then
+ for u in next,dg1 do
+ t1[u]=d1
+ end
+ end
+ end
+ else
+ for g1,d1 in next,c do
+ t1[indices[g1]]=d1
+ end
+ end
+ done[c]=t1
end
- local replacements=reversecoverage.replacements
- if replacements then
- rule.replacements=r_uncover(splitter,r_u_cache,current,replacements)
+ step.coverage=t1
+ end
+ elseif kind=="gpos_cursive" then
+ local c=step.coverage
+ if c then
+ local t1=done[c]
+ if not t1 then
+ t1={}
+ if duplicates then
+ for g1,d1 in next,c do
+ local ug1=indices[g1]
+ t1[ug1]=d1
+ local dg1=duplicates[ug1]
+ if dg1 then
+ for u in next,dg1 do
+ t1[u]=copy(d1)
+ end
+ end
+ end
+ else
+ for g1,d1 in next,c do
+ t1[indices[g1]]=d1
+ end
+ end
+ done[c]=t1
end
- rule.reversecoverage=nil
+ step.coverage=t1
end
end
- elseif format=="glyphs" then
- for i=1,#rules do
- local rule=rules[i]
- local glyphs=rule.glyphs
- if glyphs then
- local fore=glyphs.fore
- if fore and fore~="" then
- fore=s_uncover(splitter,s_u_cache,fore)
- rule.after=s_hashed(fore,s_h_cache)
- end
- local back=glyphs.back
- if back then
- back=s_uncover(splitter,s_u_cache,back)
- rule.before=s_hashed(back,s_h_cache)
- end
- local names=glyphs.names
- if names then
- names=s_uncover(splitter,s_u_cache,names)
- rule.current=s_hashed(names,s_h_cache)
- end
- rule.glyphs=nil
- local lookups=rule.lookups
- if lookups then
- for i=1,#names do
- if not lookups[i] then
- lookups[i]=""
+ local rules=step.rules
+ if rules then
+ for i=1,#rules do
+ local rule=rules[i]
+ local before=rule.before if before then recover(before) end
+ local after=rule.after if after then recover(after) end
+ local current=rule.current if current then recover(current) end
+ local replacements=rule.replacements
+ if replacements then
+ if not done[replacements] then
+ local r={}
+ for k,v in next,replacements do
+ r[indices[k]]=indices[v]
end
+ rule.replacements=r
+ done[replacements]=r
end
end
end
@@ -8572,644 +13704,1788 @@ actions["reorganize lookups"]=function(data,filename,raw)
end
end
end
+ unifythem(resources.sequences)
+ unifythem(resources.sublookups)
end
-actions["expand lookups"]=function(data,filename,raw)
- if data.lookups then
- local cache=data.helpers.matchcache
- if cache then
- local duplicates=data.resources.duplicates
- for key,hash in next,cache do
- local done=nil
- for key in next,hash do
- local unicode=duplicates[key]
- if not unicode then
- elseif type(unicode)=="table" then
- for i=1,#unicode do
- local u=unicode[i]
- if hash[u] then
- elseif done then
- done[u]=key
- else
- done={ [u]=key }
- end
- end
- else
- if hash[unicode] then
- elseif done then
- done[unicode]=key
+local function copyduplicates(fontdata)
+ if doduplicates then
+ local descriptions=fontdata.descriptions
+ local resources=fontdata.resources
+ local duplicates=resources.duplicates
+ if duplicates then
+ for u,d in next,duplicates do
+ local du=descriptions[u]
+ if du then
+ local t={ f_character_y(u),"@",f_index(du.index),"->" }
+ for u in next,d do
+ if descriptions[u] then
+ t[#t+1]=f_character_n(u)
else
- done={ [unicode]=key }
+ local c=copy(du)
+ descriptions[u]=c
+ t[#t+1]=f_character_y(u)
end
end
+ report("duplicates: % t",t)
+ else
end
- if done then
- for u in next,done do
- hash[u]=true
+ end
+ end
+ end
+end
+local ignore={
+ ["notdef"]=true,
+ [".notdef"]=true,
+ ["null"]=true,
+ [".null"]=true,
+ ["nonmarkingreturn"]=true,
+}
+local function checklookups(fontdata,missing,nofmissing)
+ local descriptions=fontdata.descriptions
+ local resources=fontdata.resources
+ if missing and nofmissing and nofmissing<=0 then
+ return
+ end
+ local singles={}
+ local alternates={}
+ local ligatures={}
+ if not missing then
+ missing={}
+ nofmissing=0
+ for u,d in next,descriptions do
+ if not d.unicode then
+ nofmissing=nofmissing+1
+ missing[u]=true
+ end
+ end
+ end
+ local function collectthem(sequences)
+ if not sequences then
+ return
+ end
+ for i=1,#sequences do
+ local sequence=sequences[i]
+ local kind=sequence.type
+ local steps=sequence.steps
+ if steps then
+ for i=1,#steps do
+ local step=steps[i]
+ if kind=="gsub_single" then
+ local c=step.coverage
+ if c then
+ singles[#singles+1]=c
+ end
+ elseif kind=="gsub_alternate" then
+ local c=step.coverage
+ if c then
+ alternates[#alternates+1]=c
+ end
+ elseif kind=="gsub_ligature" then
+ local c=step.coverage
+ if c then
+ ligatures[#ligatures+1]=c
+ end
end
end
end
end
end
-end
-local function check_variants(unicode,the_variants,splitter,unicodes)
- local variants=the_variants.variants
- if variants then
- local glyphs=lpegmatch(splitter,variants)
- local done={ [unicode]=true }
- local n=0
- for i=1,#glyphs do
- local g=glyphs[i]
- if done[g] then
- if i>1 then
- report_otf("skipping cyclic reference %U in math variant %U",g,unicode)
+ collectthem(resources.sequences)
+ collectthem(resources.sublookups)
+ local loops=0
+ while true do
+ loops=loops+1
+ local old=nofmissing
+ for i=1,#singles do
+ local c=singles[i]
+ for g1,g2 in next,c do
+ if missing[g1] then
+ local u2=descriptions[g2].unicode
+ if u2 then
+ missing[g1]=false
+ descriptions[g1].unicode=u2
+ nofmissing=nofmissing-1
+ end
end
- else
- if n==0 then
- n=1
- variants={ g }
- else
+ if missing[g2] then
+ local u1=descriptions[g1].unicode
+ if u1 then
+ missing[g2]=false
+ descriptions[g2].unicode=u1
+ nofmissing=nofmissing-1
+ end
+ end
+ end
+ end
+ for i=1,#alternates do
+ local c=alternates[i]
+ for g1,d1 in next,c do
+ if missing[g1] then
+ for i=1,#d1 do
+ local g2=d1[i]
+ local u2=descriptions[g2].unicode
+ if u2 then
+ missing[g1]=false
+ descriptions[g1].unicode=u2
+ nofmissing=nofmissing-1
+ end
+ end
+ end
+ if not missing[g1] then
+ for i=1,#d1 do
+ local g2=d1[i]
+ if missing[g2] then
+ local u1=descriptions[g1].unicode
+ if u1 then
+ missing[g2]=false
+ descriptions[g2].unicode=u1
+ nofmissing=nofmissing-1
+ end
+ end
+ end
+ end
+ end
+ end
+ if nofmissing<=0 then
+ report("all done in %s loops",loops)
+ return
+ elseif old==nofmissing then
+ break
+ end
+ end
+ local t,n
+ local function recursed(c)
+ for g,d in next,c do
+ if g~="ligature" then
+ local u=descriptions[g].unicode
+ if u then
n=n+1
- variants[n]=g
+ t[n]=u
+ recursed(d)
+ n=n-1
end
- done[g]=true
+ elseif missing[d] then
+ local l={}
+ local m=0
+ for i=1,n do
+ local u=t[i]
+ if type(u)=="table" then
+ for i=1,#u do
+ m=m+1
+ l[m]=u[i]
+ end
+ else
+ m=m+1
+ l[m]=u
+ end
+ end
+ missing[d]=false
+ descriptions[d].unicode=l
+ nofmissing=nofmissing-1
end
end
- if n==0 then
- variants=nil
+ end
+ if nofmissing>0 then
+ t={}
+ n=0
+ local loops=0
+ while true do
+ loops=loops+1
+ local old=nofmissing
+ for i=1,#ligatures do
+ recursed(ligatures[i])
+ end
+ if nofmissing<=0 then
+ report("all done in %s loops",loops)
+ return
+ elseif old==nofmissing then
+ break
+ end
end
+ t=nil
+ n=0
end
- local parts=the_variants.parts
- if parts then
- local p=#parts
- if p>0 then
- for i=1,p do
- local pi=parts[i]
- pi.glyph=unicodes[pi.component] or 0
- pi.component=nil
+ if nofmissing>0 then
+ local done={}
+ for i,r in next,missing do
+ if r then
+ local name=descriptions[i].name or f_index(i)
+ if not ignore[name] then
+ done[#done+1]=name
+ end
end
+ end
+ if #done>0 then
+ table.sort(done)
+ report("not unicoded: % t",done)
+ end
+ end
+end
+local function unifymissing(fontdata)
+ if not fonts.mappings then
+ require("font-map")
+ require("font-agl")
+ end
+ local unicodes={}
+ local private=fontdata.private
+ local resources=fontdata.resources
+ resources.unicodes=unicodes
+ for unicode,d in next,fontdata.descriptions do
+ if unicode<privateoffset then
+ local name=d.name
+ if name then
+ unicodes[name]=unicode
+ end
+ end
+ end
+ fonts.mappings.addtounicode(fontdata,fontdata.filename,checklookups)
+ resources.unicodes=nil
+end
+local function unifyglyphs(fontdata,usenames)
+ local private=fontdata.private or privateoffset
+ local glyphs=fontdata.glyphs
+ local indices={}
+ local descriptions={}
+ local names=usenames and {}
+ local resources=fontdata.resources
+ local zero=glyphs[0]
+ local zerocode=zero.unicode
+ if not zerocode then
+ zerocode=private
+ zero.unicode=zerocode
+ private=private+1
+ end
+ descriptions[zerocode]=zero
+ if names then
+ local name=glyphs[0].name or f_private(zerocode)
+ indices[0]=name
+ names[name]=zerocode
+ else
+ indices[0]=zerocode
+ end
+ for index=1,#glyphs do
+ local glyph=glyphs[index]
+ local unicode=glyph.unicode
+ if not unicode then
+ unicode=private
+ if names then
+ local name=glyph.name or f_private(unicode)
+ indices[index]=name
+ names[name]=unicode
+ else
+ indices[index]=unicode
+ end
+ private=private+1
+ elseif descriptions[unicode] then
+ report("assigning private unicode %U to glyph indexed %05X (%C)",private,index,unicode)
+ unicode=private
+ if names then
+ local name=glyph.name or f_private(unicode)
+ indices[index]=name
+ names[name]=unicode
+ else
+ indices[index]=unicode
+ end
+ private=private+1
else
- parts=nil
+ if names then
+ local name=glyph.name or f_unicode(unicode)
+ indices[index]=name
+ names[name]=unicode
+ else
+ indices[index]=unicode
+ end
+ end
+ descriptions[unicode]=glyph
+ end
+ for index=1,#glyphs do
+ local math=glyphs[index].math
+ if math then
+ local list=math.vparts
+ if list then
+ for i=1,#list do local l=list[i] l.glyph=indices[l.glyph] end
+ end
+ local list=math.hparts
+ if list then
+ for i=1,#list do local l=list[i] l.glyph=indices[l.glyph] end
+ end
+ local list=math.vvariants
+ if list then
+ for i=1,#list do list[i]=indices[list[i]] end
+ end
+ local list=math.hvariants
+ if list then
+ for i=1,#list do list[i]=indices[list[i]] end
+ end
end
end
- local italic=the_variants.italic
- if italic and italic==0 then
- italic=nil
+ fontdata.private=private
+ fontdata.glyphs=nil
+ fontdata.names=names
+ fontdata.descriptions=descriptions
+ fontdata.hashmethod=hashmethod
+ return indices,names
+end
+local p_bogusname=(
+ (P("uni")+P("UNI")+P("Uni")+P("U")+P("u"))*S("Xx")^0*R("09","AF")^1+(P("identity")+P("Identity")+P("IDENTITY"))*R("09","AF")^1+(P("index")+P("Index")+P("INDEX"))*R("09")^1
+)*P(-1)
+local function stripredundant(fontdata)
+ local descriptions=fontdata.descriptions
+ if descriptions then
+ local n=0
+ local c=0
+ for unicode,d in next,descriptions do
+ local name=d.name
+ if name and lpegmatch(p_bogusname,name) then
+ d.name=nil
+ n=n+1
+ end
+ if d.class=="base" then
+ d.class=nil
+ c=c+1
+ end
+ end
+ if n>0 then
+ report("%s bogus names removed (verbose unicode)",n)
+ end
+ if c>0 then
+ report("%s base class tags removed (default is base)",c)
+ end
end
- return variants,parts,italic
end
-actions["analyze math"]=function(data,filename,raw)
- if raw.math then
- data.metadata.math=raw.math
- local unicodes=data.resources.unicodes
- local splitter=data.helpers.tounicodetable
- for unicode,description in next,data.descriptions do
- local glyph=description.glyph
- local mathkerns=glyph.mathkern
- local hvariants=glyph.horiz_variants
- local vvariants=glyph.vert_variants
- local accent=glyph.top_accent
- local italic=glyph.italic_correction
- if mathkerns or hvariants or vvariants or accent or italic then
- local math={}
- if accent then
- math.accent=accent
+function readers.rehash(fontdata,hashmethod)
+ if not (fontdata and fontdata.glyphs) then
+ return
+ end
+ if hashmethod=="indices" then
+ fontdata.hashmethod="indices"
+ elseif hashmethod=="names" then
+ fontdata.hashmethod="names"
+ local indices=unifyglyphs(fontdata,true)
+ unifyresources(fontdata,indices)
+ copyduplicates(fontdata)
+ unifymissing(fontdata)
+ else
+ fontdata.hashmethod="unicode"
+ local indices=unifyglyphs(fontdata)
+ unifyresources(fontdata,indices)
+ copyduplicates(fontdata)
+ unifymissing(fontdata)
+ stripredundant(fontdata)
+ end
+end
+function readers.checkhash(fontdata)
+ local hashmethod=fontdata.hashmethod
+ if hashmethod=="unicodes" then
+ fontdata.names=nil
+ elseif hashmethod=="names" and fontdata.names then
+ unifyresources(fontdata,fontdata.names)
+ copyduplicates(fontdata)
+ fontdata.hashmethod="unicode"
+ fontdata.names=nil
+ else
+ readers.rehash(fontdata,"unicode")
+ end
+end
+function readers.addunicodetable(fontdata)
+ local resources=fontdata.resources
+ local unicodes=resources.unicodes
+ if not unicodes then
+ local descriptions=fontdata.descriptions
+ if descriptions then
+ unicodes={}
+ resources.unicodes=unicodes
+ for u,d in next,descriptions do
+ local n=d.name
+ if n then
+ unicodes[n]=u
end
- if mathkerns then
- for k,v in next,mathkerns do
- if not next(v) then
- mathkerns[k]=nil
+ end
+ end
+ end
+end
+local concat,sort=table.concat,table.sort
+local next,type,tostring=next,type,tostring
+local criterium=1
+local threshold=0
+local trace_packing=false trackers.register("otf.packing",function(v) trace_packing=v end)
+local trace_loading=false trackers.register("otf.loading",function(v) trace_loading=v end)
+local report_otf=logs.reporter("fonts","otf loading")
+local function tabstr_normal(t)
+ local s={}
+ local n=0
+ for k,v in next,t do
+ n=n+1
+ if type(v)=="table" then
+ s[n]=k..">"..tabstr_normal(v)
+ elseif v==true then
+ s[n]=k.."+"
+ elseif v then
+ s[n]=k.."="..v
+ else
+ s[n]=k.."-"
+ end
+ end
+ if n==0 then
+ return ""
+ elseif n==1 then
+ return s[1]
+ else
+ sort(s)
+ return concat(s,",")
+ end
+end
+local function tabstr_flat(t)
+ local s={}
+ local n=0
+ for k,v in next,t do
+ n=n+1
+ s[n]=k.."="..v
+ end
+ if n==0 then
+ return ""
+ elseif n==1 then
+ return s[1]
+ else
+ sort(s)
+ return concat(s,",")
+ end
+end
+local function tabstr_mixed(t)
+ local s={}
+ local n=#t
+ if n==0 then
+ return ""
+ elseif n==1 then
+ local k=t[1]
+ if k==true then
+ return "++"
+ elseif k==false then
+ return "--"
+ else
+ return tostring(k)
+ end
+ else
+ for i=1,n do
+ local k=t[i]
+ if k==true then
+ s[i]="++"
+ elseif k==false then
+ s[i]="--"
+ else
+ s[i]=k
+ end
+ end
+ return concat(s,",")
+ end
+end
+local function tabstr_boolean(t)
+ local s={}
+ local n=0
+ for k,v in next,t do
+ n=n+1
+ if v then
+ s[n]=k.."+"
+ else
+ s[n]=k.."-"
+ end
+ end
+ if n==0 then
+ return ""
+ elseif n==1 then
+ return s[1]
+ else
+ sort(s)
+ return concat(s,",")
+ end
+end
+function readers.pack(data)
+ if data then
+ local h,t,c={},{},{}
+ local hh,tt,cc={},{},{}
+ local nt,ntt=0,0
+ local function pack_normal(v)
+ local tag=tabstr_normal(v)
+ local ht=h[tag]
+ if ht then
+ c[ht]=c[ht]+1
+ return ht
+ else
+ nt=nt+1
+ t[nt]=v
+ h[tag]=nt
+ c[nt]=1
+ return nt
+ end
+ end
+ local function pack_flat(v)
+ local tag=tabstr_flat(v)
+ local ht=h[tag]
+ if ht then
+ c[ht]=c[ht]+1
+ return ht
+ else
+ nt=nt+1
+ t[nt]=v
+ h[tag]=nt
+ c[nt]=1
+ return nt
+ end
+ end
+ local function pack_boolean(v)
+ local tag=tabstr_boolean(v)
+ local ht=h[tag]
+ if ht then
+ c[ht]=c[ht]+1
+ return ht
+ else
+ nt=nt+1
+ t[nt]=v
+ h[tag]=nt
+ c[nt]=1
+ return nt
+ end
+ end
+ local function pack_indexed(v)
+ local tag=concat(v," ")
+ local ht=h[tag]
+ if ht then
+ c[ht]=c[ht]+1
+ return ht
+ else
+ nt=nt+1
+ t[nt]=v
+ h[tag]=nt
+ c[nt]=1
+ return nt
+ end
+ end
+ local function pack_mixed(v)
+ local tag=tabstr_mixed(v)
+ local ht=h[tag]
+ if ht then
+ c[ht]=c[ht]+1
+ return ht
+ else
+ nt=nt+1
+ t[nt]=v
+ h[tag]=nt
+ c[nt]=1
+ return nt
+ end
+ end
+ local function pack_final(v)
+ if c[v]<=criterium then
+ return t[v]
+ else
+ local hv=hh[v]
+ if hv then
+ return hv
+ else
+ ntt=ntt+1
+ tt[ntt]=t[v]
+ hh[v]=ntt
+ cc[ntt]=c[v]
+ return ntt
+ end
+ end
+ end
+ local function success(stage,pass)
+ if nt==0 then
+ if trace_loading or trace_packing then
+ report_otf("pack quality: nothing to pack")
+ end
+ return false
+ elseif nt>=threshold then
+ local one,two,rest=0,0,0
+ if pass==1 then
+ for k,v in next,c do
+ if v==1 then
+ one=one+1
+ elseif v==2 then
+ two=two+1
else
- for k,v in next,v do
- if v==0 then
- k[v]=nil
- end
- end
+ rest=rest+1
+ end
+ end
+ else
+ for k,v in next,cc do
+ if v>20 then
+ rest=rest+1
+ elseif v>10 then
+ two=two+1
+ else
+ one=one+1
end
end
- math.kerns=mathkerns
+ data.tables=tt
end
- if hvariants then
- math.hvariants,math.hparts,math.hitalic=check_variants(unicode,hvariants,splitter,unicodes)
+ if trace_loading or trace_packing then
+ report_otf("pack quality: stage %s, pass %s, %s packed, 1-10:%s, 11-20:%s, rest:%s (criterium: %s)",
+ stage,pass,one+two+rest,one,two,rest,criterium)
end
- if vvariants then
- math.vvariants,math.vparts,math.vitalic=check_variants(unicode,vvariants,splitter,unicodes)
+ return true
+ else
+ if trace_loading or trace_packing then
+ report_otf("pack quality: stage %s, pass %s, %s packed, aborting pack (threshold: %s)",
+ stage,pass,nt,threshold)
end
- if italic and italic~=0 then
- math.italic=italic
+ return false
+ end
+ end
+ local function packers(pass)
+ if pass==1 then
+ return pack_normal,pack_indexed,pack_flat,pack_boolean,pack_mixed
+ else
+ return pack_final,pack_final,pack_final,pack_final,pack_final
+ end
+ end
+ local resources=data.resources
+ local sequences=resources.sequences
+ local sublookups=resources.sublookups
+ local features=resources.features
+ local chardata=characters and characters.data
+ local descriptions=data.descriptions or data.glyphs
+ if not descriptions then
+ return
+ end
+ for pass=1,2 do
+ if trace_packing then
+ report_otf("start packing: stage 1, pass %s",pass)
+ end
+ local pack_normal,pack_indexed,pack_flat,pack_boolean,pack_mixed=packers(pass)
+ for unicode,description in next,descriptions do
+ local boundingbox=description.boundingbox
+ if boundingbox then
+ description.boundingbox=pack_indexed(boundingbox)
+ end
+ local math=description.math
+ if math then
+ local kerns=math.kerns
+ if kerns then
+ for tag,kern in next,kerns do
+ kerns[tag]=pack_normal(kern)
+ end
+ end
+ end
+ end
+ local function packthem(sequences)
+ for i=1,#sequences do
+ local sequence=sequences[i]
+ local kind=sequence.type
+ local steps=sequence.steps
+ local order=sequence.order
+ local features=sequence.features
+ local flags=sequence.flags
+ if steps then
+ for i=1,#steps do
+ local step=steps[i]
+ if kind=="gpos_pair" then
+ local c=step.coverage
+ if c then
+ if step.format=="kern" then
+ for g1,d1 in next,c do
+ c[g1]=pack_normal(d1)
+ end
+ else
+ for g1,d1 in next,c do
+ for g2,d2 in next,d1 do
+ local f=d2[1] if f then d2[1]=pack_indexed(f) end
+ local s=d2[2] if s then d2[2]=pack_indexed(s) end
+ end
+ end
+ end
+ end
+ elseif kind=="gpos_single" then
+ local c=step.coverage
+ if c then
+ if step.format=="kern" then
+ step.coverage=pack_normal(c)
+ else
+ for g1,d1 in next,c do
+ c[g1]=pack_indexed(d1)
+ end
+ end
+ end
+ elseif kind=="gpos_cursive" then
+ local c=step.coverage
+ if c then
+ for g1,d1 in next,c do
+ local f=d1[2] if f then d1[2]=pack_indexed(f) end
+ local s=d1[3] if s then d1[3]=pack_indexed(s) end
+ end
+ end
+ elseif kind=="gpos_mark2base" or kind=="gpos_mark2mark" then
+ local c=step.baseclasses
+ if c then
+ for g1,d1 in next,c do
+ for g2,d2 in next,d1 do
+ d1[g2]=pack_indexed(d2)
+ end
+ end
+ end
+ local c=step.coverage
+ if c then
+ for g1,d1 in next,c do
+ d1[2]=pack_indexed(d1[2])
+ end
+ end
+ elseif kind=="gpos_mark2ligature" then
+ local c=step.baseclasses
+ if c then
+ for g1,d1 in next,c do
+ for g2,d2 in next,d1 do
+ for g3,d3 in next,d2 do
+ d2[g3]=pack_indexed(d3)
+ end
+ end
+ end
+ end
+ local c=step.coverage
+ if c then
+ for g1,d1 in next,c do
+ d1[2]=pack_indexed(d1[2])
+ end
+ end
+ end
+ local rules=step.rules
+ if rules then
+ for i=1,#rules do
+ local rule=rules[i]
+ local r=rule.before if r then for i=1,#r do r[i]=pack_boolean(r[i]) end end
+ local r=rule.after if r then for i=1,#r do r[i]=pack_boolean(r[i]) end end
+ local r=rule.current if r then for i=1,#r do r[i]=pack_boolean(r[i]) end end
+ local r=rule.replacements if r then rule.replacements=pack_flat (r) end
+ end
+ end
+ end
+ end
+ if order then
+ sequence.order=pack_indexed(order)
+ end
+ if features then
+ for script,feature in next,features do
+ features[script]=pack_normal(feature)
+ end
+ end
+ if flags then
+ sequence.flags=pack_normal(flags)
+ end
end
- description.math=math
+ end
+ if sequences then
+ packthem(sequences)
+ end
+ if sublookups then
+ packthem(sublookups)
+ end
+ if features then
+ for k,list in next,features do
+ for feature,spec in next,list do
+ list[feature]=pack_normal(spec)
+ end
+ end
+ end
+ if not success(1,pass) then
+ return
end
end
- end
-end
-actions["reorganize glyph kerns"]=function(data,filename,raw)
- local descriptions=data.descriptions
- local resources=data.resources
- local unicodes=resources.unicodes
- for unicode,description in next,descriptions do
- local kerns=description.glyph.kerns
- if kerns then
- local newkerns={}
- for k,kern in next,kerns do
- local name=kern.char
- local offset=kern.off
- local lookup=kern.lookup
- if name and offset and lookup then
- local unicode=unicodes[name]
- if unicode then
- if type(lookup)=="table" then
- for l=1,#lookup do
- local lookup=lookup[l]
- local lookupkerns=newkerns[lookup]
- if lookupkerns then
- lookupkerns[unicode]=offset
- else
- newkerns[lookup]={ [unicode]=offset }
+ if nt>0 then
+ for pass=1,2 do
+ if trace_packing then
+ report_otf("start packing: stage 2, pass %s",pass)
+ end
+ local pack_normal,pack_indexed,pack_flat,pack_boolean,pack_mixed=packers(pass)
+ for unicode,description in next,descriptions do
+ local math=description.math
+ if math then
+ local kerns=math.kerns
+ if kerns then
+ math.kerns=pack_normal(kerns)
+ end
+ end
+ end
+ local function packthem(sequences)
+ for i=1,#sequences do
+ local sequence=sequences[i]
+ local kind=sequence.type
+ local steps=sequence.steps
+ local features=sequence.features
+ if steps then
+ for i=1,#steps do
+ local step=steps[i]
+ if kind=="gpos_pair" then
+ local c=step.coverage
+ if c then
+ if step.format=="kern" then
+ else
+ for g1,d1 in next,c do
+ for g2,d2 in next,d1 do
+ d1[g2]=pack_normal(d2)
+ end
+ end
+ end
+ end
+ end
+ local rules=step.rules
+ if rules then
+ for i=1,#rules do
+ local rule=rules[i]
+ local r=rule.before if r then rule.before=pack_normal(r) end
+ local r=rule.after if r then rule.after=pack_normal(r) end
+ local r=rule.current if r then rule.current=pack_normal(r) end
+ end
end
end
- else
- local lookupkerns=newkerns[lookup]
- if lookupkerns then
- lookupkerns[unicode]=offset
- else
- newkerns[lookup]={ [unicode]=offset }
+ end
+ if features then
+ sequence.features=pack_normal(features)
+ end
+ end
+ end
+ if sequences then
+ packthem(sequences)
+ end
+ if sublookups then
+ packthem(sublookups)
+ end
+ if not success(2,pass) then
+ end
+ end
+ for pass=1,2 do
+ if trace_packing then
+ report_otf("start packing: stage 3, pass %s",pass)
+ end
+ local pack_normal,pack_indexed,pack_flat,pack_boolean,pack_mixed=packers(pass)
+ local function packthem(sequences)
+ for i=1,#sequences do
+ local sequence=sequences[i]
+ local kind=sequence.type
+ local steps=sequence.steps
+ local features=sequence.features
+ if steps then
+ for i=1,#steps do
+ local step=steps[i]
+ if kind=="gpos_pair" then
+ local c=step.coverage
+ if c then
+ if step.format=="kern" then
+ else
+ for g1,d1 in next,c do
+ c[g1]=pack_normal(d1)
+ end
+ end
+ end
+ end
end
end
- elseif trace_loading then
- report_otf("problems with unicode %a of kern %a of glyph %U",name,k,unicode)
end
end
+ if sequences then
+ packthem(sequences)
+ end
+ if sublookups then
+ packthem(sublookups)
+ end
end
- description.kerns=newkerns
end
end
end
-actions["merge kern classes"]=function(data,filename,raw)
- local gposlist=raw.gpos
- if gposlist then
- local descriptions=data.descriptions
- local resources=data.resources
- local unicodes=resources.unicodes
- local splitter=data.helpers.tounicodetable
- local ignored=0
- local blocked=0
- for gp=1,#gposlist do
- local gpos=gposlist[gp]
- local subtables=gpos.subtables
- if subtables then
- local first_done={}
- local split={}
- for s=1,#subtables do
- local subtable=subtables[s]
- local kernclass=subtable.kernclass
- local lookup=subtable.lookup or subtable.name
- if kernclass then
- if #kernclass>0 then
- kernclass=kernclass[1]
- lookup=type(kernclass.lookup)=="string" and kernclass.lookup or lookup
- report_otf("fixing kernclass table of lookup %a",lookup)
- end
- local firsts=kernclass.firsts
- local seconds=kernclass.seconds
- local offsets=kernclass.offsets
- for n,s in next,firsts do
- split[s]=split[s] or lpegmatch(splitter,s)
+local unpacked_mt={
+ __index=function(t,k)
+ t[k]=false
+ return k
+ end
+}
+function readers.unpack(data)
+ if data then
+ local tables=data.tables
+ if tables then
+ local resources=data.resources
+ local descriptions=data.descriptions or data.glyphs
+ local sequences=resources.sequences
+ local sublookups=resources.sublookups
+ local features=resources.features
+ local unpacked={}
+ setmetatable(unpacked,unpacked_mt)
+ for unicode,description in next,descriptions do
+ local tv=tables[description.boundingbox]
+ if tv then
+ description.boundingbox=tv
+ end
+ local math=description.math
+ if math then
+ local kerns=math.kerns
+ if kerns then
+ local tm=tables[kerns]
+ if tm then
+ math.kerns=tm
+ kerns=unpacked[tm]
end
- local maxseconds=0
- for n,s in next,seconds do
- if n>maxseconds then
- maxseconds=n
+ if kerns then
+ for k,kern in next,kerns do
+ local tv=tables[kern]
+ if tv then
+ kerns[k]=tv
+ end
end
- split[s]=split[s] or lpegmatch(splitter,s)
end
- for fk=1,#firsts do
- local fv=firsts[fk]
- local splt=split[fv]
- if splt then
- local extrakerns={}
- local baseoffset=(fk-1)*maxseconds
- for sk=2,maxseconds do
- local sv=seconds[sk]
- if sv then
- local splt=split[sv]
- if splt then
- local offset=offsets[baseoffset+sk]
- if offset then
- for i=1,#splt do
- extrakerns[splt[i]]=offset
+ end
+ end
+ end
+ local function unpackthem(sequences)
+ for i=1,#sequences do
+ local sequence=sequences[i]
+ local kind=sequence.type
+ local steps=sequence.steps
+ local order=sequence.order
+ local features=sequence.features
+ local flags=sequence.flags
+ local markclass=sequence.markclass
+ if steps then
+ for i=1,#steps do
+ local step=steps[i]
+ if kind=="gpos_pair" then
+ local c=step.coverage
+ if c then
+ if step.format=="kern" then
+ for g1,d1 in next,c do
+ local tv=tables[d1]
+ if tv then
+ c[g1]=tv
+ end
+ end
+ else
+ for g1,d1 in next,c do
+ local tv=tables[d1]
+ if tv then
+ c[g1]=tv
+ d1=tv
+ end
+ for g2,d2 in next,d1 do
+ local tv=tables[d2]
+ if tv then
+ d1[g2]=tv
+ d2=tv
end
+ local f=tables[d2[1]] if f then d2[1]=f end
+ local s=tables[d2[2]] if s then d2[2]=s end
end
end
end
end
- for i=1,#splt do
- local first_unicode=splt[i]
- if first_done[first_unicode] then
- report_otf("lookup %a: ignoring further kerns of %C",lookup,first_unicode)
- blocked=blocked+1
+ elseif kind=="gpos_single" then
+ local c=step.coverage
+ if c then
+ if step.format=="kern" then
+ local tv=tables[c]
+ if tv then
+ step.coverage=tv
+ end
else
- first_done[first_unicode]=true
- local description=descriptions[first_unicode]
- if description then
- local kerns=description.kerns
- if not kerns then
- kerns={}
- description.kerns=kerns
+ for g1,d1 in next,c do
+ local tv=tables[d1]
+ if tv then
+ c[g1]=tv
end
- local lookupkerns=kerns[lookup]
- if not lookupkerns then
- lookupkerns={}
- kerns[lookup]=lookupkerns
+ end
+ end
+ end
+ elseif kind=="gpos_cursive" then
+ local c=step.coverage
+ if c then
+ for g1,d1 in next,c do
+ local f=tables[d1[2]] if f then d1[2]=f end
+ local s=tables[d1[3]] if s then d1[3]=s end
+ end
+ end
+ elseif kind=="gpos_mark2base" or kind=="gpos_mark2mark" then
+ local c=step.baseclasses
+ if c then
+ for g1,d1 in next,c do
+ for g2,d2 in next,d1 do
+ local tv=tables[d2]
+ if tv then
+ d1[g2]=tv
end
- if overloadkerns then
- for second_unicode,kern in next,extrakerns do
- lookupkerns[second_unicode]=kern
- end
- else
- for second_unicode,kern in next,extrakerns do
- local k=lookupkerns[second_unicode]
- if not k then
- lookupkerns[second_unicode]=kern
- elseif k~=kern then
- if trace_loading then
- report_otf("lookup %a: ignoring overload of kern between %C and %C, rejecting %a, keeping %a",lookup,first_unicode,second_unicode,k,kern)
- end
- ignored=ignored+1
- end
+ end
+ end
+ end
+ local c=step.coverage
+ if c then
+ for g1,d1 in next,c do
+ local tv=tables[d1[2]]
+ if tv then
+ d1[2]=tv
+ end
+ end
+ end
+ elseif kind=="gpos_mark2ligature" then
+ local c=step.baseclasses
+ if c then
+ for g1,d1 in next,c do
+ for g2,d2 in next,d1 do
+ for g3,d3 in next,d2 do
+ local tv=tables[d2[g3]]
+ if tv then
+ d2[g3]=tv
end
end
- elseif trace_loading then
- report_otf("no glyph data for %U",first_unicode)
+ end
+ end
+ end
+ local c=step.coverage
+ if c then
+ for g1,d1 in next,c do
+ local tv=tables[d1[2]]
+ if tv then
+ d1[2]=tv
+ end
+ end
+ end
+ end
+ local rules=step.rules
+ if rules then
+ for i=1,#rules do
+ local rule=rules[i]
+ local before=rule.before
+ if before then
+ local tv=tables[before]
+ if tv then
+ rule.before=tv
+ before=tv
+ end
+ for i=1,#before do
+ local tv=tables[before[i]]
+ if tv then
+ before[i]=tv
+ end
+ end
+ end
+ local after=rule.after
+ if after then
+ local tv=tables[after]
+ if tv then
+ rule.after=tv
+ after=tv
+ end
+ for i=1,#after do
+ local tv=tables[after[i]]
+ if tv then
+ after[i]=tv
+ end
+ end
+ end
+ local current=rule.current
+ if current then
+ local tv=tables[current]
+ if tv then
+ rule.current=tv
+ current=tv
+ end
+ for i=1,#current do
+ local tv=tables[current[i]]
+ if tv then
+ current[i]=tv
+ end
+ end
+ end
+ local replacements=rule.replacements
+ if replacements then
+ local tv=tables[replace]
+ if tv then
+ rule.replacements=tv
end
end
end
end
end
- subtable.kernclass={}
+ end
+ if features then
+ local tv=tables[features]
+ if tv then
+ sequence.features=tv
+ features=tv
+ end
+ for script,feature in next,features do
+ local tv=tables[feature]
+ if tv then
+ features[script]=tv
+ end
+ end
+ end
+ if order then
+ local tv=tables[order]
+ if tv then
+ sequence.order=tv
+ end
+ end
+ if flags then
+ local tv=tables[flags]
+ if tv then
+ sequence.flags=tv
+ end
end
end
end
- end
- if ignored>0 then
- report_otf("%s kern overloads ignored",ignored)
- end
- if blocked>0 then
- report_otf("%s successive kerns blocked",blocked)
+ if sequences then
+ unpackthem(sequences)
+ end
+ if sublookups then
+ unpackthem(sublookups)
+ end
+ if features then
+ for k,list in next,features do
+ for feature,spec in next,list do
+ local tv=tables[spec]
+ if tv then
+ list[feature]=tv
+ end
+ end
+ end
+ end
+ data.tables=nil
end
end
end
-actions["check glyphs"]=function(data,filename,raw)
- for unicode,description in next,data.descriptions do
- description.glyph=nil
+local mt={
+ __index=function(t,k)
+ if k=="height" then
+ local ht=t.boundingbox[4]
+ return ht<0 and 0 or ht
+ elseif k=="depth" then
+ local dp=-t.boundingbox[2]
+ return dp<0 and 0 or dp
+ elseif k=="width" then
+ return 0
+ elseif k=="name" then
+ return forcenotdef and ".notdef"
+ end
end
+}
+local function sameformat(sequence,steps,first,nofsteps,kind)
+ return true
end
-local valid=(R("\x00\x7E")-S("(){}[]<>%/ \n\r\f\v"))^0*P(-1)
-local function valid_ps_name(str)
- return str and str~="" and #str<64 and lpegmatch(valid,str) and true or false
-end
-actions["check metadata"]=function(data,filename,raw)
- local metadata=data.metadata
- for _,k in next,mainfields do
- if valid_fields[k] then
- local v=raw[k]
- if not metadata[k] then
- metadata[k]=v
+local function mergesteps_1(lookup,strict)
+ local steps=lookup.steps
+ local nofsteps=lookup.nofsteps
+ local first=steps[1]
+ if strict then
+ local f=first.format
+ for i=2,nofsteps do
+ if steps[i].format~=f then
+ report("not merging %a steps of %a lookup %a, different formats",nofsteps,lookup.type,lookup.name)
+ return 0
+ end
+ end
+ end
+ report("merging %a steps of %a lookup %a",nofsteps,lookup.type,lookup.name)
+ local target=first.coverage
+ for i=2,nofsteps do
+ for k,v in next,steps[i].coverage do
+ if not target[k] then
+ target[k]=v
+ end
+ end
+ end
+ lookup.nofsteps=1
+ lookup.merged=true
+ lookup.steps={ first }
+ return nofsteps-1
+end
+local function mergesteps_2(lookup,strict)
+ local steps=lookup.steps
+ local nofsteps=lookup.nofsteps
+ local first=steps[1]
+ if strict then
+ local f=first.format
+ for i=2,nofsteps do
+ if steps[i].format~=f then
+ report("not merging %a steps of %a lookup %a, different formats",nofsteps,lookup.type,lookup.name)
+ return 0
+ end
+ end
+ end
+ report("merging %a steps of %a lookup %a",nofsteps,lookup.type,lookup.name)
+ local target=first.coverage
+ for i=2,nofsteps do
+ for k,v in next,steps[i].coverage do
+ local tk=target[k]
+ if tk then
+ for k,v in next,v do
+ if not tk[k] then
+ tk[k]=v
+ end
+ end
+ else
+ target[k]=v
+ end
+ end
+ end
+ lookup.nofsteps=1
+ lookup.steps={ first }
+ return nofsteps-1
+end
+local function mergesteps_3(lookup,strict)
+ local steps=lookup.steps
+ local nofsteps=lookup.nofsteps
+ local first=steps[1]
+ report("merging %a steps of %a lookup %a",nofsteps,lookup.type,lookup.name)
+ local baseclasses={}
+ local coverage={}
+ local used={}
+ for i=1,nofsteps do
+ local offset=i*10
+ local step=steps[i]
+ for k,v in sortedhash(step.baseclasses) do
+ baseclasses[offset+k]=v
+ end
+ for k,v in next,step.coverage do
+ local tk=coverage[k]
+ if tk then
+ for k,v in next,v do
+ if not tk[k] then
+ tk[k]=v
+ local c=offset+v[1]
+ v[1]=c
+ if not used[c] then
+ used[c]=true
+ end
+ end
+ end
+ else
+ coverage[k]=v
+ local c=offset+v[1]
+ v[1]=c
+ if not used[c] then
+ used[c]=true
+ end
end
end
end
- local ttftables=metadata.ttf_tables
- if ttftables then
- for i=1,#ttftables do
- ttftables[i].data="deleted"
+ for k,v in next,baseclasses do
+ if not used[k] then
+ baseclasses[k]=nil
+ report("discarding not used baseclass %i",k)
end
end
- local names=raw.names
- if metadata.validation_state and table.contains(metadata.validation_state,"bad_ps_fontname") then
- local function valid(what)
- if names then
- for i=1,#names do
- local list=names[i]
- local names=list.names
- if names then
- local name=names[what]
- if name and valid_ps_name(name) then
- return name
- end
- end
- end
+ first.baseclasses=baseclasses
+ first.coverage=coverage
+ lookup.nofsteps=1
+ lookup.steps={ first }
+ return nofsteps-1
+end
+local function nested(old,new)
+ for k,v in next,old do
+ if k=="ligature" then
+ if not new.ligature then
+ new.ligature=v
+ end
+ else
+ local n=new[k]
+ if n then
+ nested(v,n)
+ else
+ new[k]=v
end
end
- local function check(what)
- local oldname=metadata[what]
- if valid_ps_name(oldname) then
- report_otf("ignoring warning %a because %s %a is proper ASCII","bad_ps_fontname",what,oldname)
+ end
+end
+local function mergesteps_4(lookup)
+ local steps=lookup.steps
+ local nofsteps=lookup.nofsteps
+ local first=steps[1]
+ report("merging %a steps of %a lookup %a",nofsteps,lookup.type,lookup.name)
+ local target=first.coverage
+ for i=2,nofsteps do
+ for k,v in next,steps[i].coverage do
+ local tk=target[k]
+ if tk then
+ nested(v,tk)
else
- local newname=valid(what)
- if not newname then
- newname=formatters["bad-%s-%s"](what,file.nameonly(filename))
+ target[k]=v
+ end
+ end
+ end
+ lookup.nofsteps=1
+ lookup.steps={ first }
+ return nofsteps-1
+end
+local function checkkerns(lookup)
+ local steps=lookup.steps
+ local nofsteps=lookup.nofsteps
+ for i=1,nofsteps do
+ local step=steps[i]
+ if step.format=="pair" then
+ local coverage=step.coverage
+ local kerns=true
+ for g1,d1 in next,coverage do
+ if d1[1]~=0 or d1[2]~=0 or d1[4]~=0 then
+ kerns=false
+ break
end
- local warning=formatters["overloading %s from invalid ASCII name %a to %a"](what,oldname,newname)
- data.warnings[#data.warnings+1]=warning
- report_otf(warning)
- metadata[what]=newname
+ end
+ if kerns then
+ report("turning pairs of step %a of %a lookup %a into kerns",i,lookup.type,lookup.name)
+ for g1,d1 in next,coverage do
+ coverage[g1]=d1[3]
+ end
+ step.format="kern"
end
end
- check("fontname")
- check("fullname")
end
- if names then
- local psname=metadata.psname
- if not psname or psname=="" then
- for i=1,#names do
- local name=names[i]
- if lower(name.lang)=="english (us)" then
- local specification=name.names
- if specification then
- local postscriptname=specification.postscriptname
- if postscriptname then
- psname=postscriptname
+end
+local function checkpairs(lookup)
+ local steps=lookup.steps
+ local nofsteps=lookup.nofsteps
+ local kerned=0
+ for i=1,nofsteps do
+ local step=steps[i]
+ if step.format=="pair" then
+ local coverage=step.coverage
+ local kerns=true
+ for g1,d1 in next,coverage do
+ for g2,d2 in next,d1 do
+ if d2[2] then
+ kerns=false
+ break
+ else
+ local v=d2[1]
+ if v[1]~=0 or v[2]~=0 or v[4]~=0 then
+ kerns=false
+ break
end
end
end
- break
+ end
+ if kerns then
+ report("turning pairs of step %a of %a lookup %a into kerns",i,lookup.type,lookup.name)
+ for g1,d1 in next,coverage do
+ for g2,d2 in next,d1 do
+ d1[g2]=d2[1][3]
+ end
+ end
+ step.format="kern"
+ kerned=kerned+1
end
end
- if psname~=metadata.fontname then
- report_otf("fontname %a, fullname %a, psname %a",metadata.fontname,metadata.fullname,psname)
- end
- metadata.psname=psname
end
+ return kerned
end
-actions["cleanup tables"]=function(data,filename,raw)
- local duplicates=data.resources.duplicates
- if duplicates then
- for k,v in next,duplicates do
- if #v==1 then
- duplicates[k]=v[1]
+function readers.compact(data)
+ if not data or data.compacted then
+ return
+ else
+ data.compacted=true
+ end
+ local resources=data.resources
+ local merged=0
+ local kerned=0
+ local allsteps=0
+ local function compact(what)
+ local lookups=resources[what]
+ if lookups then
+ for i=1,#lookups do
+ local lookup=lookups[i]
+ local nofsteps=lookup.nofsteps
+ allsteps=allsteps+nofsteps
+ if nofsteps>1 then
+ local kind=lookup.type
+ if kind=="gsub_single" or kind=="gsub_alternate" or kind=="gsub_multiple" then
+ merged=merged+mergesteps_1(lookup)
+ elseif kind=="gsub_ligature" then
+ merged=merged+mergesteps_4(lookup)
+ elseif kind=="gpos_single" then
+ merged=merged+mergesteps_1(lookup,true)
+ checkkerns(lookup)
+ elseif kind=="gpos_pair" then
+ merged=merged+mergesteps_2(lookup,true)
+ kerned=kerned+checkpairs(lookup)
+ elseif kind=="gpos_cursive" then
+ merged=merged+mergesteps_2(lookup)
+ elseif kind=="gpos_mark2mark" or kind=="gpos_mark2base" or kind=="gpos_mark2ligature" then
+ merged=merged+mergesteps_3(lookup)
+ end
+ end
end
+ else
+ report("no lookups in %a",what)
end
end
- data.resources.indices=nil
- data.resources.unicodes=nil
- data.helpers=nil
+ compact("sequences")
+ compact("sublookups")
+ if merged>0 then
+ report("%i steps of %i removed due to merging",merged,allsteps)
+ end
+ if kerned>0 then
+ report("%i steps of %i steps turned from pairs into kerns",kerned,allsteps)
+ end
end
-actions["reorganize glyph lookups"]=function(data,filename,raw)
+function readers.expand(data)
+ if not data or data.expanded then
+ return
+ else
+ data.expanded=true
+ end
local resources=data.resources
- local unicodes=resources.unicodes
+ local sublookups=resources.sublookups
+ local sequences=resources.sequences
+ local markclasses=resources.markclasses
local descriptions=data.descriptions
- local splitter=data.helpers.tounicodelist
- local lookuptypes=resources.lookuptypes
- for unicode,description in next,descriptions do
- local lookups=description.glyph.lookups
- if lookups then
- for tag,lookuplist in next,lookups do
- for l=1,#lookuplist do
- local lookup=lookuplist[l]
- local specification=lookup.specification
- local lookuptype=lookup.type
- local lt=lookuptypes[tag]
- if not lt then
- lookuptypes[tag]=lookuptype
- elseif lt~=lookuptype then
- report_otf("conflicting lookuptypes, %a points to %a and %a",tag,lt,lookuptype)
- end
- if lookuptype=="ligature" then
- lookuplist[l]={ lpegmatch(splitter,specification.components) }
- elseif lookuptype=="alternate" then
- lookuplist[l]={ lpegmatch(splitter,specification.components) }
- elseif lookuptype=="substitution" then
- lookuplist[l]=unicodes[specification.variant]
- elseif lookuptype=="multiple" then
- lookuplist[l]={ lpegmatch(splitter,specification.components) }
- elseif lookuptype=="position" then
- lookuplist[l]={
- specification.x or 0,
- specification.y or 0,
- specification.h or 0,
- specification.v or 0
- }
- elseif lookuptype=="pair" then
- local one=specification.offsets[1]
- local two=specification.offsets[2]
- local paired=unicodes[specification.paired]
- if one then
- if two then
- lookuplist[l]={ paired,{ one.x or 0,one.y or 0,one.h or 0,one.v or 0 },{ two.x or 0,two.y or 0,two.h or 0,two.v or 0 } }
- else
- lookuplist[l]={ paired,{ one.x or 0,one.y or 0,one.h or 0,one.v or 0 } }
- end
- else
- if two then
- lookuplist[l]={ paired,{},{ two.x or 0,two.y or 0,two.h or 0,two.v or 0} }
- else
- lookuplist[l]={ paired }
- end
- end
- end
- end
+ if descriptions then
+ local defaultwidth=resources.defaultwidth or 0
+ local defaultheight=resources.defaultheight or 0
+ local defaultdepth=resources.defaultdepth or 0
+ local basename=trace_markwidth and file.basename(resources.filename)
+ for u,d in next,descriptions do
+ local bb=d.boundingbox
+ local wd=d.width
+ if not wd then
+ d.width=defaultwidth
+ elseif trace_markwidth and wd~=0 and d.class=="mark" then
+ report("mark %a with width %b found in %a",d.name or "<noname>",wd,basename)
end
- local slookups,mlookups
- for tag,lookuplist in next,lookups do
- if #lookuplist==1 then
- if slookups then
- slookups[tag]=lookuplist[1]
- else
- slookups={ [tag]=lookuplist[1] }
- end
+ if bb then
+ local ht=bb[4]
+ local dp=-bb[2]
+ if ht==0 or ht<0 then
else
- if mlookups then
- mlookups[tag]=lookuplist
- else
- mlookups={ [tag]=lookuplist }
- end
+ d.height=ht
+ end
+ if dp==0 or dp<0 then
+ else
+ d.depth=dp
end
- end
- if slookups then
- description.slookups=slookups
- end
- if mlookups then
- description.mlookups=mlookups
end
end
end
-end
-local zero={ 0,0 }
-actions["reorganize glyph anchors"]=function(data,filename,raw)
- local descriptions=data.descriptions
- for unicode,description in next,descriptions do
- local anchors=description.glyph.anchors
- if anchors then
- for class,data in next,anchors do
- if class=="baselig" then
- for tag,specification in next,data do
- local n=0
- for k,v in next,specification do
- if k>n then
- n=k
+ local function expandlookups(sequences)
+ if sequences then
+ for i=1,#sequences do
+ local sequence=sequences[i]
+ local steps=sequence.steps
+ if steps then
+ local kind=sequence.type
+ local markclass=sequence.markclass
+ if markclass then
+ if not markclasses then
+ report_warning("missing markclasses")
+ sequence.markclass=false
+ else
+ sequence.markclass=markclasses[markclass]
+ end
+ end
+ for i=1,sequence.nofsteps do
+ local step=steps[i]
+ local baseclasses=step.baseclasses
+ if baseclasses then
+ local coverage=step.coverage
+ for k,v in next,coverage do
+ v[1]=baseclasses[v[1]]
end
- local x,y=v.x,v.y
- if x or y then
- specification[k]={ x or 0,y or 0 }
- else
- specification[k]=zero
+ elseif kind=="gpos_cursive" then
+ local coverage=step.coverage
+ for k,v in next,coverage do
+ v[1]=coverage
end
end
- local t={}
- for i=1,n do
- t[i]=specification[i] or zero
- end
- data[tag]=t
- end
- else
- for tag,specification in next,data do
- local x,y=specification.x,specification.y
- if x or y then
- data[tag]={ x or 0,y or 0 }
- else
- data[tag]=zero
+ local rules=step.rules
+ if rules then
+ local rulehash={}
+ local rulesize=0
+ local coverage={}
+ local lookuptype=sequence.type
+ step.coverage=coverage
+ for nofrules=1,#rules do
+ local rule=rules[nofrules]
+ local current=rule.current
+ local before=rule.before
+ local after=rule.after
+ local replacements=rule.replacements or false
+ local sequence={}
+ local nofsequences=0
+ if before then
+ for n=1,#before do
+ nofsequences=nofsequences+1
+ sequence[nofsequences]=before[n]
+ end
+ end
+ local start=nofsequences+1
+ for n=1,#current do
+ nofsequences=nofsequences+1
+ sequence[nofsequences]=current[n]
+ end
+ local stop=nofsequences
+ if after then
+ for n=1,#after do
+ nofsequences=nofsequences+1
+ sequence[nofsequences]=after[n]
+ end
+ end
+ local lookups=rule.lookups or false
+ local subtype=nil
+ if lookups then
+ for k,v in next,lookups do
+ local lookup=sublookups[v]
+ if lookup then
+ lookups[k]=lookup
+ if not subtype then
+ subtype=lookup.type
+ end
+ else
+ end
+ end
+ end
+ if sequence[1] then
+ rulesize=rulesize+1
+ rulehash[rulesize]={
+ nofrules,
+ lookuptype,
+ sequence,
+ start,
+ stop,
+ lookups,
+ replacements,
+ subtype,
+ }
+ for unic in next,sequence[start] do
+ local cu=coverage[unic]
+ if not cu then
+ coverage[unic]=rulehash
+ end
+ end
+ end
+ end
end
end
end
end
- description.anchors=anchors
end
end
+ expandlookups(sequences)
+ expandlookups(sublookups)
end
-local bogusname=(P("uni")+P("u"))*R("AF","09")^4+(P("index")+P("glyph")+S("Ii")*P("dentity")*P(".")^0)*R("09")^1
-local uselessname=(1-bogusname)^0*bogusname
-actions["purge names"]=function(data,filename,raw)
- if purge_names then
- local n=0
- for u,d in next,data.descriptions do
- if lpegmatch(uselessname,d.name) then
- n=n+1
- d.name=nil
- end
- end
- if n>0 then
- report_otf("%s bogus names removed",n)
+
+end -- closure
+
+do -- begin closure to overcome local limits and interference
+
+if not modules then modules={} end modules ['font-otl']={
+ version=1.001,
+ comment="companion to font-ini.mkiv",
+ author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright="PRAGMA ADE / ConTeXt Development Team",
+ license="see context related readme files",
+}
+local gmatch,find,match,lower,strip=string.gmatch,string.find,string.match,string.lower,string.strip
+local type,next,tonumber,tostring,unpack=type,next,tonumber,tostring,unpack
+local abs=math.abs
+local ioflush=io.flush
+local derivetable=table.derive
+local formatters=string.formatters
+local setmetatableindex=table.setmetatableindex
+local allocate=utilities.storage.allocate
+local registertracker=trackers.register
+local registerdirective=directives.register
+local starttiming=statistics.starttiming
+local stoptiming=statistics.stoptiming
+local elapsedtime=statistics.elapsedtime
+local findbinfile=resolvers.findbinfile
+local trace_loading=false registertracker("otf.loading",function(v) trace_loading=v end)
+local trace_features=false registertracker("otf.features",function(v) trace_features=v end)
+local trace_defining=false registertracker("fonts.defining",function(v) trace_defining=v end)
+local report_otf=logs.reporter("fonts","otf loading")
+local fonts=fonts
+local otf=fonts.handlers.otf
+otf.version=3.016
+otf.cache=containers.define("fonts","otl",otf.version,true)
+local otfreaders=otf.readers
+local hashes=fonts.hashes
+local definers=fonts.definers
+local readers=fonts.readers
+local constructors=fonts.constructors
+local otffeatures=constructors.newfeatures("otf")
+local registerotffeature=otffeatures.register
+local enhancers=allocate()
+otf.enhancers=enhancers
+local patches={}
+enhancers.patches=patches
+local forceload=false
+local cleanup=0
+local syncspace=true
+local forcenotdef=false
+local applyruntimefixes=fonts.treatments and fonts.treatments.applyfixes
+local wildcard="*"
+local default="dflt"
+local formats=fonts.formats
+formats.otf="opentype"
+formats.ttf="truetype"
+formats.ttc="truetype"
+registerdirective("fonts.otf.loader.cleanup",function(v) cleanup=tonumber(v) or (v and 1) or 0 end)
+registerdirective("fonts.otf.loader.force",function(v) forceload=v end)
+registerdirective("fonts.otf.loader.syncspace",function(v) syncspace=v end)
+registerdirective("fonts.otf.loader.forcenotdef",function(v) forcenotdef=v end)
+local ordered_enhancers={
+ "check extra features",
+}
+local actions=allocate()
+local before=allocate()
+local after=allocate()
+patches.before=before
+patches.after=after
+local function enhance(name,data,filename,raw)
+ local enhancer=actions[name]
+ if enhancer then
+ if trace_loading then
+ report_otf("apply enhancement %a to file %a",name,filename)
+ ioflush()
end
+ enhancer(data,filename,raw)
+ else
end
end
-actions["compact lookups"]=function(data,filename,raw)
- if not compact_lookups then
- report_otf("not compacting")
- return
+function enhancers.apply(data,filename,raw)
+ local basename=file.basename(lower(filename))
+ if trace_loading then
+ report_otf("%s enhancing file %a","start",filename)
end
- local last=0
- local tags=table.setmetatableindex({},
- function(t,k)
- last=last+1
- t[k]=last
- return last
- end
- )
- local descriptions=data.descriptions
- local resources=data.resources
- for u,d in next,descriptions do
- local slookups=d.slookups
- if type(slookups)=="table" then
- local s={}
- for k,v in next,slookups do
- s[tags[k]]=v
- end
- d.slookups=s
- end
- local mlookups=d.mlookups
- if type(mlookups)=="table" then
- local m={}
- for k,v in next,mlookups do
- m[tags[k]]=v
- end
- d.mlookups=m
- end
- local kerns=d.kerns
- if type(kerns)=="table" then
- local t={}
- for k,v in next,kerns do
- t[tags[k]]=v
- end
- d.kerns=t
- end
- end
- local lookups=data.lookups
- if lookups then
- local l={}
- for k,v in next,lookups do
- local rules=v.rules
- if rules then
- for i=1,#rules do
- local l=rules[i].lookups
- if type(l)=="table" then
- for i=1,#l do
- l[i]=tags[l[i]]
- end
- end
+ ioflush()
+ for e=1,#ordered_enhancers do
+ local enhancer=ordered_enhancers[e]
+ local b=before[enhancer]
+ if b then
+ for pattern,action in next,b do
+ if find(basename,pattern) then
+ action(data,filename,raw)
end
end
- l[tags[k]]=v
end
- data.lookups=l
- end
- local lookups=resources.lookups
- if lookups then
- local l={}
- for k,v in next,lookups do
- local s=v.subtables
- if type(s)=="table" then
- for i=1,#s do
- s[i]=tags[s[i]]
+ enhance(enhancer,data,filename,raw)
+ local a=after[enhancer]
+ if a then
+ for pattern,action in next,a do
+ if find(basename,pattern) then
+ action(data,filename,raw)
end
end
- l[tags[k]]=v
end
- resources.lookups=l
+ ioflush()
end
- local sequences=resources.sequences
- if sequences then
- for i=1,#sequences do
- local s=sequences[i]
- local n=s.name
- if n then
- s.name=tags[n]
- end
- local t=s.subtables
- if type(t)=="table" then
- for i=1,#t do
- t[i]=tags[t[i]]
- end
- end
+ if trace_loading then
+ report_otf("%s enhancing file %a","stop",filename)
+ end
+ ioflush()
+end
+function patches.register(what,where,pattern,action)
+ local pw=patches[what]
+ if pw then
+ local ww=pw[where]
+ if ww then
+ ww[pattern]=action
+ else
+ pw[where]={ [pattern]=action}
end
end
- local lookuptypes=resources.lookuptypes
- if lookuptypes then
- local l={}
- for k,v in next,lookuptypes do
- l[tags[k]]=v
+end
+function patches.report(fmt,...)
+ if trace_loading then
+ report_otf("patching: %s",formatters[fmt](...))
+ end
+end
+function enhancers.register(what,action)
+ actions[what]=action
+end
+function otf.load(filename,sub,featurefile)
+ local featurefile=nil
+ local base=file.basename(file.removesuffix(filename))
+ local name=file.removesuffix(base)
+ local attr=lfs.attributes(filename)
+ local size=attr and attr.size or 0
+ local time=attr and attr.modification or 0
+ if featurefile then
+ name=name.."@"..file.removesuffix(file.basename(featurefile))
+ end
+ if sub=="" then
+ sub=false
+ end
+ local hash=name
+ if sub then
+ hash=hash.."-"..sub
+ end
+ hash=containers.cleanname(hash)
+ local featurefiles
+ if featurefile then
+ featurefiles={}
+ for s in gmatch(featurefile,"[^,]+") do
+ local name=resolvers.findfile(file.addsuffix(s,'fea'),'fea') or ""
+ if name=="" then
+ report_otf("loading error, no featurefile %a",s)
+ else
+ local attr=lfs.attributes(name)
+ featurefiles[#featurefiles+1]={
+ name=name,
+ size=attr and attr.size or 0,
+ time=attr and attr.modification or 0,
+ }
+ end
+ end
+ if #featurefiles==0 then
+ featurefiles=nil
end
- resources.lookuptypes=l
end
- local anchor_to_lookup=resources.anchor_to_lookup
- if anchor_to_lookup then
- for anchor,lookups in next,anchor_to_lookup do
- local l={}
- for lookup,value in next,lookups do
- l[tags[lookup]]=value
+ local data=containers.read(otf.cache,hash)
+ local reload=not data or data.size~=size or data.time~=time or data.tableversion~=otfreaders.tableversion
+ if forceload then
+ report_otf("forced reload of %a due to hard coded flag",filename)
+ reload=true
+ end
+ if reload then
+ report_otf("loading %a, hash %a",filename,hash)
+ starttiming(otfreaders)
+ data=otfreaders.loadfont(filename,sub or 1)
+ if data then
+ otfreaders.compact(data)
+ otfreaders.rehash(data,"unicodes")
+ otfreaders.addunicodetable(data)
+ otfreaders.extend(data)
+ otfreaders.pack(data)
+ report_otf("loading done")
+ report_otf("saving %a in cache",filename)
+ data=containers.write(otf.cache,hash,data)
+ if cleanup>1 then
+ collectgarbage("collect")
+ end
+ stoptiming(otfreaders)
+ if elapsedtime then
+ report_otf("loading, optimizing, packing and caching time %s",elapsedtime(otfreaders))
+ end
+ if cleanup>3 then
+ collectgarbage("collect")
+ end
+ data=containers.read(otf.cache,hash)
+ if cleanup>2 then
+ collectgarbage("collect")
end
- anchor_to_lookup[anchor]=l
+ else
+ data=nil
+ report_otf("loading failed due to read error")
end
end
- local lookup_to_anchor=resources.lookup_to_anchor
- if lookup_to_anchor then
- local l={}
- for lookup,value in next,lookup_to_anchor do
- l[tags[lookup]]=value
+ if data then
+ if trace_defining then
+ report_otf("loading from cache using hash %a",hash)
+ end
+ otfreaders.unpack(data)
+ otfreaders.expand(data)
+ otfreaders.addunicodetable(data)
+ enhancers.apply(data,filename,data)
+ constructors.addcoreunicodes(unicodes)
+ if applyruntimefixes then
+ applyruntimefixes(filename,data)
end
- resources.lookup_to_anchor=l
+ data.metadata.math=data.resources.mathconstants
end
- tags=table.swapped(tags)
- report_otf("%s lookup tags compacted",#tags)
- resources.lookuptags=tags
+ return data
end
function otf.setfeatures(tfmdata,features)
local okay=constructors.initializefeatures("otf",tfmdata,features,trace_features,report_otf)
@@ -9222,7 +15498,6 @@ end
local function copytotfm(data,cache_id)
if data then
local metadata=data.metadata
- local warnings=data.warnings
local resources=data.resources
local properties=derivetable(data.properties)
local descriptions=derivetable(data.descriptions)
@@ -9230,14 +15505,13 @@ local function copytotfm(data,cache_id)
local characters={}
local parameters={}
local mathparameters={}
- local pfminfo=metadata.pfminfo or {}
local resources=data.resources
local unicodes=resources.unicodes
local spaceunits=500
local spacer="space"
- local designsize=metadata.designsize or metadata.design_size or 100
- local minsize=metadata.minsize or metadata.design_range_bottom or designsize
- local maxsize=metadata.maxsize or metadata.design_range_top or designsize
+ local designsize=metadata.designsize or 100
+ local minsize=metadata.minsize or designsize
+ local maxsize=metadata.maxsize or designsize
local mathspecs=metadata.math
if designsize==0 then
designsize=100
@@ -9249,7 +15523,7 @@ local function copytotfm(data,cache_id)
mathparameters[name]=value
end
end
- for unicode,_ in next,data.descriptions do
+ for unicode in next,data.descriptions do
characters[unicode]={}
end
if mathspecs then
@@ -9265,8 +15539,8 @@ local function copytotfm(data,cache_id)
local c=character
for i=1,#variants do
local un=variants[i]
- c.next=un
- c=characters[un]
+ c.next=un
+ c=characters[un]
end
c.horiz_variants=parts
elseif parts then
@@ -9279,20 +15553,20 @@ local function copytotfm(data,cache_id)
local c=character
for i=1,#variants do
local un=variants[i]
- c.next=un
- c=characters[un]
+ c.next=un
+ c=characters[un]
end
c.vert_variants=parts
elseif parts then
character.vert_variants=parts
end
if italic and italic~=0 then
- character.italic=italic
+ character.italic=italic
end
if vitalic and vitalic~=0 then
character.vert_italic=vitalic
end
- local accent=m.accent
+ local accent=m.accent
if accent then
character.accent=accent
end
@@ -9306,18 +15580,20 @@ local function copytotfm(data,cache_id)
local filename=constructors.checkedfilename(resources)
local fontname=metadata.fontname
local fullname=metadata.fullname or fontname
- local psname=metadata.psname or fontname or fullname
- local units=metadata.units or metadata.units_per_em or 1000
+ local psname=fontname or fullname
+ local units=metadata.units or 1000
if units==0 then
units=1000
metadata.units=1000
report_otf("changing %a units to %a",0,units)
end
- local monospaced=metadata.monospaced or metadata.isfixedpitch or (pfminfo.panose and pfminfo.panose.proportion=="Monospaced")
- local charwidth=pfminfo.avgwidth
- local charxheight=pfminfo.os2_xheight and pfminfo.os2_xheight>0 and pfminfo.os2_xheight
+ local monospaced=metadata.monospaced
+ local charwidth=metadata.averagewidth
+ local charxheight=metadata.xheight
local italicangle=metadata.italicangle
+ local hasitalics=metadata.hasitalics
properties.monospaced=monospaced
+ properties.hasitalics=hasitalics
parameters.italicangle=italicangle
parameters.charwidth=charwidth
parameters.charxheight=charxheight
@@ -9347,7 +15623,7 @@ local function copytotfm(data,cache_id)
spaceunits=tonumber(spaceunits) or 500
parameters.slant=0
parameters.space=spaceunits
- parameters.space_stretch=units/2
+ parameters.space_stretch=1*units/2
parameters.space_shrink=1*units/3
parameters.x_height=2*units/5
parameters.quad=units
@@ -9380,26 +15656,18 @@ local function copytotfm(data,cache_id)
parameters.designsize=(designsize/10)*65536
parameters.minsize=(minsize/10)*65536
parameters.maxsize=(maxsize/10)*65536
- parameters.ascender=abs(metadata.ascender or metadata.ascent or 0)
- parameters.descender=abs(metadata.descender or metadata.descent or 0)
+ parameters.ascender=abs(metadata.ascender or 0)
+ parameters.descender=abs(metadata.descender or 0)
parameters.units=units
properties.space=spacer
properties.encodingbytes=2
- properties.format=data.format or otf_format(filename) or formats.otf
+ properties.format=data.format or formats.otf
properties.noglyphnames=true
properties.filename=filename
properties.fontname=fontname
properties.fullname=fullname
properties.psname=psname
properties.name=filename or fullname
- if warnings and #warnings>0 then
- report_otf("warnings for font: %s",filename)
- report_otf()
- for i=1,#warnings do
- report_otf(" %s",warnings[i])
- end
- report_otf()
- end
return {
characters=characters,
descriptions=descriptions,
@@ -9408,7 +15676,6 @@ local function copytotfm(data,cache_id)
resources=resources,
properties=properties,
goodies=goodies,
- warnings=warnings,
}
end
end
@@ -9418,38 +15685,13 @@ local function otftotfm(specification)
if not tfmdata then
local name=specification.name
local sub=specification.sub
+ local subindex=specification.subindex
local filename=specification.filename
local features=specification.features.normal
local rawdata=otf.load(filename,sub,features and features.featurefile)
if rawdata and next(rawdata) then
local descriptions=rawdata.descriptions
- local duplicates=rawdata.resources.duplicates
- if duplicates then
- local nofduplicates,nofduplicated=0,0
- for parent,list in next,duplicates do
- if type(list)=="table" then
- local n=#list
- for i=1,n do
- local unicode=list[i]
- if not descriptions[unicode] then
- descriptions[unicode]=descriptions[parent]
- nofduplicated=nofduplicated+1
- end
- end
- nofduplicates=nofduplicates+n
- else
- if not descriptions[list] then
- descriptions[list]=descriptions[parent]
- nofduplicated=nofduplicated+1
- end
- nofduplicates=nofduplicates+1
- end
- end
- if trace_otf and nofduplicated~=nofduplicates then
- report_otf("%i extra duplicates copied out of %i",nofduplicated,nofduplicates)
- end
- end
- rawdata.lookuphash={}
+ rawdata.lookuphash={}
tfmdata=copytotfm(rawdata,cache_id)
if tfmdata and next(tfmdata) then
local features=constructors.checkedfeatures("otf",features)
@@ -9501,33 +15743,129 @@ registerotffeature {
}
}
function otf.collectlookups(rawdata,kind,script,language)
- local sequences=rawdata.resources.sequences
- if sequences then
- local featuremap,featurelist={},{}
- for s=1,#sequences do
- local sequence=sequences[s]
- local features=sequence.features
- features=features and features[kind]
- features=features and (features[script] or features[default] or features[wildcard])
- features=features and (features[language] or features[default] or features[wildcard])
- if features then
- local subtables=sequence.subtables
- if subtables then
- for s=1,#subtables do
- local ss=subtables[s]
- if not featuremap[s] then
- featuremap[ss]=true
- featurelist[#featurelist+1]=ss
+ if not kind then
+ return
+ end
+ if not script then
+ script=default
+ end
+ if not language then
+ language=default
+ end
+ local lookupcache=rawdata.lookupcache
+ if not lookupcache then
+ lookupcache={}
+ rawdata.lookupcache=lookupcache
+ end
+ local kindlookup=lookupcache[kind]
+ if not kindlookup then
+ kindlookup={}
+ lookupcache[kind]=kindlookup
+ end
+ local scriptlookup=kindlookup[script]
+ if not scriptlookup then
+ scriptlookup={}
+ kindlookup[script]=scriptlookup
+ end
+ local languagelookup=scriptlookup[language]
+ if not languagelookup then
+ local sequences=rawdata.resources.sequences
+ local featuremap={}
+ local featurelist={}
+ if sequences then
+ for s=1,#sequences do
+ local sequence=sequences[s]
+ local features=sequence.features
+ if features then
+ features=features[kind]
+ if features then
+ features=features[script] or features[wildcard]
+ if features then
+ features=features[language] or features[wildcard]
+ if features then
+ if not featuremap[sequence] then
+ featuremap[sequence]=true
+ featurelist[#featurelist+1]=sequence
+ end
+ end
end
end
end
end
+ if #featurelist==0 then
+ featuremap,featurelist=false,false
+ end
+ else
+ featuremap,featurelist=false,false
+ end
+ languagelookup={ featuremap,featurelist }
+ scriptlookup[language]=languagelookup
+ end
+ return unpack(languagelookup)
+end
+local function getgsub(tfmdata,k,kind,value)
+ local shared=tfmdata.shared
+ local rawdata=shared and shared.rawdata
+ if rawdata then
+ local sequences=rawdata.resources.sequences
+ if sequences then
+ local properties=tfmdata.properties
+ local validlookups,lookuplist=otf.collectlookups(rawdata,kind,properties.script,properties.language)
+ if validlookups then
+ local choice=tonumber(value) or 1
+ for i=1,#lookuplist do
+ local lookup=lookuplist[i]
+ local steps=lookup.steps
+ local nofsteps=lookup.nofsteps
+ for i=1,nofsteps do
+ local coverage=steps[i].coverage
+ if coverage then
+ local found=coverage[k]
+ if found then
+ return found,lookup.type
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+end
+otf.getgsub=getgsub
+function otf.getsubstitution(tfmdata,k,kind,value)
+ local found,kind=getgsub(tfmdata,k,kind)
+ if not found then
+ elseif kind=="gsub_single" then
+ return found
+ elseif kind=="gsub_alternate" then
+ local choice=tonumber(value) or 1
+ return found[choice] or found[1] or k
+ end
+ return k
+end
+otf.getalternate=otf.getsubstitution
+function otf.getmultiple(tfmdata,k,kind)
+ local found,kind=getgsub(tfmdata,k,kind)
+ if found and kind=="gsub_multiple" then
+ return found
+ end
+ return { k }
+end
+function otf.getkern(tfmdata,left,right,kind)
+ local kerns=getgsub(tfmdata,left,kind or "kern",true)
+ if kerns then
+ local found=kerns[right]
+ local kind=type(found)
+ if kind=="table" then
+ found=found[1][3]
+ elseif kind~="number" then
+ found=false
end
- if #featurelist>0 then
- return featuremap,featurelist
+ if found then
+ return found*tfmdata.parameters.factor
end
end
- return nil,nil
+ return 0
end
local function check_otf(forced,specification,suffix)
local name=specification.name
@@ -9555,7 +15893,6 @@ readers.opentype=opentypereader
function readers.otf (specification) return opentypereader(specification,"otf") end
function readers.ttf (specification) return opentypereader(specification,"ttf") end
function readers.ttc (specification) return opentypereader(specification,"ttf") end
-function readers.dfont(specification) return opentypereader(specification,"ttf") end
function otf.scriptandlanguage(tfmdata,attr)
local properties=tfmdata.properties
return properties.script or "dflt",properties.language or "dflt"
@@ -9564,110 +15901,55 @@ local function justset(coverage,unicode,replacement)
coverage[unicode]=replacement
end
otf.coverup={
- stepkey="subtables",
+ stepkey="steps",
actions={
+ chainsubstitution=justset,
+ chainposition=justset,
substitution=justset,
alternate=justset,
multiple=justset,
- ligature=justset,
kern=justset,
- chainsubstitution=justset,
- chainposition=justset,
+ pair=justset,
+ ligature=function(coverage,unicode,ligature)
+ local first=ligature[1]
+ local tree=coverage[first]
+ if not tree then
+ tree={}
+ coverage[first]=tree
+ end
+ for i=2,#ligature do
+ local l=ligature[i]
+ local t=tree[l]
+ if not t then
+ t={}
+ tree[l]=t
+ end
+ tree=t
+ end
+ tree.ligature=unicode
+ end,
},
- register=function(coverage,lookuptype,format,feature,n,descriptions,resources)
- local name=formatters["ctx_%s_%s_%s"](feature,lookuptype,n)
- if lookuptype=="kern" then
- resources.lookuptypes[name]="position"
- else
- resources.lookuptypes[name]=lookuptype
- end
- for u,c in next,coverage do
- local description=descriptions[u]
- local slookups=description.slookups
- if slookups then
- slookups[name]=c
- else
- description.slookups={ [name]=c }
- end
- end
- return name
+ register=function(coverage,featuretype,format)
+ return {
+ format=format,
+ coverage=coverage,
+ }
end
}
-local function getgsub(tfmdata,k,kind)
- local description=tfmdata.descriptions[k]
- if description then
- local slookups=description.slookups
- if slookups then
- local shared=tfmdata.shared
- local rawdata=shared and shared.rawdata
- if rawdata then
- local lookuptypes=rawdata.resources.lookuptypes
- if lookuptypes then
- local properties=tfmdata.properties
- local validlookups,lookuplist=otf.collectlookups(rawdata,kind,properties.script,properties.language)
- if validlookups then
- for l=1,#lookuplist do
- local lookup=lookuplist[l]
- local found=slookups[lookup]
- if found then
- return found,lookuptypes[lookup]
- end
- end
- end
- end
- end
- end
- end
-end
-otf.getgsub=getgsub
-function otf.getsubstitution(tfmdata,k,kind,value)
- local found,kind=getgsub(tfmdata,k,kind)
- if not found then
- elseif kind=="substitution" then
- return found
- elseif kind=="alternate" then
- local choice=tonumber(value) or 1
- return found[choice] or found[1] or k
- end
- return k
-end
-otf.getalternate=otf.getsubstitution
-function otf.getmultiple(tfmdata,k,kind)
- local found,kind=getgsub(tfmdata,k,kind)
- if found and kind=="multiple" then
- return found
- end
- return { k }
-end
-function otf.getkern(tfmdata,left,right,kind)
- local kerns=getgsub(tfmdata,left,kind or "kern",true)
- if kerns then
- local found=kerns[right]
- local kind=type(found)
- if kind=="table" then
- found=found[1][3]
- elseif kind~="number" then
- found=false
- end
- if found then
- return found*tfmdata.parameters.factor
- end
- end
- return 0
-end
end -- closure
do -- begin closure to overcome local limits and interference
-if not modules then modules={} end modules ['font-otb']={
+if not modules then modules={} end modules ['font-oto']={
version=1.001,
comment="companion to font-ini.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
-local concat=table.concat
+local concat,unpack=table.concat,table.unpack
+local insert,remove=table.insert,table.remove
local format,gmatch,gsub,find,match,lower,strip=string.format,string.gmatch,string.gsub,string.find,string.match,string.lower,string.strip
local type,next,tonumber,tostring,rawget=type,next,tonumber,tostring,rawget
local lpegmatch=lpeg.match
@@ -9677,7 +15959,6 @@ local trace_singles=false trackers.register("otf.singles",function(v) trace_sing
local trace_multiples=false trackers.register("otf.multiples",function(v) trace_multiples=v end)
local trace_alternatives=false trackers.register("otf.alternatives",function(v) trace_alternatives=v end)
local trace_ligatures=false trackers.register("otf.ligatures",function(v) trace_ligatures=v end)
-local trace_ligatures_detail=false trackers.register("otf.ligatures.detail",function(v) trace_ligatures_detail=v end)
local trace_kerns=false trackers.register("otf.kerns",function(v) trace_kerns=v end)
local trace_preparing=false trackers.register("otf.preparing",function(v) trace_preparing=v end)
local report_prepare=logs.reporter("fonts","otf prepare")
@@ -9716,48 +15997,36 @@ local function gref(descriptions,n)
return "<error in base mode tracing>"
end
end
-local function cref(feature,lookuptags,lookupname)
- if lookupname then
- return formatters["feature %a, lookup %a"](feature,lookuptags[lookupname])
- else
- return formatters["feature %a"](feature)
- end
+local function cref(feature,sequence)
+ return formatters["feature %a, type %a, chain lookup %a"](feature,sequence.type,sequence.name)
end
-local function report_alternate(feature,lookuptags,lookupname,descriptions,unicode,replacement,value,comment)
+local function report_alternate(feature,sequence,descriptions,unicode,replacement,value,comment)
report_prepare("%s: base alternate %s => %s (%S => %S)",
- cref(feature,lookuptags,lookupname),
+ cref(feature,sequence),
gref(descriptions,unicode),
replacement and gref(descriptions,replacement),
value,
comment)
end
-local function report_substitution(feature,lookuptags,lookupname,descriptions,unicode,substitution)
+local function report_substitution(feature,sequence,descriptions,unicode,substitution)
report_prepare("%s: base substitution %s => %S",
- cref(feature,lookuptags,lookupname),
+ cref(feature,sequence),
gref(descriptions,unicode),
gref(descriptions,substitution))
end
-local function report_ligature(feature,lookuptags,lookupname,descriptions,unicode,ligature)
+local function report_ligature(feature,sequence,descriptions,unicode,ligature)
report_prepare("%s: base ligature %s => %S",
- cref(feature,lookuptags,lookupname),
+ cref(feature,sequence),
gref(descriptions,ligature),
gref(descriptions,unicode))
end
-local function report_kern(feature,lookuptags,lookupname,descriptions,unicode,otherunicode,value)
+local function report_kern(feature,sequence,descriptions,unicode,otherunicode,value)
report_prepare("%s: base kern %s + %s => %S",
- cref(feature,lookuptags,lookupname),
+ cref(feature,sequence),
gref(descriptions,unicode),
gref(descriptions,otherunicode),
value)
end
-local basemethods={}
-local basemethod="<unset>"
-local function applybasemethod(what,...)
- local m=basemethods[basemethod][what]
- if m then
- return m(...)
- end
-end
local basehash,basehashes,applied={},1,{}
local function registerbasehash(tfmdata)
local properties=tfmdata.properties
@@ -9775,239 +16044,6 @@ end
local function registerbasefeature(feature,value)
applied[#applied+1]=feature.."="..tostring(value)
end
-local trace=false
-local function finalize_ligatures(tfmdata,ligatures)
- local nofligatures=#ligatures
- if nofligatures>0 then
- local characters=tfmdata.characters
- local descriptions=tfmdata.descriptions
- local resources=tfmdata.resources
- local unicodes=resources.unicodes
- local private=resources.private
- local alldone=false
- while not alldone do
- local done=0
- for i=1,nofligatures do
- local ligature=ligatures[i]
- if ligature then
- local unicode,lookupdata=ligature[1],ligature[2]
- if trace_ligatures_detail then
- report_prepare("building % a into %a",lookupdata,unicode)
- end
- local size=#lookupdata
- local firstcode=lookupdata[1]
- local firstdata=characters[firstcode]
- local okay=false
- if firstdata then
- local firstname="ctx_"..firstcode
- for i=1,size-1 do
- local firstdata=characters[firstcode]
- if not firstdata then
- firstcode=private
- if trace_ligatures_detail then
- report_prepare("defining %a as %a",firstname,firstcode)
- end
- unicodes[firstname]=firstcode
- firstdata={ intermediate=true,ligatures={} }
- characters[firstcode]=firstdata
- descriptions[firstcode]={ name=firstname }
- private=private+1
- end
- local target
- local secondcode=lookupdata[i+1]
- local secondname=firstname.."_"..secondcode
- if i==size-1 then
- target=unicode
- if not rawget(unicodes,secondname) then
- unicodes[secondname]=unicode
- end
- okay=true
- else
- target=rawget(unicodes,secondname)
- if not target then
- break
- end
- end
- if trace_ligatures_detail then
- report_prepare("codes (%a,%a) + (%a,%a) -> %a",firstname,firstcode,secondname,secondcode,target)
- end
- local firstligs=firstdata.ligatures
- if firstligs then
- firstligs[secondcode]={ char=target }
- else
- firstdata.ligatures={ [secondcode]={ char=target } }
- end
- firstcode=target
- firstname=secondname
- end
- elseif trace_ligatures_detail then
- report_prepare("no glyph (%a,%a) for building %a",firstname,firstcode,target)
- end
- if okay then
- ligatures[i]=false
- done=done+1
- end
- end
- end
- alldone=done==0
- end
- if trace_ligatures_detail then
- for k,v in table.sortedhash(characters) do
- if v.ligatures then
- table.print(v,k)
- end
- end
- end
- resources.private=private
- return true
- end
-end
-local function preparesubstitutions(tfmdata,feature,value,validlookups,lookuplist)
- local characters=tfmdata.characters
- local descriptions=tfmdata.descriptions
- local resources=tfmdata.resources
- local properties=tfmdata.properties
- local changed=tfmdata.changed
- local lookuphash=resources.lookuphash
- local lookuptypes=resources.lookuptypes
- local lookuptags=resources.lookuptags
- local ligatures={}
- local alternate=tonumber(value) or true and 1
- local defaultalt=otf.defaultbasealternate
- local trace_singles=trace_baseinit and trace_singles
- local trace_alternatives=trace_baseinit and trace_alternatives
- local trace_ligatures=trace_baseinit and trace_ligatures
- local actions={
- substitution=function(lookupdata,lookuptags,lookupname,description,unicode)
- if trace_singles then
- report_substitution(feature,lookuptags,lookupname,descriptions,unicode,lookupdata)
- end
- changed[unicode]=lookupdata
- end,
- alternate=function(lookupdata,lookuptags,lookupname,description,unicode)
- local replacement=lookupdata[alternate]
- if replacement then
- changed[unicode]=replacement
- if trace_alternatives then
- report_alternate(feature,lookuptags,lookupname,descriptions,unicode,replacement,value,"normal")
- end
- elseif defaultalt=="first" then
- replacement=lookupdata[1]
- changed[unicode]=replacement
- if trace_alternatives then
- report_alternate(feature,lookuptags,lookupname,descriptions,unicode,replacement,value,defaultalt)
- end
- elseif defaultalt=="last" then
- replacement=lookupdata[#data]
- if trace_alternatives then
- report_alternate(feature,lookuptags,lookupname,descriptions,unicode,replacement,value,defaultalt)
- end
- else
- if trace_alternatives then
- report_alternate(feature,lookuptags,lookupname,descriptions,unicode,replacement,value,"unknown")
- end
- end
- end,
- ligature=function(lookupdata,lookuptags,lookupname,description,unicode)
- if trace_ligatures then
- report_ligature(feature,lookuptags,lookupname,descriptions,unicode,lookupdata)
- end
- ligatures[#ligatures+1]={ unicode,lookupdata }
- end,
- }
- for unicode,character in next,characters do
- local description=descriptions[unicode]
- local lookups=description.slookups
- if lookups then
- for l=1,#lookuplist do
- local lookupname=lookuplist[l]
- local lookupdata=lookups[lookupname]
- if lookupdata then
- local lookuptype=lookuptypes[lookupname]
- local action=actions[lookuptype]
- if action then
- action(lookupdata,lookuptags,lookupname,description,unicode)
- end
- end
- end
- end
- local lookups=description.mlookups
- if lookups then
- for l=1,#lookuplist do
- local lookupname=lookuplist[l]
- local lookuplist=lookups[lookupname]
- if lookuplist then
- local lookuptype=lookuptypes[lookupname]
- local action=actions[lookuptype]
- if action then
- for i=1,#lookuplist do
- action(lookuplist[i],lookuptags,lookupname,description,unicode)
- end
- end
- end
- end
- end
- end
- properties.hasligatures=finalize_ligatures(tfmdata,ligatures)
-end
-local function preparepositionings(tfmdata,feature,value,validlookups,lookuplist)
- local characters=tfmdata.characters
- local descriptions=tfmdata.descriptions
- local resources=tfmdata.resources
- local properties=tfmdata.properties
- local lookuptags=resources.lookuptags
- local sharedkerns={}
- local traceindeed=trace_baseinit and trace_kerns
- local haskerns=false
- for unicode,character in next,characters do
- local description=descriptions[unicode]
- local rawkerns=description.kerns
- if rawkerns then
- local s=sharedkerns[rawkerns]
- if s==false then
- elseif s then
- character.kerns=s
- else
- local newkerns=character.kerns
- local done=false
- for l=1,#lookuplist do
- local lookup=lookuplist[l]
- local kerns=rawkerns[lookup]
- if kerns then
- for otherunicode,value in next,kerns do
- if value==0 then
- elseif not newkerns then
- newkerns={ [otherunicode]=value }
- done=true
- if traceindeed then
- report_kern(feature,lookuptags,lookup,descriptions,unicode,otherunicode,value)
- end
- elseif not newkerns[otherunicode] then
- newkerns[otherunicode]=value
- done=true
- if traceindeed then
- report_kern(feature,lookuptags,lookup,descriptions,unicode,otherunicode,value)
- end
- end
- end
- end
- end
- if done then
- sharedkerns[rawkerns]=newkerns
- character.kerns=newkerns
- haskerns=true
- else
- sharedkerns[rawkerns]=false
- end
- end
- end
- end
- properties.haskerns=haskerns
-end
-basemethods.independent={
- preparesubstitutions=preparesubstitutions,
- preparepositionings=preparepositionings,
-}
local function makefake(tfmdata,name,present)
local resources=tfmdata.resources
local private=resources.private
@@ -10028,13 +16064,13 @@ local function make_1(present,tree,name)
end
end
end
-local function make_2(present,tfmdata,characters,tree,name,preceding,unicode,done,lookuptags,lookupname)
+local function make_2(present,tfmdata,characters,tree,name,preceding,unicode,done)
for k,v in next,tree do
if k=="ligature" then
local character=characters[preceding]
if not character then
if trace_baseinit then
- report_prepare("weird ligature in lookup %a, current %C, preceding %C",lookuptags[lookupname],v,preceding)
+ report_prepare("weird ligature in lookup %a, current %C, preceding %C",sequence.name,v,preceding)
end
character=makefake(tfmdata,name,present)
end
@@ -10045,9 +16081,9 @@ local function make_2(present,tfmdata,characters,tree,name,preceding,unicode,don
character.ligatures={ [unicode]={ char=v } }
end
if done then
- local d=done[lookupname]
+ local d=done[name]
if not d then
- done[lookupname]={ "dummy",v }
+ done[name]={ "dummy",v }
else
d[#d+1]=v
end
@@ -10055,7 +16091,7 @@ local function make_2(present,tfmdata,characters,tree,name,preceding,unicode,don
else
local code=present[name] or unicode
local name=name.."_"..k
- make_2(present,tfmdata,characters,v,name,code,k,done,lookuptags,lookupname)
+ make_2(present,tfmdata,characters,v,name,code,k,done)
end
end
end
@@ -10064,52 +16100,63 @@ local function preparesubstitutions(tfmdata,feature,value,validlookups,lookuplis
local descriptions=tfmdata.descriptions
local resources=tfmdata.resources
local changed=tfmdata.changed
- local lookuphash=resources.lookuphash
- local lookuptypes=resources.lookuptypes
- local lookuptags=resources.lookuptags
local ligatures={}
local alternate=tonumber(value) or true and 1
local defaultalt=otf.defaultbasealternate
local trace_singles=trace_baseinit and trace_singles
local trace_alternatives=trace_baseinit and trace_alternatives
local trace_ligatures=trace_baseinit and trace_ligatures
- for l=1,#lookuplist do
- local lookupname=lookuplist[l]
- local lookupdata=lookuphash[lookupname]
- local lookuptype=lookuptypes[lookupname]
- for unicode,data in next,lookupdata do
- if lookuptype=="substitution" then
- if trace_singles then
- report_substitution(feature,lookuptags,lookupname,descriptions,unicode,data)
- end
- changed[unicode]=data
- elseif lookuptype=="alternate" then
- local replacement=data[alternate]
- if replacement then
- changed[unicode]=replacement
- if trace_alternatives then
- report_alternate(feature,lookuptags,lookupname,descriptions,unicode,replacement,value,"normal")
- end
- elseif defaultalt=="first" then
- replacement=data[1]
- changed[unicode]=replacement
- if trace_alternatives then
- report_alternate(feature,lookuptags,lookupname,descriptions,unicode,replacement,value,defaultalt)
- end
- elseif defaultalt=="last" then
- replacement=data[#data]
- if trace_alternatives then
- report_alternate(feature,lookuptags,lookupname,descriptions,unicode,replacement,value,defaultalt)
+ for i=1,#lookuplist do
+ local sequence=lookuplist[i]
+ local steps=sequence.steps
+ local kind=sequence.type
+ if kind=="gsub_single" then
+ for i=1,#steps do
+ for unicode,data in next,steps[i].coverage do
+ if not changed[unicode] then
+ if trace_singles then
+ report_substitution(feature,sequence,descriptions,unicode,data)
+ end
+ changed[unicode]=data
end
- else
- if trace_alternatives then
- report_alternate(feature,lookuptags,lookupname,descriptions,unicode,replacement,value,"unknown")
+ end
+ end
+ elseif kind=="gsub_alternate" then
+ for i=1,#steps do
+ for unicode,data in next,steps[i].coverage do
+ if not changed[unicode] then
+ local replacement=data[alternate]
+ if replacement then
+ changed[unicode]=replacement
+ if trace_alternatives then
+ report_alternate(feature,sequence,descriptions,unicode,replacement,value,"normal")
+ end
+ elseif defaultalt=="first" then
+ replacement=data[1]
+ changed[unicode]=replacement
+ if trace_alternatives then
+ report_alternate(feature,sequence,descriptions,unicode,replacement,value,defaultalt)
+ end
+ elseif defaultalt=="last" then
+ replacement=data[#data]
+ if trace_alternatives then
+ report_alternate(feature,sequence,descriptions,unicode,replacement,value,defaultalt)
+ end
+ else
+ if trace_alternatives then
+ report_alternate(feature,sequence,descriptions,unicode,replacement,value,"unknown")
+ end
+ end
end
end
- elseif lookuptype=="ligature" then
- ligatures[#ligatures+1]={ unicode,data,lookupname }
- if trace_ligatures then
- report_ligature(feature,lookuptags,lookupname,descriptions,unicode,data)
+ end
+ elseif kind=="gsub_ligature" then
+ for i=1,#steps do
+ for unicode,data in next,steps[i].coverage do
+ ligatures[#ligatures+1]={ unicode,data,"" }
+ if trace_ligatures then
+ report_ligature(feature,sequence,descriptions,unicode,data)
+ end
end
end
end
@@ -10127,7 +16174,7 @@ local function preparesubstitutions(tfmdata,feature,value,validlookups,lookuplis
for i=1,nofligatures do
local ligature=ligatures[i]
local unicode,tree,lookupname=ligature[1],ligature[2],ligature[3]
- make_2(present,tfmdata,characters,tree,"ctx_"..unicode,unicode,unicode,done,lookuptags,lookupname)
+ make_2(present,tfmdata,characters,tree,"ctx_"..unicode,unicode,unicode,done,sequence)
end
end
end
@@ -10136,30 +16183,62 @@ local function preparepositionings(tfmdata,feature,value,validlookups,lookuplist
local descriptions=tfmdata.descriptions
local resources=tfmdata.resources
local properties=tfmdata.properties
- local lookuphash=resources.lookuphash
- local lookuptags=resources.lookuptags
local traceindeed=trace_baseinit and trace_kerns
- for l=1,#lookuplist do
- local lookupname=lookuplist[l]
- local lookupdata=lookuphash[lookupname]
- for unicode,data in next,lookupdata do
- local character=characters[unicode]
- local kerns=character.kerns
- if not kerns then
- kerns={}
- character.kerns=kerns
- end
- if traceindeed then
- for otherunicode,kern in next,data do
- if not kerns[otherunicode] and kern~=0 then
- kerns[otherunicode]=kern
- report_kern(feature,lookuptags,lookup,descriptions,unicode,otherunicode,kern)
+ for i=1,#lookuplist do
+ local sequence=lookuplist[i]
+ local steps=sequence.steps
+ local kind=sequence.type
+ local format=sequence.format
+ if kind=="gpos_pair" then
+ for i=1,#steps do
+ local step=steps[i]
+ if step.format=="kern" then
+ for unicode,data in next,steps[i].coverage do
+ local character=characters[unicode]
+ local kerns=character.kerns
+ if not kerns then
+ kerns={}
+ character.kerns=kerns
+ end
+ if traceindeed then
+ for otherunicode,kern in next,data do
+ if not kerns[otherunicode] and kern~=0 then
+ kerns[otherunicode]=kern
+ report_kern(feature,sequence,descriptions,unicode,otherunicode,kern)
+ end
+ end
+ else
+ for otherunicode,kern in next,data do
+ if not kerns[otherunicode] and kern~=0 then
+ kerns[otherunicode]=kern
+ end
+ end
+ end
end
- end
- else
- for otherunicode,kern in next,data do
- if not kerns[otherunicode] and kern~=0 then
- kerns[otherunicode]=kern
+ else
+ for unicode,data in next,steps[i].coverage do
+ local character=characters[unicode]
+ local kerns=character.kerns
+ for otherunicode,kern in next,data do
+ if not kern[2] and not (kerns and kerns[otherunicode]) then
+ local kern=kern[1]
+ if kern[1]~=0 or kern[2]~=0 or kern[4]~=0 then
+ else
+ kern=kern[3]
+ if kern~=0 then
+ if kerns then
+ kerns[otherunicode]=kern
+ else
+ kerns={ [otherunicode]=kern }
+ character.kerns=kerns
+ end
+ if traceindeed then
+ report_kern(feature,sequence,descriptions,unicode,otherunicode,kern)
+ end
+ end
+ end
+ end
+ end
end
end
end
@@ -10167,28 +16246,22 @@ local function preparepositionings(tfmdata,feature,value,validlookups,lookuplist
end
end
local function initializehashes(tfmdata)
- nodeinitializers.features(tfmdata)
end
-basemethods.shared={
- initializehashes=initializehashes,
- preparesubstitutions=preparesubstitutions,
- preparepositionings=preparepositionings,
-}
-basemethod="independent"
local function featuresinitializer(tfmdata,value)
if true then
local starttime=trace_preparing and os.clock()
local features=tfmdata.shared.features
local fullname=tfmdata.properties.fullname or "?"
if features then
- applybasemethod("initializehashes",tfmdata)
+ initializehashes(tfmdata)
local collectlookups=otf.collectlookups
local rawdata=tfmdata.shared.rawdata
local properties=tfmdata.properties
- local script=properties.script
- local language=properties.language
- local basesubstitutions=rawdata.resources.features.gsub
- local basepositionings=rawdata.resources.features.gpos
+ local script=properties.script
+ local language=properties.language
+ local rawfeatures=rawdata.resources.features
+ local basesubstitutions=rawfeatures and rawfeatures.gsub
+ local basepositionings=rawfeatures and rawfeatures.gpos
if basesubstitutions or basepositionings then
local sequences=tfmdata.resources.sequences
for s=1,#sequences do
@@ -10207,13 +16280,13 @@ local function featuresinitializer(tfmdata,value)
if trace_preparing then
report_prepare("filtering base %s feature %a for %a with value %a","sub",feature,fullname,value)
end
- applybasemethod("preparesubstitutions",tfmdata,feature,value,validlookups,lookuplist)
+ preparesubstitutions(tfmdata,feature,value,validlookups,lookuplist)
registerbasefeature(feature,value)
elseif basepositionings and basepositionings[feature] then
if trace_preparing then
report_prepare("filtering base %a feature %a for %a with value %a","pos",feature,fullname,value)
end
- applybasemethod("preparepositionings",tfmdata,feature,value,validlookups,lookuplist)
+ preparepositionings(tfmdata,feature,value,validlookups,lookuplist)
registerbasefeature(feature,value)
end
end
@@ -10237,17 +16310,12 @@ registerotffeature {
base=featuresinitializer,
}
}
-directives.register("fonts.otf.loader.basemethod",function(v)
- if basemethods[v] then
- basemethod=v
- end
-end)
end -- closure
do -- begin closure to overcome local limits and interference
-if not modules then modules={} end modules ['font-inj']={
+if not modules then modules={} end modules ['font-otj']={
version=1.001,
comment="companion to font-lib.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
@@ -10258,17 +16326,30 @@ if not nodes.properties then return end
local next,rawget=next,rawget
local utfchar=utf.char
local fastcopy=table.fastcopy
-local trace_injections=false trackers.register("fonts.injections",function(v) trace_injections=v end)
+local registertracker=trackers.register
+local trace_injections=false registertracker("fonts.injections",function(v) trace_injections=v end)
+local trace_marks=false registertracker("fonts.injections.marks",function(v) trace_marks=v end)
+local trace_cursive=false registertracker("fonts.injections.cursive",function(v) trace_cursive=v end)
+local trace_spaces=false registertracker("otf.spaces",function(v) trace_spaces=v end)
+local use_advance=false directives.register("fonts.injections.advance",function(v) use_advance=v end)
local report_injections=logs.reporter("fonts","injections")
+local report_spaces=logs.reporter("fonts","spaces")
local attributes,nodes,node=attributes,nodes,node
fonts=fonts
-local fontdata=fonts.hashes.identifiers
+local hashes=fonts.hashes
+local fontdata=hashes.identifiers
+local parameters=fonts.hashes.parameters
+local resources=fonts.hashes.resources
nodes.injections=nodes.injections or {}
local injections=nodes.injections
+local tracers=nodes.tracers
+local setcolor=tracers and tracers.colors.set
+local resetcolor=tracers and tracers.colors.reset
local nodecodes=nodes.nodecodes
local glyph_code=nodecodes.glyph
local disc_code=nodecodes.disc
local kern_code=nodecodes.kern
+local glue_code=nodecodes.glue
local nuts=nodes.nuts
local nodepool=nuts.pool
local newkern=nodepool.kern
@@ -10282,7 +16363,12 @@ local getid=nuts.getid
local getfont=nuts.getfont
local getsubtype=nuts.getsubtype
local getchar=nuts.getchar
+local getboth=nuts.getboth
+local ischar=nuts.is_char
+local getdisc=nuts.getdisc
+local setdisc=nuts.setdisc
local traverse_id=nuts.traverse_id
+local traverse_char=nuts.traverse_char
local insert_node_before=nuts.insert_before
local insert_node_after=nuts.insert_after
local find_tail=nuts.tail
@@ -10317,7 +16403,7 @@ function injections.copy(target,source)
local sp=rawget(properties,source)
if sp then
local tp=rawget(properties,target)
- local si=rawget(sp,"injections")
+ local si=sp.injections
if si then
si=fastcopy(si)
if tp then
@@ -10344,7 +16430,7 @@ end
function injections.setligaindex(n,index)
local p=rawget(properties,n)
if p then
- local i=rawget(p,"injections")
+ local i=p.injections
if i then
i.ligaindex=index
else
@@ -10363,7 +16449,7 @@ end
function injections.getligaindex(n,default)
local p=rawget(properties,n)
if p then
- local i=rawget(p,"injections")
+ local i=p.injections
if i then
return i.ligaindex or default
end
@@ -10381,9 +16467,12 @@ function injections.setcursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmne
else
dx=dx-ws
end
+ if dx==0 then
+ dx=0
+ end
local p=rawget(properties,start)
if p then
- local i=rawget(p,"injections")
+ local i=p.injections
if i then
i.cursiveanchor=true
else
@@ -10400,7 +16489,7 @@ function injections.setcursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmne
end
local p=rawget(properties,nxt)
if p then
- local i=rawget(p,"injections")
+ local i=p.injections
if i then
i.cursivex=dx
i.cursivey=dy
@@ -10518,7 +16607,7 @@ function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase,mkmk)
end
local p=rawget(properties,start)
if p then
- local i=rawget(p,"injections")
+ local i=p.injections
if i then
if i.markmark then
else
@@ -10622,10 +16711,9 @@ local function trace(head,where)
show(n,"preinjections",false,"<")
show(n,"postinjections",false,">")
show(n,"replaceinjections",false,"=")
+ show(n,"emptyinjections",false,"*")
elseif id==disc_code then
- local pre=getfield(n,"pre")
- local post=getfield(n,"post")
- local replace=getfield(n,"replace")
+ local pre,post,replace=getdisc(n)
if pre then
showsub(pre,"preinjections","pre")
end
@@ -10635,6 +16723,7 @@ local function trace(head,where)
if replace then
showsub(replace,"replaceinjections","replace")
end
+ show(n,"emptyinjections",false,"*")
end
n=getnext(n)
end
@@ -10659,421 +16748,372 @@ local function show_result(head)
current=getnext(current)
end
end
-local function collect_glyphs(head,offsets)
- local glyphs,glyphi,nofglyphs={},{},0
- local marks,marki,nofmarks={},{},0
- local nf,tm=nil,nil
- local n=head
- local function identify(n,what)
- local f=getfont(n)
- if f~=nf then
- nf=f
- tm=fontdata[nf].resources
- if tm then
- tm=tm.marks
- end
- end
- if tm and tm[getchar(n)] then
- nofmarks=nofmarks+1
- marks[nofmarks]=n
- marki[nofmarks]="injections"
- else
- nofglyphs=nofglyphs+1
- glyphs[nofglyphs]=n
- glyphi[nofglyphs]=what
- end
- if offsets then
- local p=rawget(properties,n)
- if p then
- local i=rawget(p,what)
- if i then
- local yoffset=i.yoffset
- if yoffset and yoffset~=0 then
- setfield(n,"yoffset",yoffset)
- end
- end
- end
- end
+local function inject_kerns_only(head,where)
+ head=tonut(head)
+ if trace_injections then
+ trace(head,"kerns")
end
- while n do
- local id=getid(n)
+ local current=head
+ local prev=nil
+ local next=nil
+ local prevdisc=nil
+ local prevglyph=nil
+ local pre=nil
+ local post=nil
+ local replace=nil
+ local pretail=nil
+ local posttail=nil
+ local replacetail=nil
+ while current do
+ local id=getid(current)
+ local next=getnext(current)
if id==glyph_code then
- identify(n,"injections")
- elseif id==disc_code then
- local d=getfield(n,"pre")
- if d then
- for n in traverse_id(glyph_code,d) do
- if getsubtype(n)<256 then
- identify(n,"preinjections")
- end
- end
- end
- local d=getfield(n,"post")
- if d then
- for n in traverse_id(glyph_code,d) do
- if getsubtype(n)<256 then
- identify(n,"postinjections")
- end
- end
- end
- local d=getfield(n,"replace")
- if d then
- for n in traverse_id(glyph_code,d) do
- if getsubtype(n)<256 then
- identify(n,"replaceinjections")
- end
- end
- end
- end
- n=getnext(n)
- end
- return glyphs,glyphi,nofglyphs,marks,marki,nofmarks
-end
-local function inject_marks(marks,marki,nofmarks)
- for i=1,nofmarks do
- local n=marks[i]
- local pn=rawget(properties,n)
- if pn then
- local ni=marki[i]
- local pn=rawget(pn,ni)
- if pn then
- local p=pn.markbasenode
+ if getsubtype(current)<256 then
+ local p=rawget(properties,current)
if p then
- local px=getfield(p,"xoffset")
- local ox=0
- local rightkern=nil
- local pp=rawget(properties,p)
- if pp then
- pp=rawget(pp,ni)
- if pp then
- rightkern=pp.rightkern
+ local i=p.injections
+ if i then
+ local leftkern=i.leftkern
+ if leftkern and leftkern~=0 then
+ if use_advance then
+ setfield(current,"xoffset",leftkern)
+ setfield(current,"xadvance",leftkern)
+ else
+ insert_node_before(head,current,newkern(leftkern))
+ end
end
end
- if rightkern then
- if pn.markdir<0 then
- ox=px-pn.markx-rightkern
+ if prevdisc then
+ local done=false
+ if post then
+ local i=p.postinjections
+ if i then
+ local leftkern=i.leftkern
+ if leftkern and leftkern~=0 then
+ if use_advance then
+ setfield(post,"xadvance",leftkern)
+ else
+ insert_node_after(post,posttail,newkern(leftkern))
+ done=true
+ end
+ end
+ end
+ end
+ if replace then
+ local i=p.replaceinjections
+ if i then
+ local leftkern=i.leftkern
+ if leftkern and leftkern~=0 then
+ if use_advance then
+ setfield(replace,"xadvance",leftkern)
+ else
+ insert_node_after(replace,replacetail,newkern(leftkern))
+ done=true
+ end
+ end
+ end
else
-
-
- if false then
- local leftkern=pp.leftkern
- if leftkern then
- ox=px-pn.markx-leftkern
- else
- ox=px-pn.markx
+ local i=p.emptyinjections
+ if i then
+ local leftkern=i.leftkern
+ if leftkern and leftkern~=0 then
+ setfield(prev,"replace",newkern(leftkern))
end
- else
- ox=px-pn.markx
end
end
- else
- ox=px-pn.markx
- local wn=getfield(n,"width")
- if wn~=0 then
- pn.leftkern=-wn/2
- pn.rightkern=-wn/2
+ if done then
+ setdisc(prevdisc,pre,post,replace)
end
end
- setfield(n,"xoffset",ox)
- local py=getfield(p,"yoffset")
- local oy=getfield(n,"yoffset")+py+pn.marky
- setfield(n,"yoffset",oy)
- else
end
end
- end
- end
-end
-local function inject_cursives(glyphs,glyphi,nofglyphs)
- local cursiveanchor,lastanchor=nil,nil
- local minc,maxc,last=0,0,nil
- for i=1,nofglyphs do
- local n=glyphs[i]
- local pn=rawget(properties,n)
- if pn then
- pn=rawget(pn,glyphi[i])
- end
- if pn then
- local cursivex=pn.cursivex
- if cursivex then
- if cursiveanchor then
- if cursivex~=0 then
- pn.leftkern=(pn.leftkern or 0)+cursivex
- end
- if lastanchor then
- if maxc==0 then
- minc=lastanchor
+ prevdisc=nil
+ prevglyph=current
+ elseif id==disc_code then
+ pre,post,replace,pretail,posttail,replacetail=getdisc(current,true)
+ local done=false
+ if pre then
+ for n in traverse_char(pre) do
+ local p=rawget(properties,n)
+ if p then
+ local i=p.injections or p.preinjections
+ if i then
+ local leftkern=i.leftkern
+ if leftkern and leftkern~=0 then
+ if use_advance then
+ setfield(pre,"xoffset",leftkern)
+ setfield(pre,"xadvance",leftkern)
+ else
+ pre=insert_node_before(pre,n,newkern(leftkern))
+ done=true
+ end
+ end
end
- maxc=lastanchor
- properties[cursiveanchor].cursivedy=pn.cursivey
end
- last=n
- else
- maxc=0
end
- elseif maxc>0 then
- local ny=getfield(n,"yoffset")
- for i=maxc,minc,-1 do
- local ti=glyphs[i]
- ny=ny+properties[ti].cursivedy
- setfield(ti,"yoffset",ny)
+ end
+ if post then
+ for n in traverse_char(post) do
+ local p=rawget(properties,n)
+ if p then
+ local i=p.injections or p.postinjections
+ if i then
+ local leftkern=i.leftkern
+ if leftkern and leftkern~=0 then
+ if use_advance then
+ setfield(post,"xoffset",leftkern)
+ setfield(post,"xadvance",leftkern)
+ else
+ post=insert_node_before(post,n,newkern(leftkern))
+ done=true
+ end
+ end
+ end
+ end
end
- maxc=0
end
- if pn.cursiveanchor then
- cursiveanchor=n
- lastanchor=i
- else
- cursiveanchor=nil
- lastanchor=nil
- if maxc>0 then
- local ny=getfield(n,"yoffset")
- for i=maxc,minc,-1 do
- local ti=glyphs[i]
- ny=ny+properties[ti].cursivedy
- setfield(ti,"yoffset",ny)
+ if replace then
+ for n in traverse_char(replace) do
+ local p=rawget(properties,n)
+ if p then
+ local i=p.injections or p.replaceinjections
+ if i then
+ local leftkern=i.leftkern
+ if leftkern and leftkern~=0 then
+ if use_advance then
+ setfield(replace,"xoffset",leftkern)
+ setfield(replace,"xadvance",leftkern)
+ else
+ replace=insert_node_before(replace,n,newkern(leftkern))
+ done=true
+ end
+ end
+ end
end
- maxc=0
end
end
- elseif maxc>0 then
- local ny=getfield(n,"yoffset")
- for i=maxc,minc,-1 do
- local ti=glyphs[i]
- ny=ny+properties[ti].cursivedy
- setfield(ti,"yoffset",getfield(ti,"yoffset")+ny)
+ if done then
+ setdisc(current,pre,post,replace)
end
- maxc=0
- cursiveanchor=nil
- lastanchor=nil
- end
- end
- if last and maxc>0 then
- local ny=getfield(last,"yoffset")
- for i=maxc,minc,-1 do
- local ti=glyphs[i]
- ny=ny+properties[ti].cursivedy
- setfield(ti,"yoffset",ny)
- end
- end
-end
-local function inject_kerns(head,glist,ilist,length)
- for i=1,length do
- local n=glist[i]
- local pn=rawget(properties,n)
- if pn then
- local dp=nil
- local dr=nil
- local ni=ilist[i]
- local p=nil
- if ni=="injections" then
- p=getprev(n)
- if p then
- local id=getid(p)
- if id==disc_code then
- dp=getfield(p,"post")
- dr=getfield(p,"replace")
- end
- end
- end
- if dp then
- local i=rawget(pn,"postinjections")
- if i then
- local leftkern=i.leftkern
- if leftkern and leftkern~=0 then
- local t=find_tail(dp)
- insert_node_after(dp,t,newkern(leftkern))
- setfield(p,"post",dp)
- end
- end
- end
- if dr then
- local i=rawget(pn,"replaceinjections")
- if i then
- local leftkern=i.leftkern
- if leftkern and leftkern~=0 then
- local t=find_tail(dr)
- insert_node_after(dr,t,newkern(leftkern))
- setfield(p,"replace",dr)
- end
- end
- else
- local i=rawget(pn,ni)
- if i then
- local leftkern=i.leftkern
- if leftkern and leftkern~=0 then
- insert_node_before(head,n,newkern(leftkern))
- end
- local rightkern=i.rightkern
- if rightkern and rightkern~=0 then
- insert_node_after(head,n,newkern(rightkern))
- end
- end
- end
- end
- end
-end
-local function inject_everything(head,where)
- head=tonut(head)
- if trace_injections then
- trace(head,"everything")
- end
- local glyphs,glyphi,nofglyphs,marks,marki,nofmarks=collect_glyphs(head,nofregisteredpairs>0)
- if nofglyphs>0 then
- if nofregisteredcursives>0 then
- inject_cursives(glyphs,glyphi,nofglyphs)
- end
- if nofregisteredmarks>0 then
- inject_marks(marks,marki,nofmarks)
+ prevglyph=nil
+ prevdisc=current
+ else
+ prevglyph=nil
+ prevdisc=nil
end
- inject_kerns(head,glyphs,glyphi,nofglyphs)
+ prev=current
+ current=next
end
- if nofmarks>0 then
- inject_kerns(head,marks,marki,nofmarks)
- end
if keepregisteredcounts then
keepregisteredcounts=false
else
nofregisteredkerns=0
- nofregisteredpairs=0
- nofregisteredmarks=0
- nofregisteredcursives=0
end
return tonode(head),true
end
-local function inject_kerns_only(head,where)
+local function inject_pairs_only(head,where)
head=tonut(head)
if trace_injections then
- trace(head,"kerns")
+ trace(head,"pairs")
end
- local n=head
- local p=nil
- while n do
- local id=getid(n)
+ local current=head
+ local prev=nil
+ local next=nil
+ local prevdisc=nil
+ local prevglyph=nil
+ local pre=nil
+ local post=nil
+ local replace=nil
+ local pretail=nil
+ local posttail=nil
+ local replacetail=nil
+ while current do
+ local id=getid(current)
+ local next=getnext(current)
if id==glyph_code then
- if getsubtype(n)<256 then
- local pn=rawget(properties,n)
- if pn then
- if p then
- local d=getfield(p,"post")
- if d then
- local i=rawget(pn,"postinjections")
+ if getsubtype(current)<256 then
+ local p=rawget(properties,current)
+ if p then
+ local i=p.injections
+ if i then
+ local yoffset=i.yoffset
+ if yoffset and yoffset~=0 then
+ setfield(current,"yoffset",yoffset)
+ end
+ local leftkern=i.leftkern
+ if leftkern and leftkern~=0 then
+ insert_node_before(head,current,newkern(leftkern))
+ end
+ local rightkern=i.rightkern
+ if rightkern and rightkern~=0 then
+ insert_node_after(head,current,newkern(rightkern))
+ end
+ else
+ local i=p.emptyinjections
+ if i then
+ local rightkern=i.rightkern
+ if rightkern and rightkern~=0 then
+ if next and getid(next)==disc_code then
+ if replace then
+ else
+ setfield(next,"replace",newkern(rightkern))
+ end
+ end
+ end
+ end
+ end
+ if prevdisc then
+ local done=false
+ if post then
+ local i=p.postinjections
if i then
local leftkern=i.leftkern
if leftkern and leftkern~=0 then
- local t=find_tail(d)
- insert_node_after(d,t,newkern(leftkern))
- setfield(p,"post",d)
+ insert_node_after(post,posttail,newkern(leftkern))
+ done=true
end
end
end
- local d=getfield(p,"replace")
- if d then
- local i=rawget(pn,"replaceinjections")
+ if replace then
+ local i=p.replaceinjections
if i then
local leftkern=i.leftkern
if leftkern and leftkern~=0 then
- local t=find_tail(d)
- insert_node_after(d,t,newkern(leftkern))
- setfield(p,"replace",d)
+ insert_node_after(replace,replacetail,newkern(leftkern))
+ done=true
end
end
else
- local i=rawget(pn,"injections")
+ local i=p.emptyinjections
if i then
local leftkern=i.leftkern
if leftkern and leftkern~=0 then
- setfield(p,"replace",newkern(leftkern))
+ setfield(prev,"replace",newkern(leftkern))
end
end
end
- else
- local i=rawget(pn,"injections")
+ if done then
+ setdisc(prevdisc,pre,post,replace)
+ end
+ end
+ end
+ end
+ prevdisc=nil
+ prevglyph=current
+ elseif id==disc_code then
+ pre,post,replace,pretail,posttail,replacetail=getdisc(current,true)
+ local done=false
+ if pre then
+ for n in traverse_char(pre) do
+ local p=rawget(properties,n)
+ if p then
+ local i=p.injections or p.preinjections
if i then
+ local yoffset=i.yoffset
+ if yoffset and yoffset~=0 then
+ setfield(n,"yoffset",yoffset)
+ end
local leftkern=i.leftkern
if leftkern and leftkern~=0 then
- head=insert_node_before(head,n,newkern(leftkern))
+ pre=insert_node_before(pre,n,newkern(leftkern))
+ done=true
+ end
+ local rightkern=i.rightkern
+ if rightkern and rightkern~=0 then
+ insert_node_after(pre,n,newkern(rightkern))
+ done=true
end
end
end
end
end
- p=nil
- elseif id==disc_code then
- local d=getfield(n,"pre")
- if d then
- local h=d
- for n in traverse_id(glyph_code,d) do
- if getsubtype(n)<256 then
- local pn=rawget(properties,n)
- if pn then
- local i=rawget(pn,"preinjections")
- if i then
- local leftkern=i.leftkern
- if leftkern and leftkern~=0 then
- h=insert_node_before(h,n,newkern(leftkern))
- end
+ if post then
+ for n in traverse_char(post) do
+ local p=rawget(properties,n)
+ if p then
+ local i=p.injections or p.postinjections
+ if i then
+ local yoffset=i.yoffset
+ if yoffset and yoffset~=0 then
+ setfield(n,"yoffset",yoffset)
+ end
+ local leftkern=i.leftkern
+ if leftkern and leftkern~=0 then
+ post=insert_node_before(post,n,newkern(leftkern))
+ done=true
+ end
+ local rightkern=i.rightkern
+ if rightkern and rightkern~=0 then
+ insert_node_after(post,n,newkern(rightkern))
+ done=true
end
end
- else
- break
end
end
- if h~=d then
- setfield(n,"pre",h)
- end
end
- local d=getfield(n,"post")
- if d then
- local h=d
- for n in traverse_id(glyph_code,d) do
- if getsubtype(n)<256 then
- local pn=rawget(properties,n)
- if pn then
- local i=rawget(pn,"postinjections")
- if i then
- local leftkern=i.leftkern
- if leftkern and leftkern~=0 then
- h=insert_node_before(h,n,newkern(leftkern))
- end
+ if replace then
+ for n in traverse_char(replace) do
+ local p=rawget(properties,n)
+ if p then
+ local i=p.injections or p.replaceinjections
+ if i then
+ local yoffset=i.yoffset
+ if yoffset and yoffset~=0 then
+ setfield(n,"yoffset",yoffset)
+ end
+ local leftkern=i.leftkern
+ if leftkern and leftkern~=0 then
+ replace=insert_node_before(replace,n,newkern(leftkern))
+ done=true
+ end
+ local rightkern=i.rightkern
+ if rightkern and rightkern~=0 then
+ insert_node_after(replace,n,newkern(rightkern))
+ done=true
end
end
- else
- break
end
end
- if h~=d then
- setfield(n,"post",h)
- end
end
- local d=getfield(n,"replace")
- if d then
- local h=d
- for n in traverse_id(glyph_code,d) do
- if getsubtype(n)<256 then
- local pn=rawget(properties,n)
- if pn then
- local i=rawget(pn,"replaceinjections")
- if i then
- local leftkern=i.leftkern
- if leftkern and leftkern~=0 then
- h=insert_node_before(h,n,newkern(leftkern))
- end
+ if prevglyph then
+ if pre then
+ local p=rawget(properties,prevglyph)
+ if p then
+ local i=p.preinjections
+ if i then
+ local rightkern=i.rightkern
+ if rightkern and rightkern~=0 then
+ pre=insert_node_before(pre,pre,newkern(rightkern))
+ done=true
end
end
- else
- break
end
end
- if h~=d then
- setfield(n,"replace",h)
+ if replace then
+ local p=rawget(properties,prevglyph)
+ if p then
+ local i=p.replaceinjections
+ if i then
+ local rightkern=i.rightkern
+ if rightkern and rightkern~=0 then
+ replace=insert_node_before(replace,replace,newkern(rightkern))
+ done=true
+ end
+ end
+ end
end
end
- p=n
+ if done then
+ setdisc(current,pre,post,replace)
+ end
+ prevglyph=nil
+ prevdisc=current
else
- p=nil
+ prevglyph=nil
+ prevdisc=nil
end
- n=getnext(n)
+ prev=current
+ current=next
end
if keepregisteredcounts then
keepregisteredcounts=false
@@ -11082,185 +17122,484 @@ local function inject_kerns_only(head,where)
end
return tonode(head),true
end
-local function inject_pairs_only(head,where)
+local function showoffset(n,flag)
+ local o=getfield(n,"xoffset")
+ if o==0 then
+ o=getfield(n,"yoffset")
+ end
+ if o~=0 then
+ setcolor(n,flag and "darkred" or "darkgreen")
+ else
+ resetcolor(n)
+ end
+end
+local function inject_everything(head,where)
head=tonut(head)
if trace_injections then
- trace(head,"pairs")
+ trace(head,"everything")
end
- local n=head
- local p=nil
- while n do
- local id=getid(n)
+ local hascursives=nofregisteredcursives>0
+ local hasmarks=nofregisteredmarks>0
+ local current=head
+ local last=nil
+ local font=font
+ local markdata=nil
+ local prev=nil
+ local next=nil
+ local prevdisc=nil
+ local prevglyph=nil
+ local pre=nil
+ local post=nil
+ local replace=nil
+ local pretail=nil
+ local posttail=nil
+ local replacetail=nil
+ local cursiveanchor=nil
+ local minc=0
+ local maxc=0
+ local glyphs={}
+ local marks={}
+ local nofmarks=0
+ local function processmark(p,n,pn)
+ local px=getfield(p,"xoffset")
+ local ox=0
+ local rightkern=nil
+ local pp=rawget(properties,p)
+ if pp then
+ pp=pp.injections
+ if pp then
+ rightkern=pp.rightkern
+ end
+ end
+ if rightkern then
+ if pn.markdir<0 then
+ ox=px-pn.markx-rightkern
+ else
+ if false then
+ local leftkern=pp.leftkern
+ if leftkern then
+ ox=px-pn.markx-leftkern
+ else
+ ox=px-pn.markx
+ end
+ else
+ ox=px-pn.markx
+ end
+ end
+ else
+ ox=px-pn.markx
+ local wn=getfield(n,"width")
+ if wn~=0 then
+ pn.leftkern=-wn/2
+ pn.rightkern=-wn/2
+ end
+ end
+ local oy=getfield(n,"yoffset")+getfield(p,"yoffset")+pn.marky
+ setfield(n,"xoffset",ox)
+ setfield(n,"yoffset",oy)
+ if trace_marks then
+ showoffset(n,true)
+ end
+ end
+ while current do
+ local id=getid(current)
+ local next=getnext(current)
if id==glyph_code then
- if getsubtype(n)<256 then
- local pn=rawget(properties,n)
- if pn then
- if p then
- local d=getfield(p,"post")
- if d then
- local i=rawget(pn,"postinjections")
- if i then
- local leftkern=i.leftkern
- if leftkern and leftkern~=0 then
- local t=find_tail(d)
- insert_node_after(d,t,newkern(leftkern))
- setfield(p,"post",d)
+ if getsubtype(current)<256 then
+ local p=rawget(properties,current)
+ if p then
+ local i=p.injections
+ if i then
+ local pm=i.markbasenode
+ if pm then
+ nofmarks=nofmarks+1
+ marks[nofmarks]=current
+ else
+ if hascursives then
+ local cursivex=i.cursivex
+ if cursivex then
+ if cursiveanchor then
+ if cursivex~=0 then
+ i.leftkern=(i.leftkern or 0)+cursivex
+ end
+ if maxc==0 then
+ minc=1
+ maxc=1
+ glyphs[1]=cursiveanchor
+ else
+ maxc=maxc+1
+ glyphs[maxc]=cursiveanchor
+ end
+ properties[cursiveanchor].cursivedy=i.cursivey
+ last=current
+ else
+ maxc=0
+ end
+ elseif maxc>0 then
+ local ny=getfield(current,"yoffset")
+ for i=maxc,minc,-1 do
+ local ti=glyphs[i]
+ ny=ny+properties[ti].cursivedy
+ setfield(ti,"yoffset",ny)
+ if trace_cursive then
+ showoffset(ti)
+ end
+ end
+ maxc=0
+ cursiveanchor=nil
+ end
+ if i.cursiveanchor then
+ cursiveanchor=current
+ else
+ if maxc>0 then
+ local ny=getfield(current,"yoffset")
+ for i=maxc,minc,-1 do
+ local ti=glyphs[i]
+ ny=ny+properties[ti].cursivedy
+ setfield(ti,"yoffset",ny)
+ if trace_cursive then
+ showoffset(ti)
+ end
+ end
+ maxc=0
+ end
+ cursiveanchor=nil
+ end
+ end
+ local yoffset=i.yoffset
+ if yoffset and yoffset~=0 then
+ setfield(current,"yoffset",yoffset)
+ end
+ local leftkern=i.leftkern
+ if leftkern and leftkern~=0 then
+ insert_node_before(head,current,newkern(leftkern))
+ end
+ local rightkern=i.rightkern
+ if rightkern and rightkern~=0 then
+ insert_node_after(head,current,newkern(rightkern))
+ end
+ end
+ else
+ local i=p.emptyinjections
+ if i then
+ local rightkern=i.rightkern
+ if rightkern and rightkern~=0 then
+ if next and getid(next)==disc_code then
+ if replace then
+ else
+ setfield(next,"replace",newkern(rightkern))
+ end
end
end
end
- local d=getfield(p,"replace")
- if d then
- local i=rawget(pn,"replaceinjections")
- if i then
- local leftkern=i.leftkern
- if leftkern and leftkern~=0 then
- local t=find_tail(d)
- insert_node_after(d,t,newkern(leftkern))
- setfield(p,"replace",d)
+ end
+ if prevdisc then
+ if p then
+ local done=false
+ if post then
+ local i=p.postinjections
+ if i then
+ local leftkern=i.leftkern
+ if leftkern and leftkern~=0 then
+ insert_node_after(post,posttail,newkern(leftkern))
+ done=true
+ end
end
end
- else
- local i=rawget(pn,"injections")
- if i then
+ if replace then
+ local i=p.replaceinjections
+ if i then
+ local leftkern=i.leftkern
+ if leftkern and leftkern~=0 then
+ insert_node_after(replace,replacetail,newkern(leftkern))
+ done=true
+ end
+ end
+ else
+ local i=p.emptyinjections
local leftkern=i.leftkern
if leftkern and leftkern~=0 then
- setfield(p,"replace",newkern(leftkern))
+ setfield(prev,"replace",newkern(leftkern))
end
end
+ if done then
+ setdisc(prevdisc,pre,post,replace)
+ end
end
- else
- local i=rawget(pn,"injections")
+ end
+ else
+ if hascursives and maxc>0 then
+ local ny=getfield(current,"yoffset")
+ for i=maxc,minc,-1 do
+ local ti=glyphs[i]
+ ny=ny+properties[ti].cursivedy
+ setfield(ti,"yoffset",getfield(ti,"yoffset")+ny)
+ end
+ maxc=0
+ cursiveanchor=nil
+ end
+ end
+ end
+ prevdisc=nil
+ prevglyph=current
+ elseif id==disc_code then
+ pre,post,replace,pretail,posttail,replacetail=getdisc(current,true)
+ local done=false
+ if pre then
+ for n in traverse_char(pre) do
+ local p=rawget(properties,n)
+ if p then
+ local i=p.injections or p.preinjections
if i then
+ local yoffset=i.yoffset
+ if yoffset and yoffset~=0 then
+ setfield(n,"yoffset",yoffset)
+ end
local leftkern=i.leftkern
if leftkern and leftkern~=0 then
- head=insert_node_before(head,n,newkern(leftkern))
+ pre=insert_node_before(pre,n,newkern(leftkern))
+ done=true
end
local rightkern=i.rightkern
if rightkern and rightkern~=0 then
- insert_node_after(head,n,newkern(rightkern))
- n=getnext(n)
+ insert_node_after(pre,n,newkern(rightkern))
+ done=true
end
+ end
+ if hasmarks then
+ local pm=i.markbasenode
+ if pm then
+ processmark(pm,current,i)
+ end
+ end
+ end
+ end
+ end
+ if post then
+ for n in traverse_char(post) do
+ local p=rawget(properties,n)
+ if p then
+ local i=p.injections or p.postinjections
+ if i then
local yoffset=i.yoffset
if yoffset and yoffset~=0 then
setfield(n,"yoffset",yoffset)
end
+ local leftkern=i.leftkern
+ if leftkern and leftkern~=0 then
+ post=insert_node_before(post,n,newkern(leftkern))
+ done=true
+ end
+ local rightkern=i.rightkern
+ if rightkern and rightkern~=0 then
+ insert_node_after(post,n,newkern(rightkern))
+ done=true
+ end
+ end
+ if hasmarks then
+ local pm=i.markbasenode
+ if pm then
+ processmark(pm,current,i)
+ end
end
end
end
end
- p=nil
- elseif id==disc_code then
- local d=getfield(n,"pre")
- if d then
- local h=d
- for n in traverse_id(glyph_code,d) do
- if getsubtype(n)<256 then
- local pn=rawget(properties,n)
- if pn then
- local i=rawget(pn,"preinjections")
- if i then
- local leftkern=i.leftkern
- if leftkern and leftkern~=0 then
- h=insert_node_before(h,n,newkern(leftkern))
- end
- local rightkern=i.rightkern
- if rightkern and rightkern~=0 then
- insert_node_after(head,n,newkern(rightkern))
- n=getnext(n)
- end
- local yoffset=i.yoffset
- if yoffset and yoffset~=0 then
- setfield(n,"yoffset",yoffset)
- end
+ if replace then
+ for n in traverse_char(replace) do
+ local p=rawget(properties,n)
+ if p then
+ local i=p.injections or p.replaceinjections
+ if i then
+ local yoffset=i.yoffset
+ if yoffset and yoffset~=0 then
+ setfield(n,"yoffset",yoffset)
+ end
+ local leftkern=i.leftkern
+ if leftkern and leftkern~=0 then
+ replace=insert_node_before(replace,n,newkern(leftkern))
+ done=true
+ end
+ local rightkern=i.rightkern
+ if rightkern and rightkern~=0 then
+ insert_node_after(replace,n,newkern(rightkern))
+ done=true
+ end
+ end
+ if hasmarks then
+ local pm=i.markbasenode
+ if pm then
+ processmark(pm,current,i)
end
end
- else
- break
end
end
- if h~=d then
- setfield(n,"pre",h)
- end
end
- local d=getfield(n,"post")
- if d then
- local h=d
- for n in traverse_id(glyph_code,d) do
- if getsubtype(n)<256 then
- local pn=rawget(properties,n)
- if pn then
- local i=rawget(pn,"postinjections")
- if i then
- local leftkern=i.leftkern
- if leftkern and leftkern~=0 then
- h=insert_node_before(h,n,newkern(leftkern))
- end
- local rightkern=i.rightkern
- if rightkern and rightkern~=0 then
- insert_node_after(head,n,newkern(rightkern))
- n=getnext(n)
- end
- local yoffset=i.yoffset
- if yoffset and yoffset~=0 then
- setfield(n,"yoffset",yoffset)
- end
+ if prevglyph then
+ if pre then
+ local p=rawget(properties,prevglyph)
+ if p then
+ local i=p.preinjections
+ if i then
+ local rightkern=i.rightkern
+ if rightkern and rightkern~=0 then
+ pre=insert_node_before(pre,pre,newkern(rightkern))
+ done=true
end
end
- else
- break
end
end
- if h~=d then
- setfield(n,"post",h)
- end
- end
- local d=getfield(n,"replace")
- if d then
- local h=d
- for n in traverse_id(glyph_code,d) do
- if getsubtype(n)<256 then
- local pn=rawget(properties,n)
- if pn then
- local i=rawget(pn,"replaceinjections")
- if i then
- local leftkern=i.leftkern
- if leftkern and leftkern~=0 then
- h=insert_node_before(h,n,newkern(leftkern))
- end
- local rightkern=i.rightkern
- if rightkern and rightkern~=0 then
- insert_node_after(head,n,newkern(rightkern))
- n=getnext(n)
- end
- local yoffset=i.yoffset
- if yoffset and yoffset~=0 then
- setfield(n,"yoffset",yoffset)
- end
+ if replace then
+ local p=rawget(properties,prevglyph)
+ if p then
+ local i=p.replaceinjections
+ if i then
+ local rightkern=i.rightkern
+ if rightkern and rightkern~=0 then
+ replace=insert_node_before(replace,replace,newkern(rightkern))
+ done=true
end
end
- else
- break
end
end
- if h~=d then
- setfield(n,"replace",h)
- end
end
- p=n
+ if done then
+ setdisc(current,pre,post,replace)
+ end
+ prevglyph=nil
+ prevdisc=current
else
- p=nil
+ prevglyph=nil
+ prevdisc=nil
end
- n=getnext(n)
+ prev=current
+ current=next
+ end
+ if hascursives and maxc>0 then
+ local ny=getfield(last,"yoffset")
+ for i=maxc,minc,-1 do
+ local ti=glyphs[i]
+ ny=ny+properties[ti].cursivedy
+ setfield(ti,"yoffset",ny)
+ if trace_cursive then
+ showoffset(ti)
+ end
+ end
+ end
+ if nofmarks>0 then
+ for i=1,nofmarks do
+ local m=marks[i]
+ local p=rawget(properties,m)
+ local i=p.injections
+ local b=i.markbasenode
+ processmark(b,m,i)
+ end
+ elseif hasmarks then
end
if keepregisteredcounts then
keepregisteredcounts=false
else
- nofregisteredpairs=0
nofregisteredkerns=0
+ nofregisteredpairs=0
+ nofregisteredmarks=0
+ nofregisteredcursives=0
end
return tonode(head),true
end
+local triggers=false
+function nodes.injections.setspacekerns(font,sequence)
+ if triggers then
+ triggers[font]=sequence
+ else
+ triggers={ [font]=sequence }
+ end
+end
+local function injectspaces(head)
+ if not triggers then
+ return head,false
+ end
+ local lastfont=nil
+ local spacekerns=nil
+ local leftkerns=nil
+ local rightkerns=nil
+ local factor=0
+ local threshold=0
+ local leftkern=false
+ local rightkern=false
+ local function updatefont(font,trig)
+ leftkerns=trig.left
+ rightkerns=trig.right
+ local par=parameters[font]
+ factor=par.factor
+ threshold=par.spacing.width-1
+ lastfont=font
+ end
+ for n in traverse_id(glue_code,tonut(head)) do
+ local prev,next=getboth(n)
+ local prevchar=ischar(prev)
+ local nextchar=ischar(next)
+ if nextchar then
+ local font=getfont(next)
+ local trig=triggers[font]
+ if trig then
+ if lastfont~=font then
+ updatefont(font,trig)
+ end
+ if rightkerns then
+ rightkern=rightkerns[nextchar]
+ end
+ end
+ end
+ if prevchar then
+ local font=getfont(next)
+ local trig=triggers[font]
+ if trig then
+ if lastfont~=font then
+ updatefont(font,trig)
+ end
+ if leftkerns then
+ leftkern=leftkerns[prevchar]
+ end
+ end
+ end
+ if leftkern then
+ local old=getfield(n,"width")
+ if old>=threshold then
+ if rightkern then
+ local new=old+(leftkern+rightkern)*factor
+ if trace_spaces then
+ report_spaces("%C [%p -> %p] %C",prevchar,old,new,nextchar)
+ end
+ setfield(n,"width",new)
+ leftkern=false
+ else
+ local new=old+leftkern*factor
+ if trace_spaces then
+ report_spaces("%C [%p -> %p]",prevchar,old,new)
+ end
+ setfield(n,"width",new)
+ end
+ end
+ leftkern=false
+ elseif rightkern then
+ local old=getfield(n,"width")
+ if old>=threshold then
+ local new=old+rightkern*factor
+ if trace_spaces then
+ report_spaces("[%p -> %p] %C",nextchar,old,new)
+ end
+ setfield(n,"width",new)
+ end
+ rightkern=false
+ end
+ end
+ triggers=false
+ return head,true
+end
function injections.handler(head,where)
+ if triggers then
+ head=injectspaces(head)
+ end
if nofregisteredmarks>0 or nofregisteredcursives>0 then
return inject_everything(head,where)
elseif nofregisteredpairs>0 then
@@ -11276,7 +17615,7 @@ end -- closure
do -- begin closure to overcome local limits and interference
-if not modules then modules={} end modules ['luatex-fonts-ota']={
+if not modules then modules={} end modules ['font-ota']={
version=1.001,
comment="companion to font-otf.lua (analysing)",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
@@ -11299,21 +17638,22 @@ local tonut=nuts.tonut
local getfield=nuts.getfield
local getnext=nuts.getnext
local getprev=nuts.getprev
-local getid=nuts.getid
+local getprev=nuts.getprev
local getprop=nuts.getprop
local setprop=nuts.setprop
local getfont=nuts.getfont
local getsubtype=nuts.getsubtype
local getchar=nuts.getchar
+local ischar=nuts.is_char
local traverse_id=nuts.traverse_id
local traverse_node_list=nuts.traverse
local end_of_math=nuts.end_of_math
local nodecodes=nodes.nodecodes
-local glyph_code=nodecodes.glyph
local disc_code=nodecodes.disc
local math_code=nodecodes.math
local fontdata=fonts.hashes.identifiers
local categories=characters and characters.categories or {}
+local chardata=characters and characters.data
local otffeatures=fonts.constructors.newfeatures("otf")
local registerotffeature=otffeatures.register
local s_init=1 local s_rphf=7
@@ -11325,7 +17665,10 @@ local s_rest=6
local states={
init=s_init,
medi=s_medi,
+ med2=s_medi,
fina=s_fina,
+ fin2=s_fina,
+ fin3=s_fina,
isol=s_isol,
mark=s_mark,
rest=s_rest,
@@ -11338,7 +17681,10 @@ local states={
local features={
init=s_init,
medi=s_medi,
+ med2=s_medi,
fina=s_fina,
+ fin2=s_fina,
+ fin3=s_fina,
isol=s_isol,
rphf=s_rphf,
half=s_half,
@@ -11356,10 +17702,9 @@ function analyzers.setstate(head,font)
local first,last,current,n,done=nil,nil,head,0,false
current=tonut(current)
while current do
- local id=getid(current)
- if id==glyph_code and getfont(current)==font then
+ local char,id=ischar(current,font)
+ if char and not getprop(current,a_state) then
done=true
- local char=getchar(current)
local d=descriptions[char]
if d then
if d.class=="mark" then
@@ -11383,6 +17728,16 @@ function analyzers.setstate(head,font)
end
first,last,n=nil,nil,0
end
+ elseif char==false then
+ if first and first==last then
+ setprop(last,a_state,s_isol)
+ elseif last then
+ setprop(last,a_state,s_fina)
+ end
+ first,last,n=nil,nil,0
+ if id==math_code then
+ current=end_of_math(current)
+ end
elseif id==disc_code then
setprop(current,a_state,s_medi)
last=current
@@ -11447,107 +17802,6 @@ registerotffeature {
}
}
methods.latn=analyzers.setstate
-local tatweel=0x0640
-local zwnj=0x200C
-local zwj=0x200D
-local isolated={
- [0x0600]=true,[0x0601]=true,[0x0602]=true,[0x0603]=true,
- [0x0604]=true,
- [0x0608]=true,[0x060B]=true,[0x0621]=true,[0x0674]=true,
- [0x06DD]=true,
- [0x0856]=true,[0x0858]=true,[0x0857]=true,
- [0x07FA]=true,
- [zwnj]=true,
- [0x08AD]=true,
-}
-local final={
- [0x0622]=true,[0x0623]=true,[0x0624]=true,[0x0625]=true,
- [0x0627]=true,[0x0629]=true,[0x062F]=true,[0x0630]=true,
- [0x0631]=true,[0x0632]=true,[0x0648]=true,[0x0671]=true,
- [0x0672]=true,[0x0673]=true,[0x0675]=true,[0x0676]=true,
- [0x0677]=true,[0x0688]=true,[0x0689]=true,[0x068A]=true,
- [0x068B]=true,[0x068C]=true,[0x068D]=true,[0x068E]=true,
- [0x068F]=true,[0x0690]=true,[0x0691]=true,[0x0692]=true,
- [0x0693]=true,[0x0694]=true,[0x0695]=true,[0x0696]=true,
- [0x0697]=true,[0x0698]=true,[0x0699]=true,[0x06C0]=true,
- [0x06C3]=true,[0x06C4]=true,[0x06C5]=true,[0x06C6]=true,
- [0x06C7]=true,[0x06C8]=true,[0x06C9]=true,[0x06CA]=true,
- [0x06CB]=true,[0x06CD]=true,[0x06CF]=true,[0x06D2]=true,
- [0x06D3]=true,[0x06D5]=true,[0x06EE]=true,[0x06EF]=true,
- [0x0759]=true,[0x075A]=true,[0x075B]=true,[0x076B]=true,
- [0x076C]=true,[0x0771]=true,[0x0773]=true,[0x0774]=true,
- [0x0778]=true,[0x0779]=true,
- [0x08AA]=true,[0x08AB]=true,[0x08AC]=true,
- [0xFEF5]=true,[0xFEF7]=true,[0xFEF9]=true,[0xFEFB]=true,
- [0x0710]=true,[0x0715]=true,[0x0716]=true,[0x0717]=true,
- [0x0718]=true,[0x0719]=true,[0x0728]=true,[0x072A]=true,
- [0x072C]=true,[0x071E]=true,
- [0x072F]=true,[0x074D]=true,
- [0x0840]=true,[0x0849]=true,[0x0854]=true,[0x0846]=true,
- [0x084F]=true,
- [0x08AE]=true,[0x08B1]=true,[0x08B2]=true,
-}
-local medial={
- [0x0626]=true,[0x0628]=true,[0x062A]=true,[0x062B]=true,
- [0x062C]=true,[0x062D]=true,[0x062E]=true,[0x0633]=true,
- [0x0634]=true,[0x0635]=true,[0x0636]=true,[0x0637]=true,
- [0x0638]=true,[0x0639]=true,[0x063A]=true,[0x063B]=true,
- [0x063C]=true,[0x063D]=true,[0x063E]=true,[0x063F]=true,
- [0x0641]=true,[0x0642]=true,[0x0643]=true,
- [0x0644]=true,[0x0645]=true,[0x0646]=true,[0x0647]=true,
- [0x0649]=true,[0x064A]=true,[0x066E]=true,[0x066F]=true,
- [0x0678]=true,[0x0679]=true,[0x067A]=true,[0x067B]=true,
- [0x067C]=true,[0x067D]=true,[0x067E]=true,[0x067F]=true,
- [0x0680]=true,[0x0681]=true,[0x0682]=true,[0x0683]=true,
- [0x0684]=true,[0x0685]=true,[0x0686]=true,[0x0687]=true,
- [0x069A]=true,[0x069B]=true,[0x069C]=true,[0x069D]=true,
- [0x069E]=true,[0x069F]=true,[0x06A0]=true,[0x06A1]=true,
- [0x06A2]=true,[0x06A3]=true,[0x06A4]=true,[0x06A5]=true,
- [0x06A6]=true,[0x06A7]=true,[0x06A8]=true,[0x06A9]=true,
- [0x06AA]=true,[0x06AB]=true,[0x06AC]=true,[0x06AD]=true,
- [0x06AE]=true,[0x06AF]=true,[0x06B0]=true,[0x06B1]=true,
- [0x06B2]=true,[0x06B3]=true,[0x06B4]=true,[0x06B5]=true,
- [0x06B6]=true,[0x06B7]=true,[0x06B8]=true,[0x06B9]=true,
- [0x06BA]=true,[0x06BB]=true,[0x06BC]=true,[0x06BD]=true,
- [0x06BE]=true,[0x06BF]=true,[0x06C1]=true,[0x06C2]=true,
- [0x06CC]=true,[0x06CE]=true,[0x06D0]=true,[0x06D1]=true,
- [0x06FA]=true,[0x06FB]=true,[0x06FC]=true,[0x06FF]=true,
- [0x0750]=true,[0x0751]=true,[0x0752]=true,[0x0753]=true,
- [0x0754]=true,[0x0755]=true,[0x0756]=true,[0x0757]=true,
- [0x0758]=true,[0x075C]=true,[0x075D]=true,[0x075E]=true,
- [0x075F]=true,[0x0760]=true,[0x0761]=true,[0x0762]=true,
- [0x0763]=true,[0x0764]=true,[0x0765]=true,[0x0766]=true,
- [0x0767]=true,[0x0768]=true,[0x0769]=true,[0x076A]=true,
- [0x076D]=true,[0x076E]=true,[0x076F]=true,[0x0770]=true,
- [0x0772]=true,[0x0775]=true,[0x0776]=true,[0x0777]=true,
- [0x077A]=true,[0x077B]=true,[0x077C]=true,[0x077D]=true,
- [0x077E]=true,[0x077F]=true,
- [0x08A0]=true,[0x08A2]=true,[0x08A4]=true,[0x08A5]=true,
- [0x08A6]=true,[0x0620]=true,[0x08A8]=true,[0x08A9]=true,
- [0x08A7]=true,[0x08A3]=true,
- [0x0712]=true,[0x0713]=true,[0x0714]=true,[0x071A]=true,
- [0x071B]=true,[0x071C]=true,[0x071D]=true,[0x071F]=true,
- [0x0720]=true,[0x0721]=true,[0x0722]=true,[0x0723]=true,
- [0x0724]=true,[0x0725]=true,[0x0726]=true,[0x0727]=true,
- [0x0729]=true,[0x072B]=true,[0x072D]=true,[0x072E]=true,
- [0x074E]=true,[0x074F]=true,
- [0x0841]=true,[0x0842]=true,[0x0843]=true,[0x0844]=true,
- [0x0845]=true,[0x0847]=true,[0x0848]=true,[0x0855]=true,
- [0x0851]=true,[0x084E]=true,[0x084D]=true,[0x084A]=true,
- [0x084B]=true,[0x084C]=true,[0x0850]=true,[0x0852]=true,
- [0x0853]=true,
- [0x07D7]=true,[0x07E8]=true,[0x07D9]=true,[0x07EA]=true,
- [0x07CA]=true,[0x07DB]=true,[0x07CC]=true,[0x07DD]=true,
- [0x07CE]=true,[0x07DF]=true,[0x07D4]=true,[0x07E5]=true,
- [0x07E9]=true,[0x07E7]=true,[0x07E3]=true,[0x07E2]=true,
- [0x07E0]=true,[0x07E1]=true,[0x07DE]=true,[0x07DC]=true,
- [0x07D1]=true,[0x07DA]=true,[0x07D8]=true,[0x07D6]=true,
- [0x07D2]=true,[0x07D0]=true,[0x07CF]=true,[0x07CD]=true,
- [0x07CB]=true,[0x07D3]=true,[0x07E4]=true,[0x07D5]=true,
- [0x07E6]=true,
- [tatweel]=true,[zwj]=true,
- [0x08A1]=true,[0x08AF]=true,[0x08B0]=true,
-}
local arab_warned={}
local function warning(current,what)
local char=getchar(current)
@@ -11556,92 +17810,173 @@ local function warning(current,what)
arab_warned[char]=true
end
end
-local function finish(first,last)
- if last then
- if first==last then
- local fc=getchar(first)
- if medial[fc] or final[fc] then
- setprop(first,a_state,s_isol)
- else
- warning(first,"isol")
- setprop(first,a_state,s_error)
- end
- else
- local lc=getchar(last)
- if medial[lc] or final[lc] then
- setprop(last,a_state,s_fina)
- else
- warning(last,"fina")
- setprop(last,a_state,s_error)
+local mappers={
+ l=s_init,
+ d=s_medi,
+ c=s_medi,
+ r=s_fina,
+ u=s_isol,
+}
+local classifiers=characters.classifiers
+if not classifiers then
+ local first_arabic,last_arabic=characters.blockrange("arabic")
+ local first_syriac,last_syriac=characters.blockrange("syriac")
+ local first_mandiac,last_mandiac=characters.blockrange("mandiac")
+ local first_nko,last_nko=characters.blockrange("nko")
+ classifiers=table.setmetatableindex(function(t,k)
+ local c=chardata[k]
+ local v=false
+ if c then
+ local arabic=c.arabic
+ if arabic then
+ v=mappers[arabic]
+ if not v then
+ log.report("analyze","error in mapping arabic %C",k)
+ v=false
+ end
+ elseif k>=first_arabic and k<=last_arabic or k>=first_syriac and k<=last_syriac or
+ k>=first_mandiac and k<=last_mandiac or k>=first_nko and k<=last_nko then
+ if categories[k]=="mn" then
+ v=s_mark
+ else
+ v=s_rest
+ end
end
end
- first,last=nil,nil
- elseif first then
- local fc=getchar(first)
- if medial[fc] or final[fc] then
- setprop(first,a_state,s_isol)
- else
- warning(first,"isol")
- setprop(first,a_state,s_error)
- end
- first=nil
- end
- return first,last
+ t[k]=v
+ return v
+ end)
end
function methods.arab(head,font,attr)
- local useunicodemarks=analyzers.useunicodemarks
- local tfmdata=fontdata[font]
- local marks=tfmdata.resources.marks
- local first,last,current,done=nil,nil,head,false
+ local first,last=nil,nil
+ local c_first,c_last=nil,nil
+ local current,done=head,false
current=tonut(current)
while current do
- local id=getid(current)
- if id==glyph_code and getfont(current)==font and getsubtype(current)<256 and not getprop(current,a_state) then
+ local char,id=ischar(current,font)
+ if char and not getprop(current,a_state) then
done=true
- local char=getchar(current)
- if marks[char] or (useunicodemarks and categories[char]=="mn") then
+ local classifier=classifiers[char]
+ if not classifier then
+ if last then
+ if c_last==s_medi or c_last==s_fina then
+ setprop(last,a_state,s_fina)
+ else
+ warning(last,"fina")
+ setprop(last,a_state,s_error)
+ end
+ first,last=nil,nil
+ elseif first then
+ if c_first==s_medi or c_first==s_fina then
+ setprop(first,a_state,s_isol)
+ else
+ warning(first,"isol")
+ setprop(first,a_state,s_error)
+ end
+ first=nil
+ end
+ elseif classifier==s_mark then
setprop(current,a_state,s_mark)
- elseif isolated[char] then
- first,last=finish(first,last)
+ elseif classifier==s_isol then
+ if last then
+ if c_last==s_medi or c_last==s_fina then
+ setprop(last,a_state,s_fina)
+ else
+ warning(last,"fina")
+ setprop(last,a_state,s_error)
+ end
+ first,last=nil,nil
+ elseif first then
+ if c_first==s_medi or c_first==s_fina then
+ setprop(first,a_state,s_isol)
+ else
+ warning(first,"isol")
+ setprop(first,a_state,s_error)
+ end
+ first=nil
+ end
setprop(current,a_state,s_isol)
- first,last=nil,nil
- elseif not first then
- if medial[char] then
+ elseif classifier==s_medi then
+ if first then
+ last=current
+ c_last=classifier
+ setprop(current,a_state,s_medi)
+ else
setprop(current,a_state,s_init)
- first,last=first or current,current
- elseif final[char] then
- setprop(current,a_state,s_isol)
- first,last=nil,nil
- else
- first,last=finish(first,last)
+ first=current
+ c_first=classifier
end
- elseif medial[char] then
- first,last=first or current,current
- setprop(current,a_state,s_medi)
- elseif final[char] then
- if getprop(last,a_state)~=s_init then
- setprop(last,a_state,s_medi)
+ elseif classifier==s_fina then
+ if last then
+ if getprop(last,a_state)~=s_init then
+ setprop(last,a_state,s_medi)
+ end
+ setprop(current,a_state,s_fina)
+ first,last=nil,nil
+ elseif first then
+ setprop(current,a_state,s_fina)
+ first=nil
+ else
+ setprop(current,a_state,s_isol)
end
- setprop(current,a_state,s_fina)
- first,last=nil,nil
- elseif char>=0x0600 and char<=0x06FF then
- setprop(current,a_state,s_rest)
- first,last=finish(first,last)
else
- first,last=finish(first,last)
+ setprop(current,a_state,s_rest)
+ if last then
+ if c_last==s_medi or c_last==s_fina then
+ setprop(last,a_state,s_fina)
+ else
+ warning(last,"fina")
+ setprop(last,a_state,s_error)
+ end
+ first,last=nil,nil
+ elseif first then
+ if c_first==s_medi or c_first==s_fina then
+ setprop(first,a_state,s_isol)
+ else
+ warning(first,"isol")
+ setprop(first,a_state,s_error)
+ end
+ first=nil
+ end
end
else
- if first or last then
- first,last=finish(first,last)
+ if last then
+ if c_last==s_medi or c_last==s_fina then
+ setprop(last,a_state,s_fina)
+ else
+ warning(last,"fina")
+ setprop(last,a_state,s_error)
+ end
+ first,last=nil,nil
+ elseif first then
+ if c_first==s_medi or c_first==s_fina then
+ setprop(first,a_state,s_isol)
+ else
+ warning(first,"isol")
+ setprop(first,a_state,s_error)
+ end
+ first=nil
end
- if id==math_code then
+ if id==math_code then
current=end_of_math(current)
end
end
current=getnext(current)
end
- if first or last then
- finish(first,last)
+ if last then
+ if c_last==s_medi or c_last==s_fina then
+ setprop(last,a_state,s_fina)
+ else
+ warning(last,"fina")
+ setprop(last,a_state,s_error)
+ end
+ elseif first then
+ if c_first==s_medi or c_first==s_fina then
+ setprop(first,a_state,s_isol)
+ else
+ warning(first,"isol")
+ setprop(first,a_state,s_error)
+ end
end
return head,done
end
@@ -11656,7 +17991,7 @@ end -- closure
do -- begin closure to overcome local limits and interference
-if not modules then modules={} end modules ['font-otn']={
+if not modules then modules={} end modules ['font-ots']={
version=1.001,
comment="companion to font-ini.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
@@ -11666,6 +18001,7 @@ if not modules then modules={} end modules ['font-otn']={
local type,next,tonumber=type,next,tonumber
local random=math.random
local formatters=string.formatters
+local insert=table.insert
local logs,trackers,nodes,attributes=logs,trackers,nodes,attributes
local registertracker=trackers.register
local registerdirective=directives.register
@@ -11690,19 +18026,19 @@ local trace_directions=false registertracker("otf.directions",function(v) trace_
local trace_kernruns=false registertracker("otf.kernruns",function(v) trace_kernruns=v end)
local trace_discruns=false registertracker("otf.discruns",function(v) trace_discruns=v end)
local trace_compruns=false registertracker("otf.compruns",function(v) trace_compruns=v end)
+local trace_testruns=false registertracker("otf.testruns",function(v) trace_testruns=v end)
local quit_on_no_replacement=true
local zwnjruns=true
+local optimizekerns=true
registerdirective("otf.zwnjruns",function(v) zwnjruns=v end)
registerdirective("otf.chain.quitonnoreplacement",function(value) quit_on_no_replacement=value end)
local report_direct=logs.reporter("fonts","otf direct")
local report_subchain=logs.reporter("fonts","otf subchain")
local report_chain=logs.reporter("fonts","otf chain")
local report_process=logs.reporter("fonts","otf process")
-local report_prepare=logs.reporter("fonts","otf prepare")
local report_warning=logs.reporter("fonts","otf warning")
local report_run=logs.reporter("fonts","otf run")
-registertracker("otf.verbose_chain",function(v) otf.setcontextchain(v and "verbose") end)
-registertracker("otf.normal_chain",function(v) otf.setcontextchain(v and "normal") end)
+local report_check=logs.reporter("fonts","otf check")
registertracker("otf.replacements","otf.singles,otf.multiples,otf.alternatives,otf.ligatures")
registertracker("otf.positions","otf.marks,otf.kerns,otf.cursive")
registertracker("otf.actions","otf.replacements,otf.positions")
@@ -11717,6 +18053,8 @@ local getnext=nuts.getnext
local setnext=nuts.setnext
local getprev=nuts.getprev
local setprev=nuts.setprev
+local getboth=nuts.getboth
+local setboth=nuts.setboth
local getid=nuts.getid
local getattr=nuts.getattr
local setattr=nuts.setattr
@@ -11727,6 +18065,10 @@ local getsubtype=nuts.getsubtype
local setsubtype=nuts.setsubtype
local getchar=nuts.getchar
local setchar=nuts.setchar
+local getdisc=nuts.getdisc
+local setdisc=nuts.setdisc
+local setlink=nuts.setlink
+local ischar=nuts.is_char
local insert_node_before=nuts.insert_before
local insert_node_after=nuts.insert_after
local delete_node=nuts.delete
@@ -11757,7 +18099,6 @@ local discretionary_code=disccodes.discretionary
local ligature_code=glyphcodes.ligature
local privateattribute=attributes.private
local a_state=privateattribute('state')
-local a_cursbase=privateattribute('cursbase')
local injections=nodes.injections
local setmark=injections.setmark
local setcursive=injections.setcursive
@@ -11777,23 +18118,26 @@ otf.defaultnodealternate="none"
local tfmdata=false
local characters=false
local descriptions=false
-local resources=false
local marks=false
local currentfont=false
-local lookuptable=false
-local anchorlookups=false
-local lookuptypes=false
-local lookuptags=false
-local handlers={}
-local rlmode=0
-local featurevalue=false
-local sweephead={}
+local factor=0
+local threshold=0
local sweepnode=nil
local sweepprev=nil
local sweepnext=nil
+local sweephead={}
local notmatchpre={}
local notmatchpost={}
local notmatchreplace={}
+local handlers={}
+local function isspace(n)
+ if getid(n)==glue_code then
+ local w=getfield(n,"width")
+ if w>=threshold then
+ return 32
+ end
+ end
+end
local checkstep=(nodes and nodes.tracers and nodes.tracers.steppers.check) or function() end
local registerstep=(nodes and nodes.tracers and nodes.tracers.steppers.register) or function() end
local registermessage=(nodes and nodes.tracers and nodes.tracers.steppers.message) or function() end
@@ -11833,21 +18177,26 @@ local function gref(n)
return "<error in node mode tracing>"
end
end
-local function cref(kind,chainname,chainlookupname,lookupname,index)
- if index then
- return formatters["feature %a, chain %a, sub %a, lookup %a, index %a"](kind,chainname,chainlookupname,lookuptags[lookupname],index)
- elseif lookupname then
- return formatters["feature %a, chain %a, sub %a, lookup %a"](kind,chainname,chainlookupname,lookuptags[lookupname])
- elseif chainlookupname then
- return formatters["feature %a, chain %a, sub %a"](kind,lookuptags[chainname],lookuptags[chainlookupname])
- elseif chainname then
- return formatters["feature %a, chain %a"](kind,lookuptags[chainname])
+local function cref(dataset,sequence,index)
+ if not dataset then
+ return "no valid dataset"
+ elseif index then
+ return formatters["feature %a, type %a, chain lookup %a, index %a"](dataset[4],sequence.type,sequence.name,index)
else
- return formatters["feature %a"](kind)
+ return formatters["feature %a, type %a, chain lookup %a"](dataset[4],sequence.type,sequence.name)
end
end
-local function pref(kind,lookupname)
- return formatters["feature %a, lookup %a"](kind,lookuptags[lookupname])
+local function pref(dataset,sequence)
+ return formatters["feature %a, type %a, lookup %a"](dataset[4],sequence.type,sequence.name)
+end
+local function mref(rlmode)
+ if not rlmode or rlmode==0 then
+ return "---"
+ elseif rlmode==-1 or rlmode=="+TRT" then
+ return "r2l"
+ else
+ return "l2r"
+ end
end
local function copy_glyph(g)
local components=getfield(g,"components")
@@ -11864,16 +18213,14 @@ local function copy_glyph(g)
end
end
local function flattendisk(head,disc)
- local replace=getfield(disc,"replace")
+ local _,_,replace,_,_,replacetail=getdisc(disc,true)
setfield(disc,"replace",nil)
free_node(disc)
if head==disc then
local next=getnext(disc)
if replace then
if next then
- local tail=find_node_tail(replace)
- setnext(tail,next)
- setprev(next,tail)
+ setlink(replacetail,next)
end
return replace,replace
elseif next then
@@ -11882,47 +18229,36 @@ local function flattendisk(head,disc)
return
end
else
- local next=getnext(disc)
- local prev=getprev(disc)
+ local prev,next=getboth(disc)
if replace then
- local tail=find_node_tail(replace)
if next then
- setnext(tail,next)
- setprev(next,tail)
+ setlink(replacetail,next)
end
- setnext(prev,replace)
- setprev(replace,prev)
+ setlink(prev,replace)
return head,replace
else
- if next then
- setprev(next,prev)
- end
- setnext(prev,next)
+ setlink(prev,next)
return head,next
end
end
end
local function appenddisc(disc,list)
- local post=getfield(disc,"post")
- local replace=getfield(disc,"replace")
- local phead=list
- local rhead=copy_node_list(list)
- local ptail=find_node_tail(post)
- local rtail=find_node_tail(replace)
+ local pre,post,replace,pretail,posttail,replacetail=getdisc(disc,true)
+ local posthead=list
+ local replacehead=copy_node_list(list)
if post then
- setnext(ptail,phead)
- setprev(phead,ptail)
+ setlink(posttail,posthead)
else
- setfield(disc,"post",phead)
+ post=phead
end
if replace then
- setnext(rtail,rhead)
- setprev(rhead,rtail)
+ setlink(replacetail,replacehead)
else
- setfield(disc,"replace",rhead)
+ replace=rhead
end
+ setdisc(disc,pre,post,replace)
end
-local function markstoligature(kind,lookupname,head,start,stop,char)
+local function markstoligature(head,start,stop,char)
if start==stop and getchar(start)==char then
return head,start
else
@@ -11938,14 +18274,8 @@ local function markstoligature(kind,lookupname,head,start,stop,char)
setchar(base,char)
setsubtype(base,ligature_code)
setfield(base,"components",start)
- if prev then
- setnext(prev,base)
- end
- if next then
- setprev(next,base)
- end
- setnext(base,next)
- setprev(base,prev)
+ setlink(prev,base)
+ setlink(base,next)
return head,base
end
end
@@ -11967,7 +18297,7 @@ local function getcomponentindex(start)
end
end
local a_noligature=attributes.private("noligature")
-local function toligature(kind,lookupname,head,start,stop,char,markflag,discfound)
+local function toligature(head,start,stop,char,dataset,sequence,markflag,discfound)
if getattr(start,a_noligature)==1 then
return head,start
end
@@ -11998,8 +18328,7 @@ local function toligature(kind,lookupname,head,start,stop,char,markflag,discfoun
if next then
setprev(next,base)
end
- setprev(base,prev)
- setnext(base,next)
+ setboth(base,prev,next)
if not discfound then
local deletemarks=markflag~="mark"
local components=start
@@ -12015,65 +18344,57 @@ local function toligature(kind,lookupname,head,start,stop,char,markflag,discfoun
elseif not deletemarks then
setligaindex(start,baseindex+getligaindex(start,componentindex))
if trace_marks then
- logwarning("%s: keep mark %s, gets index %s",pref(kind,lookupname),gref(char),getligaindex(start))
+ logwarning("%s: keep mark %s, gets index %s",pref(dataset,sequence),gref(char),getligaindex(start))
end
local n=copy_node(start)
copyinjection(n,start)
head,current=insert_node_after(head,current,n)
elseif trace_marks then
- logwarning("%s: delete mark %s",pref(kind,lookupname),gref(char))
+ logwarning("%s: delete mark %s",pref(dataset,sequence),gref(char))
end
start=getnext(start)
end
local start=getnext(current)
- while start and getid(start)==glyph_code do
- local char=getchar(start)
- if marks[char] then
- setligaindex(start,baseindex+getligaindex(start,componentindex))
- if trace_marks then
- logwarning("%s: set mark %s, gets index %s",pref(kind,lookupname),gref(char),getligaindex(start))
+ while start do
+ local char=ischar(start)
+ if char then
+ if marks[char] then
+ setligaindex(start,baseindex+getligaindex(start,componentindex))
+ if trace_marks then
+ logwarning("%s: set mark %s, gets index %s",pref(dataset,sequence),gref(char),getligaindex(start))
+ end
+ start=getnext(start)
+ else
+ break
end
else
break
end
- start=getnext(start)
end
else
- local discprev=getprev(discfound)
- local discnext=getnext(discfound)
+ local discprev,discnext=getboth(discfound)
if discprev and discnext then
- local pre=getfield(discfound,"pre")
- local post=getfield(discfound,"post")
- local replace=getfield(discfound,"replace")
+ local pre,post,replace,pretail,posttail,replacetail=getdisc(discfound,true)
if not replace then
local prev=getprev(base)
local copied=copy_node_list(comp)
setprev(discnext,nil)
setnext(discprev,nil)
if pre then
- setnext(discprev,pre)
- setprev(pre,discprev)
+ setlink(discprev,pre)
end
pre=comp
if post then
- local tail=find_node_tail(post)
- setnext(tail,discnext)
- setprev(discnext,tail)
+ setlink(posttail,discnext)
setprev(post,nil)
else
post=discnext
end
- setnext(prev,discfound)
- setprev(discfound,prev)
- setnext(discfound,next)
- setprev(next,discfound)
- setnext(base,nil)
- setprev(base,nil)
+ setlink(prev,discfound)
+ setlink(discfound,next)
+ setboth(base,nil,nil)
setfield(base,"components",copied)
- setfield(discfound,"pre",pre)
- setfield(discfound,"post",post)
- setfield(discfound,"replace",base)
- setsubtype(discfound,discretionary_code)
+ setdisc(discfound,pre,post,base,discretionary_code)
base=prev
end
end
@@ -12091,12 +18412,7 @@ local function multiple_glyphs(head,start,multiple,ignoremarks)
local n=copy_node(start)
resetinjection(n)
setchar(n,multiple[k])
- setprev(n,start)
- setnext(n,sn)
- if sn then
- setprev(sn,n)
- end
- setnext(start,n)
+ insert_node_after(head,start,n)
start=n
end
end
@@ -12108,7 +18424,7 @@ local function multiple_glyphs(head,start,multiple,ignoremarks)
return head,start,false
end
end
-local function get_alternative_glyph(start,alternatives,value,trace_alternatives)
+local function get_alternative_glyph(start,alternatives,value)
local n=#alternatives
if value=="random" then
local r=random(1,n)
@@ -12117,70 +18433,73 @@ local function get_alternative_glyph(start,alternatives,value,trace_alternatives
return alternatives[1],trace_alternatives and formatters["value %a, taking %a"](value,1)
elseif value=="last" then
return alternatives[n],trace_alternatives and formatters["value %a, taking %a"](value,n)
- else
- value=tonumber(value)
- if type(value)~="number" then
- return alternatives[1],trace_alternatives and formatters["invalid value %s, taking %a"](value,1)
- elseif value>n then
- local defaultalt=otf.defaultnodealternate
- if defaultalt=="first" then
- return alternatives[n],trace_alternatives and formatters["invalid value %s, taking %a"](value,1)
- elseif defaultalt=="last" then
- return alternatives[1],trace_alternatives and formatters["invalid value %s, taking %a"](value,n)
- else
- return false,trace_alternatives and formatters["invalid value %a, %s"](value,"out of range")
- end
- elseif value==0 then
- return getchar(start),trace_alternatives and formatters["invalid value %a, %s"](value,"no change")
- elseif value<1 then
- return alternatives[1],trace_alternatives and formatters["invalid value %a, taking %a"](value,1)
+ end
+ value=value==true and 1 or tonumber(value)
+ if type(value)~="number" then
+ return alternatives[1],trace_alternatives and formatters["invalid value %s, taking %a"](value,1)
+ end
+ if value>n then
+ local defaultalt=otf.defaultnodealternate
+ if defaultalt=="first" then
+ return alternatives[n],trace_alternatives and formatters["invalid value %s, taking %a"](value,1)
+ elseif defaultalt=="last" then
+ return alternatives[1],trace_alternatives and formatters["invalid value %s, taking %a"](value,n)
else
- return alternatives[value],trace_alternatives and formatters["value %a, taking %a"](value,value)
+ return false,trace_alternatives and formatters["invalid value %a, %s"](value,"out of range")
end
+ elseif value==0 then
+ return getchar(start),trace_alternatives and formatters["invalid value %a, %s"](value,"no change")
+ elseif value<1 then
+ return alternatives[1],trace_alternatives and formatters["invalid value %a, taking %a"](value,1)
+ else
+ return alternatives[value],trace_alternatives and formatters["value %a, taking %a"](value,value)
end
end
-function handlers.gsub_single(head,start,kind,lookupname,replacement)
+function handlers.gsub_single(head,start,dataset,sequence,replacement)
if trace_singles then
- logprocess("%s: replacing %s by single %s",pref(kind,lookupname),gref(getchar(start)),gref(replacement))
+ logprocess("%s: replacing %s by single %s",pref(dataset,sequence),gref(getchar(start)),gref(replacement))
end
resetinjection(start)
setchar(start,replacement)
return head,start,true
end
-function handlers.gsub_alternate(head,start,kind,lookupname,alternative,sequence)
- local value=featurevalue==true and tfmdata.shared.features[kind] or featurevalue
- local choice,comment=get_alternative_glyph(start,alternative,value,trace_alternatives)
+function handlers.gsub_alternate(head,start,dataset,sequence,alternative)
+ local kind=dataset[4]
+ local what=dataset[1]
+ local value=what==true and tfmdata.shared.features[kind] or what
+ local choice,comment=get_alternative_glyph(start,alternative,value)
if choice then
if trace_alternatives then
- logprocess("%s: replacing %s by alternative %a to %s, %s",pref(kind,lookupname),gref(getchar(start)),choice,gref(choice),comment)
+ logprocess("%s: replacing %s by alternative %a to %s, %s",pref(dataset,sequence),gref(getchar(start)),gref(choice),comment)
end
resetinjection(start)
setchar(start,choice)
else
if trace_alternatives then
- logwarning("%s: no variant %a for %s, %s",pref(kind,lookupname),value,gref(getchar(start)),comment)
+ logwarning("%s: no variant %a for %s, %s",pref(dataset,sequence),value,gref(getchar(start)),comment)
end
end
return head,start,true
end
-function handlers.gsub_multiple(head,start,kind,lookupname,multiple,sequence)
+function handlers.gsub_multiple(head,start,dataset,sequence,multiple)
if trace_multiples then
- logprocess("%s: replacing %s by multiple %s",pref(kind,lookupname),gref(getchar(start)),gref(multiple))
+ logprocess("%s: replacing %s by multiple %s",pref(dataset,sequence),gref(getchar(start)),gref(multiple))
end
return multiple_glyphs(head,start,multiple,sequence.flags[1])
end
-function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence)
- local s,stop=getnext(start),nil
+function handlers.gsub_ligature(head,start,dataset,sequence,ligature)
+ local current=getnext(start)
+ local stop=nil
local startchar=getchar(start)
if marks[startchar] then
- while s do
- local id=getid(s)
- if id==glyph_code and getfont(s)==currentfont and getsubtype(s)<256 then
- local lg=ligature[getchar(s)]
+ while current do
+ local char=ischar(current,currentfont)
+ if char then
+ local lg=ligature[char]
if lg then
- stop=s
+ stop=current
ligature=lg
- s=getnext(s)
+ current=getnext(current)
else
break
end
@@ -12193,10 +18512,10 @@ function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence)
if lig then
if trace_ligatures then
local stopchar=getchar(stop)
- head,start=markstoligature(kind,lookupname,head,start,stop,lig)
- logprocess("%s: replacing %s upto %s by ligature %s case 1",pref(kind,lookupname),gref(startchar),gref(stopchar),gref(getchar(start)))
+ head,start=markstoligature(head,start,stop,lig)
+ logprocess("%s: replacing %s upto %s by ligature %s case 1",pref(dataset,sequence),gref(startchar),gref(stopchar),gref(getchar(start)))
else
- head,start=markstoligature(kind,lookupname,head,start,stop,lig)
+ head,start=markstoligature(head,start,stop,lig)
end
return head,start,true,false
else
@@ -12206,52 +18525,49 @@ function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence)
local skipmark=sequence.flags[1]
local discfound=false
local lastdisc=nil
- while s do
- local id=getid(s)
- if id==glyph_code and getsubtype(s)<256 then
- if getfont(s)==currentfont then
- local char=getchar(s)
- if skipmark and marks[char] then
- s=getnext(s)
- else
- local lg=ligature[char]
- if lg then
- if not discfound and lastdisc then
- discfound=lastdisc
- lastdisc=nil
- end
- stop=s
- ligature=lg
- s=getnext(s)
- else
- break
+ while current do
+ local char,id=ischar(current,currentfont)
+ if char then
+ if skipmark and marks[char] then
+ current=getnext(current)
+ else
+ local lg=ligature[char]
+ if lg then
+ if not discfound and lastdisc then
+ discfound=lastdisc
+ lastdisc=nil
end
+ stop=current
+ ligature=lg
+ current=getnext(current)
+ else
+ break
end
- else
- break
end
+ elseif char==false then
+ break
elseif id==disc_code then
- lastdisc=s
- s=getnext(s)
+ lastdisc=current
+ current=getnext(current)
else
break
end
end
- local lig=ligature.ligature
+ local lig=ligature.ligature
if lig then
if stop then
if trace_ligatures then
local stopchar=getchar(stop)
- head,start=toligature(kind,lookupname,head,start,stop,lig,skipmark,discfound)
- logprocess("%s: replacing %s upto %s by ligature %s case 2",pref(kind,lookupname),gref(startchar),gref(stopchar),gref(getchar(start)))
+ head,start=toligature(head,start,stop,lig,dataset,sequence,skipmark,discfound)
+ logprocess("%s: replacing %s upto %s by ligature %s case 2",pref(dataset,sequence),gref(startchar),gref(stopchar),gref(lig))
else
- head,start=toligature(kind,lookupname,head,start,stop,lig,skipmark,discfound)
+ head,start=toligature(head,start,stop,lig,dataset,sequence,skipmark,discfound)
end
else
resetinjection(start)
setchar(start,lig)
if trace_ligatures then
- logprocess("%s: replacing %s by (no real) ligature %s case 3",pref(kind,lookupname),gref(startchar),gref(lig))
+ logprocess("%s: replacing %s by (no real) ligature %s case 3",pref(dataset,sequence),gref(startchar),gref(lig))
end
end
return head,start,true,discfound
@@ -12260,190 +18576,197 @@ function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence)
end
return head,start,false,discfound
end
-function handlers.gpos_single(head,start,kind,lookupname,kerns,sequence,injection)
+function handlers.gpos_single(head,start,dataset,sequence,kerns,rlmode,step,i,injection)
local startchar=getchar(start)
- local dx,dy,w,h=setpair(start,tfmdata.parameters.factor,rlmode,sequence.flags[4],kerns,injection)
- if trace_kerns then
- logprocess("%s: shifting single %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),dx,dy,w,h)
+ if step.format=="pair" then
+ local dx,dy,w,h=setpair(start,factor,rlmode,sequence.flags[4],kerns,injection)
+ if trace_kerns then
+ logprocess("%s: shifting single %s by (%p,%p) and correction (%p,%p)",pref(dataset,sequence),gref(startchar),dx,dy,w,h)
+ end
+ else
+ local k=setkern(start,factor,rlmode,kerns,injection)
+ if trace_kerns then
+ logprocess("%s: shifting single %s by %p",pref(dataset,sequence),gref(startchar),k)
+ end
end
return head,start,false
end
-function handlers.gpos_pair(head,start,kind,lookupname,kerns,sequence,lookuphash,i,injection)
+function handlers.gpos_pair(head,start,dataset,sequence,kerns,rlmode,step,i,injection)
local snext=getnext(start)
if not snext then
return head,start,false
else
local prev=start
local done=false
- local factor=tfmdata.parameters.factor
- local lookuptype=lookuptypes[lookupname]
- while snext and getid(snext)==glyph_code and getfont(snext)==currentfont and getsubtype(snext)<256 do
- local nextchar=getchar(snext)
- local krn=kerns[nextchar]
- if not krn and marks[nextchar] then
- prev=snext
- snext=getnext(snext)
- else
- if not krn then
- elseif type(krn)=="table" then
- if lookuptype=="pair" then
- local a,b=krn[2],krn[3]
- if a and #a>0 then
- local x,y,w,h=setpair(start,factor,rlmode,sequence.flags[4],a,injection)
+ while snext do
+ local nextchar=ischar(snext,currentfont)
+ if nextchar then
+ local krn=kerns[nextchar]
+ if not krn and marks[nextchar] then
+ prev=snext
+ snext=getnext(snext)
+ elseif not krn then
+ break
+ elseif step.format=="pair" then
+ local a,b=krn[1],krn[2]
+ if optimizekerns then
+ if not b and a[1]==0 and a[2]==0 and a[4]==0 then
+ local k=setkern(snext,factor,rlmode,a[3],injection)
if trace_kerns then
- local startchar=getchar(start)
- logprocess("%s: shifting first of pair %s and %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h)
+ logprocess("%s: shifting single %s by %p",pref(dataset,sequence),gref(nextchar),k)
end
+ done=true
+ break
end
- if b and #b>0 then
- local x,y,w,h=setpair(snext,factor,rlmode,sequence.flags[4],b,injection)
- if trace_kerns then
- local startchar=getchar(start)
- logprocess("%s: shifting second of pair %s and %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h)
- end
+ end
+ if a and #a>0 then
+ local x,y,w,h=setpair(start,factor,rlmode,sequence.flags[4],a,injection)
+ if trace_kerns then
+ local startchar=getchar(start)
+ logprocess("%s: shifting first of pair %s and %s by (%p,%p) and correction (%p,%p) as %s",pref(dataset,sequence),gref(startchar),gref(nextchar),x,y,w,h,injection or "injections")
+ end
+ end
+ if b and #b>0 then
+ local x,y,w,h=setpair(snext,factor,rlmode,sequence.flags[4],b,injection)
+ if trace_kerns then
+ local startchar=getchar(snext)
+ logprocess("%s: shifting second of pair %s and %s by (%p,%p) and correction (%p,%p) as %s",pref(dataset,sequence),gref(startchar),gref(nextchar),x,y,w,h,injection or "injections")
end
- else
- report_process("%s: check this out (old kern stuff)",pref(kind,lookupname))
end
done=true
+ break
elseif krn~=0 then
local k=setkern(snext,factor,rlmode,krn,injection)
if trace_kerns then
- logprocess("%s: inserting kern %s between %s and %s",pref(kind,lookupname),k,gref(getchar(prev)),gref(nextchar))
+ logprocess("%s: inserting kern %p between %s and %s as %s",pref(dataset,sequence),k,gref(getchar(prev)),gref(nextchar),injection or "injections")
end
done=true
+ break
+ else
+ break
end
+ else
break
end
end
return head,start,done
end
end
-function handlers.gpos_mark2base(head,start,kind,lookupname,markanchors,sequence)
+function handlers.gpos_mark2base(head,start,dataset,sequence,markanchors,rlmode)
local markchar=getchar(start)
if marks[markchar] then
local base=getprev(start)
- if base and getid(base)==glyph_code and getfont(base)==currentfont and getsubtype(base)<256 then
- local basechar=getchar(base)
- if marks[basechar] then
- while true do
- base=getprev(base)
- if base and getid(base)==glyph_code and getfont(base)==currentfont and getsubtype(base)<256 then
- basechar=getchar(base)
- if not marks[basechar] then
- break
- end
- else
- if trace_bugs then
- logwarning("%s: no base for mark %s",pref(kind,lookupname),gref(markchar))
- end
- return head,start,false
- end
- end
- end
- local baseanchors=descriptions[basechar]
- if baseanchors then
- baseanchors=baseanchors.anchors
- end
- if baseanchors then
- local baseanchors=baseanchors['basechar']
- if baseanchors then
- local al=anchorlookups[lookupname]
- for anchor,ba in next,baseanchors do
- if al[anchor] then
- local ma=markanchors[anchor]
- if ma then
- local dx,dy,bound=setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma,characters[basechar])
- if trace_marks then
- logprocess("%s, anchor %s, bound %s: anchoring mark %s to basechar %s => (%p,%p)",
- pref(kind,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy)
+ if base then
+ local basechar=ischar(base,currentfont)
+ if basechar then
+ if marks[basechar] then
+ while base do
+ base=getprev(base)
+ if base then
+ basechar=ischar(base,currentfont)
+ if basechar then
+ if not marks[basechar] then
+ break
end
- return head,start,true
+ else
+ if trace_bugs then
+ logwarning("%s: no base for mark %s, case %i",pref(dataset,sequence),gref(markchar),1)
+ end
+ return head,start,false
+ end
+ else
+ if trace_bugs then
+ logwarning("%s: no base for mark %s, case %i",pref(dataset,sequence),gref(markchar),2)
end
+ return head,start,false
end
end
- if trace_bugs then
- logwarning("%s, no matching anchors for mark %s and base %s",pref(kind,lookupname),gref(markchar),gref(basechar))
+ end
+ local ba=markanchors[1][basechar]
+ if ba then
+ local ma=markanchors[2]
+ local dx,dy,bound=setmark(start,base,factor,rlmode,ba,ma,characters[basechar])
+ if trace_marks then
+ logprocess("%s, anchor %s, bound %s: anchoring mark %s to basechar %s => (%p,%p)",
+ pref(dataset,sequence),anchor,bound,gref(markchar),gref(basechar),dx,dy)
end
+ return head,start,true
end
elseif trace_bugs then
- onetimemessage(currentfont,basechar,"no base anchors",report_fonts)
+ logwarning("%s: nothing preceding, case %i",pref(dataset,sequence),1)
end
elseif trace_bugs then
- logwarning("%s: prev node is no char",pref(kind,lookupname))
+ logwarning("%s: nothing preceding, case %i",pref(dataset,sequence),2)
end
elseif trace_bugs then
- logwarning("%s: mark %s is no mark",pref(kind,lookupname),gref(markchar))
+ logwarning("%s: mark %s is no mark",pref(dataset,sequence),gref(markchar))
end
return head,start,false
end
-function handlers.gpos_mark2ligature(head,start,kind,lookupname,markanchors,sequence)
+function handlers.gpos_mark2ligature(head,start,dataset,sequence,markanchors,rlmode)
local markchar=getchar(start)
if marks[markchar] then
local base=getprev(start)
- if base and getid(base)==glyph_code and getfont(base)==currentfont and getsubtype(base)<256 then
- local basechar=getchar(base)
- if marks[basechar] then
- while true do
- base=getprev(base)
- if base and getid(base)==glyph_code and getfont(base)==currentfont and getsubtype(base)<256 then
- basechar=getchar(base)
- if not marks[basechar] then
- break
- end
- else
- if trace_bugs then
- logwarning("%s: no base for mark %s",pref(kind,lookupname),gref(markchar))
- end
- return head,start,false
- end
- end
- end
- local index=getligaindex(start)
- local baseanchors=descriptions[basechar]
- if baseanchors then
- baseanchors=baseanchors.anchors
- if baseanchors then
- local baseanchors=baseanchors['baselig']
- if baseanchors then
- local al=anchorlookups[lookupname]
- for anchor,ba in next,baseanchors do
- if al[anchor] then
- local ma=markanchors[anchor]
- if ma then
- ba=ba[index]
- if ba then
- local dx,dy,bound=setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma,characters[basechar])
- if trace_marks then
- logprocess("%s, anchor %s, index %s, bound %s: anchoring mark %s to baselig %s at index %s => (%p,%p)",
- pref(kind,lookupname),anchor,index,bound,gref(markchar),gref(basechar),index,dx,dy)
- end
- return head,start,true
- else
- if trace_bugs then
- logwarning("%s: no matching anchors for mark %s and baselig %s with index %a",pref(kind,lookupname),gref(markchar),gref(basechar),index)
- end
- end
+ if base then
+ local basechar=ischar(base,currentfont)
+ if basechar then
+ if marks[basechar] then
+ while base do
+ base=getprev(base)
+ if base then
+ basechar=ischar(base,currentfont)
+ if basechar then
+ if not marks[basechar] then
+ break
+ end
+ else
+ if trace_bugs then
+ logwarning("%s: no base for mark %s, case %i",pref(dataset,sequence),gref(markchar),1)
end
+ return head,start,false
end
+ else
+ if trace_bugs then
+ logwarning("%s: no base for mark %s, case %i",pref(dataset,sequence),gref(markchar),2)
+ end
+ return head,start,false
end
- if trace_bugs then
- logwarning("%s: no matching anchors for mark %s and baselig %s",pref(kind,lookupname),gref(markchar),gref(basechar))
+ end
+ end
+ local ba=markanchors[1][basechar]
+ if ba then
+ local ma=markanchors[2]
+ if ma then
+ local index=getligaindex(start)
+ ba=ba[index]
+ if ba then
+ local dx,dy,bound=setmark(start,base,factor,rlmode,ba,ma,characters[basechar])
+ if trace_marks then
+ logprocess("%s, anchor %s, index %s, bound %s: anchoring mark %s to baselig %s at index %s => (%p,%p)",
+ pref(dataset,sequence),anchor,index,bound,gref(markchar),gref(basechar),index,dx,dy)
+ end
+ return head,start,true
+ else
+ if trace_bugs then
+ logwarning("%s: no matching anchors for mark %s and baselig %s with index %a",pref(dataset,sequence),gref(markchar),gref(basechar),index)
+ end
end
end
+ elseif trace_bugs then
+ onetimemessage(currentfont,basechar,"no base anchors",report_fonts)
end
elseif trace_bugs then
- onetimemessage(currentfont,basechar,"no base anchors",report_fonts)
+ logwarning("%s: prev node is no char, case %i",pref(dataset,sequence),1)
end
elseif trace_bugs then
- logwarning("%s: prev node is no char",pref(kind,lookupname))
+ logwarning("%s: prev node is no char, case %i",pref(dataset,sequence),2)
end
elseif trace_bugs then
- logwarning("%s: mark %s is no mark",pref(kind,lookupname),gref(markchar))
+ logwarning("%s: mark %s is no mark",pref(dataset,sequence),gref(markchar))
end
return head,start,false
end
-function handlers.gpos_mark2mark(head,start,kind,lookupname,markanchors,sequence)
+function handlers.gpos_mark2mark(head,start,dataset,sequence,markanchors,rlmode)
local markchar=getchar(start)
if marks[markchar] then
local base=getprev(start)
@@ -12458,96 +18781,61 @@ function handlers.gpos_mark2mark(head,start,kind,lookupname,markanchors,sequence
end
end
end
- if base and getid(base)==glyph_code and getfont(base)==currentfont and getsubtype(base)<256 then
- local basechar=getchar(base)
- local baseanchors=descriptions[basechar]
- if baseanchors then
- baseanchors=baseanchors.anchors
- if baseanchors then
- baseanchors=baseanchors['basemark']
- if baseanchors then
- local al=anchorlookups[lookupname]
- for anchor,ba in next,baseanchors do
- if al[anchor] then
- local ma=markanchors[anchor]
- if ma then
- local dx,dy,bound=setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma,characters[basechar],true)
- if trace_marks then
- logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%p,%p)",
- pref(kind,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy)
- end
- return head,start,true
- end
- end
- end
- if trace_bugs then
- logwarning("%s: no matching anchors for mark %s and basemark %s",pref(kind,lookupname),gref(markchar),gref(basechar))
- end
+ if base then
+ local basechar=ischar(base,currentfont)
+ if basechar then
+ local ba=markanchors[1][basechar]
+ if ba then
+ local ma=markanchors[2]
+ local dx,dy,bound=setmark(start,base,factor,rlmode,ba,ma,characters[basechar],true)
+ if trace_marks then
+ logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%p,%p)",
+ pref(dataset,sequence),anchor,bound,gref(markchar),gref(basechar),dx,dy)
end
+ return head,start,true
end
- elseif trace_bugs then
- onetimemessage(currentfont,basechar,"no base anchors",report_fonts)
end
- elseif trace_bugs then
- logwarning("%s: prev node is no mark",pref(kind,lookupname))
end
elseif trace_bugs then
- logwarning("%s: mark %s is no mark",pref(kind,lookupname),gref(markchar))
+ logwarning("%s: mark %s is no mark",pref(dataset,sequence),gref(markchar))
end
return head,start,false
end
-function handlers.gpos_cursive(head,start,kind,lookupname,exitanchors,sequence)
- local alreadydone=cursonce and getprop(start,a_cursbase)
- if not alreadydone then
- local done=false
- local startchar=getchar(start)
- if marks[startchar] then
- if trace_cursive then
- logprocess("%s: ignoring cursive for mark %s",pref(kind,lookupname),gref(startchar))
- end
- else
- local nxt=getnext(start)
- while not done and nxt and getid(nxt)==glyph_code and getfont(nxt)==currentfont and getsubtype(nxt)<256 do
- local nextchar=getchar(nxt)
- if marks[nextchar] then
- nxt=getnext(nxt)
- else
- local entryanchors=descriptions[nextchar]
- if entryanchors then
- entryanchors=entryanchors.anchors
- if entryanchors then
- entryanchors=entryanchors['centry']
- if entryanchors then
- local al=anchorlookups[lookupname]
- for anchor,entry in next,entryanchors do
- if al[anchor] then
- local exit=exitanchors[anchor]
- if exit then
- local dx,dy,bound=setcursive(start,nxt,tfmdata.parameters.factor,rlmode,exit,entry,characters[startchar],characters[nextchar])
- if trace_cursive then
- logprocess("%s: moving %s to %s cursive (%p,%p) using anchor %s and bound %s in rlmode %s",pref(kind,lookupname),gref(startchar),gref(nextchar),dx,dy,anchor,bound,rlmode)
- end
- done=true
- break
- end
- end
- end
+function handlers.gpos_cursive(head,start,dataset,sequence,exitanchors,rlmode,step,i)
+ local done=false
+ local startchar=getchar(start)
+ if marks[startchar] then
+ if trace_cursive then
+ logprocess("%s: ignoring cursive for mark %s",pref(dataset,sequence),gref(startchar))
+ end
+ else
+ local nxt=getnext(start)
+ while not done and nxt do
+ local nextchar=ischar(nxt,currentfont)
+ if not nextchar then
+ break
+ elseif marks[nextchar] then
+ nxt=getnext(nxt)
+ else
+ local exit=exitanchors[3]
+ if exit then
+ local entry=exitanchors[1][nextchar]
+ if entry then
+ entry=entry[2]
+ if entry then
+ local dx,dy,bound=setcursive(start,nxt,factor,rlmode,exit,entry,characters[startchar],characters[nextchar])
+ if trace_cursive then
+ logprocess("%s: moving %s to %s cursive (%p,%p) using anchor %s and bound %s in %s mode",pref(dataset,sequence),gref(startchar),gref(nextchar),dx,dy,anchor,bound,mref(rlmode))
end
+ done=true
end
- elseif trace_bugs then
- onetimemessage(currentfont,startchar,"no entry anchors",report_fonts)
end
- break
end
+ break
end
end
- return head,start,done
- else
- if trace_cursive and trace_details then
- logprocess("%s, cursive %s is already done",pref(kind,lookupname),gref(getchar(start)),alreadydone)
- end
- return head,start,false
end
+ return head,start,done
end
local chainprocs={}
local function logprocess(...)
@@ -12564,16 +18852,12 @@ local function logprocess(...)
report_chain(...)
end
local logwarning=report_chain
-function chainprocs.chainsub(head,start,stop,kind,chainname,currentcontext,lookuphash,lookuplist,chainlookupname)
- logwarning("%s: a direct call to chainsub cannot happen",cref(kind,chainname,chainlookupname))
- return head,start,false
-end
-function chainprocs.reversesub(head,start,stop,kind,chainname,currentcontext,lookuphash,replacements)
+local function reversesub(head,start,stop,dataset,sequence,replacements,rlmode)
local char=getchar(start)
local replacement=replacements[char]
if replacement then
if trace_singles then
- logprocess("%s: single reverse replacement of %s by %s",cref(kind,chainname),gref(char),gref(replacement))
+ logprocess("%s: single reverse replacement of %s by %s",cref(dataset,sequence),gref(char),gref(replacement))
end
resetinjection(start)
setchar(start,replacement)
@@ -12582,36 +18866,35 @@ function chainprocs.reversesub(head,start,stop,kind,chainname,currentcontext,loo
return head,start,false
end
end
-function chainprocs.gsub_single(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex)
- local current=start
- local subtables=currentlookup.subtables
- if #subtables>1 then
- logwarning("todo: check if we need to loop over the replacements: % t",subtables)
+chainprocs.reversesub=reversesub
+local function reportmoresteps(dataset,sequence)
+ logwarning("%s: more than 1 step",cref(dataset,sequence))
+end
+function chainprocs.gsub_single(head,start,stop,dataset,sequence,currentlookup,chainindex)
+ local steps=currentlookup.steps
+ local nofsteps=currentlookup.nofsteps
+ if nofsteps>1 then
+ reportmoresteps(dataset,sequence)
end
+ local current=start
while current do
- if getid(current)==glyph_code then
- local currentchar=getchar(current)
- local lookupname=subtables[1]
- local replacement=lookuphash[lookupname]
- if not replacement then
+ local currentchar=ischar(current)
+ if currentchar then
+ local replacement=steps[1].coverage[currentchar]
+ if not replacement or replacement=="" then
if trace_bugs then
- logwarning("%s: no single hits",cref(kind,chainname,chainlookupname,lookupname,chainindex))
+ logwarning("%s: no single for %s",cref(dataset,sequence,chainindex),gref(currentchar))
end
else
- replacement=replacement[currentchar]
- if not replacement or replacement=="" then
- if trace_bugs then
- logwarning("%s: no single for %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(currentchar))
- end
- else
- if trace_singles then
- logprocess("%s: replacing single %s by %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(currentchar),gref(replacement))
- end
- resetinjection(current)
- setchar(current,replacement)
+ if trace_singles then
+ logprocess("%s: replacing single %s by %s",cref(dataset,sequence,chainindex),gref(currentchar),gref(replacement))
end
+ resetinjection(current)
+ setchar(current,replacement)
end
return head,start,true
+ elseif currentchar==false then
+ break
elseif current==stop then
break
else
@@ -12620,63 +18903,57 @@ function chainprocs.gsub_single(head,start,stop,kind,chainname,currentcontext,lo
end
return head,start,false
end
-function chainprocs.gsub_multiple(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname)
+function chainprocs.gsub_multiple(head,start,stop,dataset,sequence,currentlookup)
+ local steps=currentlookup.steps
+ local nofsteps=currentlookup.nofsteps
+ if nofsteps>1 then
+ reportmoresteps(dataset,sequence)
+ end
local startchar=getchar(start)
- local subtables=currentlookup.subtables
- local lookupname=subtables[1]
- local replacements=lookuphash[lookupname]
- if not replacements then
+ local replacement=steps[1].coverage[startchar]
+ if not replacement or replacement=="" then
if trace_bugs then
- logwarning("%s: no multiple hits",cref(kind,chainname,chainlookupname,lookupname))
+ logwarning("%s: no multiple for %s",cref(dataset,sequence),gref(startchar))
end
else
- replacements=replacements[startchar]
- if not replacements or replacement=="" then
- if trace_bugs then
- logwarning("%s: no multiple for %s",cref(kind,chainname,chainlookupname,lookupname),gref(startchar))
- end
- else
- if trace_multiples then
- logprocess("%s: replacing %s by multiple characters %s",cref(kind,chainname,chainlookupname,lookupname),gref(startchar),gref(replacements))
- end
- return multiple_glyphs(head,start,replacements,currentlookup.flags[1])
+ if trace_multiples then
+ logprocess("%s: replacing %s by multiple characters %s",cref(dataset,sequence),gref(startchar),gref(replacement))
end
+ return multiple_glyphs(head,start,replacement,currentlookup.flags[1])
end
return head,start,false
end
-function chainprocs.gsub_alternate(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname)
+function chainprocs.gsub_alternate(head,start,stop,dataset,sequence,currentlookup)
+ local steps=currentlookup.steps
+ local nofsteps=currentlookup.nofsteps
+ if nofsteps>1 then
+ reportmoresteps(dataset,sequence)
+ end
+ local kind=dataset[4]
+ local what=dataset[1]
+ local value=what==true and tfmdata.shared.features[kind] or what
local current=start
- local subtables=currentlookup.subtables
- local value=featurevalue==true and tfmdata.shared.features[kind] or featurevalue
while current do
- if getid(current)==glyph_code then
- local currentchar=getchar(current)
- local lookupname=subtables[1]
- local alternatives=lookuphash[lookupname]
- if not alternatives then
- if trace_bugs then
- logwarning("%s: no alternative hit",cref(kind,chainname,chainlookupname,lookupname))
- end
- else
- alternatives=alternatives[currentchar]
- if alternatives then
- local choice,comment=get_alternative_glyph(current,alternatives,value,trace_alternatives)
- if choice then
- if trace_alternatives then
- logprocess("%s: replacing %s by alternative %a to %s, %s",cref(kind,chainname,chainlookupname,lookupname),gref(char),choice,gref(choice),comment)
- end
- resetinjection(start)
- setchar(start,choice)
- else
- if trace_alternatives then
- logwarning("%s: no variant %a for %s, %s",cref(kind,chainname,chainlookupname,lookupname),value,gref(char),comment)
- end
+ local currentchar=ischar(current)
+ if currentchar then
+ local alternatives=steps[1].coverage[currentchar]
+ if alternatives then
+ local choice,comment=get_alternative_glyph(current,alternatives,value)
+ if choice then
+ if trace_alternatives then
+ logprocess("%s: replacing %s by alternative %a to %s, %s",cref(dataset,sequence),gref(char),choice,gref(choice),comment)
+ end
+ resetinjection(start)
+ setchar(start,choice)
+ else
+ if trace_alternatives then
+ logwarning("%s: no variant %a for %s, %s",cref(dataset,sequence),value,gref(char),comment)
end
- elseif trace_bugs then
- logwarning("%s: no alternative for %s, %s",cref(kind,chainname,chainlookupname,lookupname),gref(currentchar),comment)
end
end
return head,start,true
+ elseif currentchar==false then
+ break
elseif current==stop then
break
else
@@ -12685,295 +18962,311 @@ function chainprocs.gsub_alternate(head,start,stop,kind,chainname,currentcontext
end
return head,start,false
end
-function chainprocs.gsub_ligature(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex)
+function chainprocs.gsub_ligature(head,start,stop,dataset,sequence,currentlookup,chainindex)
+ local steps=currentlookup.steps
+ local nofsteps=currentlookup.nofsteps
+ if nofsteps>1 then
+ reportmoresteps(dataset,sequence)
+ end
local startchar=getchar(start)
- local subtables=currentlookup.subtables
- local lookupname=subtables[1]
- local ligatures=lookuphash[lookupname]
+ local ligatures=steps[1].coverage[startchar]
if not ligatures then
if trace_bugs then
- logwarning("%s: no ligature hits",cref(kind,chainname,chainlookupname,lookupname,chainindex))
+ logwarning("%s: no ligatures starting with %s",cref(dataset,sequence,chainindex),gref(startchar))
end
else
- ligatures=ligatures[startchar]
- if not ligatures then
- if trace_bugs then
- logwarning("%s: no ligatures starting with %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar))
- end
- else
- local s=getnext(start)
- local discfound=false
- local last=stop
- local nofreplacements=1
- local skipmark=currentlookup.flags[1]
- while s do
- local id=getid(s)
- if id==disc_code then
- if not discfound then
- discfound=s
- end
- if s==stop then
- break
- else
- s=getnext(s)
- end
+ local current=getnext(start)
+ local discfound=false
+ local last=stop
+ local nofreplacements=1
+ local skipmark=currentlookup.flags[1]
+ while current do
+ local id=getid(current)
+ if id==disc_code then
+ if not discfound then
+ discfound=current
+ end
+ if current==stop then
+ break
else
- local schar=getchar(s)
- if skipmark and marks[schar] then
- s=getnext(s)
- else
- local lg=ligatures[schar]
- if lg then
- ligatures,last,nofreplacements=lg,s,nofreplacements+1
- if s==stop then
- break
- else
- s=getnext(s)
- end
- else
+ current=getnext(current)
+ end
+ else
+ local schar=getchar(current)
+ if skipmark and marks[schar] then
+ current=getnext(current)
+ else
+ local lg=ligatures[schar]
+ if lg then
+ ligatures=lg
+ last=current
+ nofreplacements=nofreplacements+1
+ if current==stop then
break
+ else
+ current=getnext(current)
end
- end
- end
- end
- local l2=ligatures.ligature
- if l2 then
- if chainindex then
- stop=last
- end
- if trace_ligatures then
- if start==stop then
- logprocess("%s: replacing character %s by ligature %s case 3",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(l2))
else
- logprocess("%s: replacing character %s upto %s by ligature %s case 4",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(getchar(stop)),gref(l2))
+ break
end
end
- head,start=toligature(kind,lookupname,head,start,stop,l2,currentlookup.flags[1],discfound)
- return head,start,true,nofreplacements,discfound
- elseif trace_bugs then
+ end
+ end
+ local ligature=ligatures.ligature
+ if ligature then
+ if chainindex then
+ stop=last
+ end
+ if trace_ligatures then
if start==stop then
- logwarning("%s: replacing character %s by ligature fails",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar))
+ logprocess("%s: replacing character %s by ligature %s case 3",cref(dataset,sequence,chainindex),gref(startchar),gref(ligature))
else
- logwarning("%s: replacing character %s upto %s by ligature fails",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(getchar(stop)))
+ logprocess("%s: replacing character %s upto %s by ligature %s case 4",cref(dataset,sequence,chainindex),gref(startchar),gref(getchar(stop)),gref(ligature))
end
end
+ head,start=toligature(head,start,stop,ligature,dataset,sequence,skipmark,discfound)
+ return head,start,true,nofreplacements,discfound
+ elseif trace_bugs then
+ if start==stop then
+ logwarning("%s: replacing character %s by ligature fails",cref(dataset,sequence,chainindex),gref(startchar))
+ else
+ logwarning("%s: replacing character %s upto %s by ligature fails",cref(dataset,sequence,chainindex),gref(startchar),gref(getchar(stop)))
+ end
end
end
return head,start,false,0,false
end
-function chainprocs.gpos_single(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex,sequence)
+function chainprocs.gpos_single(head,start,stop,dataset,sequence,currentlookup,rlmode,chainindex)
+ local steps=currentlookup.steps
+ local nofsteps=currentlookup.nofsteps
+ if nofsteps>1 then
+ reportmoresteps(dataset,sequence)
+ end
local startchar=getchar(start)
- local subtables=currentlookup.subtables
- local lookupname=subtables[1]
- local kerns=lookuphash[lookupname]
- if kerns then
- kerns=kerns[startchar]
- if kerns then
- local dx,dy,w,h=setpair(start,tfmdata.parameters.factor,rlmode,sequence.flags[4],kerns)
- if trace_kerns then
- logprocess("%s: shifting single %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),dx,dy,w,h)
- end
+ local step=steps[1]
+ local kerns=step.coverage[startchar]
+ if not kerns then
+ elseif step.format=="pair" then
+ local dx,dy,w,h=setpair(start,factor,rlmode,sequence.flags[4],kerns)
+ if trace_kerns then
+ logprocess("%s: shifting single %s by (%p,%p) and correction (%p,%p)",cref(dataset,sequence),gref(startchar),dx,dy,w,h)
+ end
+ else
+ local k=setkern(start,factor,rlmode,kerns,injection)
+ if trace_kerns then
+ logprocess("%s: shifting single %s by %p",cref(dataset,sequence),gref(startchar),k)
end
end
return head,start,false
end
-function chainprocs.gpos_pair(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex,sequence)
+function chainprocs.gpos_pair(head,start,stop,dataset,sequence,currentlookup,rlmode,chainindex)
+ local steps=currentlookup.steps
+ local nofsteps=currentlookup.nofsteps
+ if nofsteps>1 then
+ reportmoresteps(dataset,sequence)
+ end
local snext=getnext(start)
if snext then
local startchar=getchar(start)
- local subtables=currentlookup.subtables
- local lookupname=subtables[1]
- local kerns=lookuphash[lookupname]
+ local step=steps[1]
+ local kerns=step.coverage[startchar]
if kerns then
- kerns=kerns[startchar]
- if kerns then
- local lookuptype=lookuptypes[lookupname]
- local prev,done=start,false
- local factor=tfmdata.parameters.factor
- while snext and getid(snext)==glyph_code and getfont(snext)==currentfont and getsubtype(snext)<256 do
- local nextchar=getchar(snext)
- local krn=kerns[nextchar]
- if not krn and marks[nextchar] then
- prev=snext
- snext=getnext(snext)
- else
- if not krn then
- elseif type(krn)=="table" then
- if lookuptype=="pair" then
- local a,b=krn[2],krn[3]
- if a and #a>0 then
- local startchar=getchar(start)
- local x,y,w,h=setpair(start,factor,rlmode,sequence.flags[4],a)
- if trace_kerns then
- logprocess("%s: shifting first of pair %s and %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),gref(nextchar),x,y,w,h)
- end
- end
- if b and #b>0 then
- local startchar=getchar(start)
- local x,y,w,h=setpair(snext,factor,rlmode,sequence.flags[4],b)
- if trace_kerns then
- logprocess("%s: shifting second of pair %s and %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),gref(nextchar),x,y,w,h)
- end
- end
- else
- report_process("%s: check this out (old kern stuff)",cref(kind,chainname,chainlookupname))
- end
- done=true
- elseif krn~=0 then
- local k=setkern(snext,factor,rlmode,krn)
+ local prev=start
+ local done=false
+ while snext do
+ local nextchar=ischar(snext,currentfont)
+ if not nextchar then
+ break
+ end
+ local krn=kerns[nextchar]
+ if not krn and marks[nextchar] then
+ prev=snext
+ snext=getnext(snext)
+ elseif not krn then
+ break
+ elseif step.format=="pair" then
+ local a,b=krn[1],krn[2]
+ if optimizekerns then
+ if not b and a[1]==0 and a[2]==0 and a[4]==0 then
+ local k=setkern(snext,factor,rlmode,a[3],"injections")
if trace_kerns then
- logprocess("%s: inserting kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(getchar(prev)),gref(nextchar))
+ logprocess("%s: shifting single %s by %p",cref(dataset,sequence),gref(startchar),k)
end
done=true
+ break
end
- break
end
+ if a and #a>0 then
+ local startchar=getchar(start)
+ local x,y,w,h=setpair(start,factor,rlmode,sequence.flags[4],a,"injections")
+ if trace_kerns then
+ logprocess("%s: shifting first of pair %s and %s by (%p,%p) and correction (%p,%p)",cref(dataset,sequence),gref(startchar),gref(nextchar),x,y,w,h)
+ end
+ end
+ if b and #b>0 then
+ local startchar=getchar(start)
+ local x,y,w,h=setpair(snext,factor,rlmode,sequence.flags[4],b,"injections")
+ if trace_kerns then
+ logprocess("%s: shifting second of pair %s and %s by (%p,%p) and correction (%p,%p)",cref(dataset,sequence),gref(startchar),gref(nextchar),x,y,w,h)
+ end
+ end
+ done=true
+ break
+ elseif krn~=0 then
+ local k=setkern(snext,factor,rlmode,krn)
+ if trace_kerns then
+ logprocess("%s: inserting kern %s between %s and %s",cref(dataset,sequence),k,gref(getchar(prev)),gref(nextchar))
+ end
+ done=true
+ break
+ else
+ break
end
- return head,start,done
end
+ return head,start,done
end
end
return head,start,false
end
-function chainprocs.gpos_mark2base(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname)
+function chainprocs.gpos_mark2base(head,start,stop,dataset,sequence,currentlookup,rlmode)
+ local steps=currentlookup.steps
+ local nofsteps=currentlookup.nofsteps
+ if nofsteps>1 then
+ reportmoresteps(dataset,sequence)
+ end
local markchar=getchar(start)
if marks[markchar] then
- local subtables=currentlookup.subtables
- local lookupname=subtables[1]
- local markanchors=lookuphash[lookupname]
- if markanchors then
- markanchors=markanchors[markchar]
- end
+ local markanchors=steps[1].coverage[markchar]
if markanchors then
local base=getprev(start)
- if base and getid(base)==glyph_code and getfont(base)==currentfont and getsubtype(base)<256 then
- local basechar=getchar(base)
- if marks[basechar] then
- while true do
- base=getprev(base)
- if base and getid(base)==glyph_code and getfont(base)==currentfont and getsubtype(base)<256 then
- basechar=getchar(base)
- if not marks[basechar] then
- break
- end
- else
- if trace_bugs then
- logwarning("%s: no base for mark %s",pref(kind,lookupname),gref(markchar))
- end
- return head,start,false
- end
- end
- end
- local baseanchors=descriptions[basechar].anchors
- if baseanchors then
- local baseanchors=baseanchors['basechar']
- if baseanchors then
- local al=anchorlookups[lookupname]
- for anchor,ba in next,baseanchors do
- if al[anchor] then
- local ma=markanchors[anchor]
- if ma then
- local dx,dy,bound=setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma,characters[basechar])
- if trace_marks then
- logprocess("%s, anchor %s, bound %s: anchoring mark %s to basechar %s => (%p,%p)",
- cref(kind,chainname,chainlookupname,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy)
+ if base then
+ local basechar=ischar(base,currentfont)
+ if basechar then
+ if marks[basechar] then
+ while base do
+ base=getprev(base)
+ if base then
+ local basechar=ischar(base,currentfont)
+ if basechar then
+ if not marks[basechar] then
+ break
end
- return head,start,true
+ else
+ if trace_bugs then
+ logwarning("%s: no base for mark %s, case %i",pref(dataset,sequence),gref(markchar),1)
+ end
+ return head,start,false
end
+ else
+ if trace_bugs then
+ logwarning("%s: no base for mark %s, case %i",pref(dataset,sequence),gref(markchar),2)
+ end
+ return head,start,false
end
end
- if trace_bugs then
- logwarning("%s, no matching anchors for mark %s and base %s",cref(kind,chainname,chainlookupname,lookupname),gref(markchar),gref(basechar))
+ end
+ local ba=markanchors[1][basechar]
+ if ba then
+ local ma=markanchors[2]
+ if ma then
+ local dx,dy,bound=setmark(start,base,factor,rlmode,ba,ma,characters[basechar])
+ if trace_marks then
+ logprocess("%s, anchor %s, bound %s: anchoring mark %s to basechar %s => (%p,%p)",
+ cref(dataset,sequence),anchor,bound,gref(markchar),gref(basechar),dx,dy)
+ end
+ return head,start,true
end
end
+ elseif trace_bugs then
+ logwarning("%s: prev node is no char, case %i",cref(dataset,sequence),1)
end
elseif trace_bugs then
- logwarning("%s: prev node is no char",cref(kind,chainname,chainlookupname,lookupname))
+ logwarning("%s: prev node is no char, case %i",cref(dataset,sequence),2)
end
elseif trace_bugs then
- logwarning("%s: mark %s has no anchors",cref(kind,chainname,chainlookupname,lookupname),gref(markchar))
+ logwarning("%s: mark %s has no anchors",cref(dataset,sequence),gref(markchar))
end
elseif trace_bugs then
- logwarning("%s: mark %s is no mark",cref(kind,chainname,chainlookupname),gref(markchar))
+ logwarning("%s: mark %s is no mark",cref(dataset,sequence),gref(markchar))
end
return head,start,false
end
-function chainprocs.gpos_mark2ligature(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname)
+function chainprocs.gpos_mark2ligature(head,start,stop,dataset,sequence,currentlookup,rlmode)
+ local steps=currentlookup.steps
+ local nofsteps=currentlookup.nofsteps
+ if nofsteps>1 then
+ reportmoresteps(dataset,sequence)
+ end
local markchar=getchar(start)
if marks[markchar] then
- local subtables=currentlookup.subtables
- local lookupname=subtables[1]
- local markanchors=lookuphash[lookupname]
- if markanchors then
- markanchors=markanchors[markchar]
- end
+ local markanchors=steps[1].coverage[markchar]
if markanchors then
local base=getprev(start)
- if base and getid(base)==glyph_code and getfont(base)==currentfont and getsubtype(base)<256 then
- local basechar=getchar(base)
- if marks[basechar] then
- while true do
- base=getprev(base)
- if base and getid(base)==glyph_code and getfont(base)==currentfont and getsubtype(base)<256 then
- basechar=getchar(base)
- if not marks[basechar] then
- break
- end
- else
- if trace_bugs then
- logwarning("%s: no base for mark %s",cref(kind,chainname,chainlookupname,lookupname),markchar)
+ if base then
+ local basechar=ischar(base,currentfont)
+ if basechar then
+ if marks[basechar] then
+ while base do
+ base=getprev(base)
+ if base then
+ local basechar=ischar(base,currentfont)
+ if basechar then
+ if not marks[basechar] then
+ break
+ end
+ else
+ if trace_bugs then
+ logwarning("%s: no base for mark %s, case %i",cref(dataset,sequence),markchar,1)
+ end
+ return head,start,false
+ end
+ else
+ if trace_bugs then
+ logwarning("%s: no base for mark %s, case %i",cref(dataset,sequence),markchar,2)
+ end
+ return head,start,false
end
- return head,start,false
end
end
- end
- local index=getligaindex(start)
- local baseanchors=descriptions[basechar].anchors
- if baseanchors then
- local baseanchors=baseanchors['baselig']
- if baseanchors then
- local al=anchorlookups[lookupname]
- for anchor,ba in next,baseanchors do
- if al[anchor] then
- local ma=markanchors[anchor]
- if ma then
- ba=ba[index]
- if ba then
- local dx,dy,bound=setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma,characters[basechar])
- if trace_marks then
- logprocess("%s, anchor %s, bound %s: anchoring mark %s to baselig %s at index %s => (%p,%p)",
- cref(kind,chainname,chainlookupname,lookupname),anchor,a or bound,gref(markchar),gref(basechar),index,dx,dy)
- end
- return head,start,true
- end
+ local ba=markanchors[1][basechar]
+ if ba then
+ local ma=markanchors[2]
+ if ma then
+ local index=getligaindex(start)
+ ba=ba[index]
+ if ba then
+ local dx,dy,bound=setmark(start,base,factor,rlmode,ba,ma,characters[basechar])
+ if trace_marks then
+ logprocess("%s, anchor %s, bound %s: anchoring mark %s to baselig %s at index %s => (%p,%p)",
+ cref(dataset,sequence),anchor,a or bound,gref(markchar),gref(basechar),index,dx,dy)
end
+ return head,start,true
end
end
- if trace_bugs then
- logwarning("%s: no matching anchors for mark %s and baselig %s",cref(kind,chainname,chainlookupname,lookupname),gref(markchar),gref(basechar))
- end
end
+ elseif trace_bugs then
+ logwarning("%s, prev node is no char, case %i",cref(dataset,sequence),1)
end
elseif trace_bugs then
- logwarning("feature %s, lookup %s: prev node is no char",kind,lookupname)
+ logwarning("%s, prev node is no char, case %i",cref(dataset,sequence),2)
end
elseif trace_bugs then
- logwarning("%s: mark %s has no anchors",cref(kind,chainname,chainlookupname,lookupname),gref(markchar))
+ logwarning("%s, mark %s has no anchors",cref(dataset,sequence),gref(markchar))
end
elseif trace_bugs then
- logwarning("%s: mark %s is no mark",cref(kind,chainname,chainlookupname),gref(markchar))
+ logwarning("%s, mark %s is no mark",cref(dataset,sequence),gref(markchar))
end
return head,start,false
end
-function chainprocs.gpos_mark2mark(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname)
+function chainprocs.gpos_mark2mark(head,start,stop,dataset,sequence,currentlookup,rlmode)
+ local steps=currentlookup.steps
+ local nofsteps=currentlookup.nofsteps
+ if nofsteps>1 then
+ reportmoresteps(dataset,sequence)
+ end
local markchar=getchar(start)
if marks[markchar] then
- local subtables=currentlookup.subtables
- local lookupname=subtables[1]
- local markanchors=lookuphash[lookupname]
- if markanchors then
- markanchors=markanchors[markchar]
- end
+ local markanchors=steps[1].coverage[markchar]
if markanchors then
local base=getprev(start)
local slc=getligaindex(start)
@@ -12987,112 +19280,91 @@ function chainprocs.gpos_mark2mark(head,start,stop,kind,chainname,currentcontext
end
end
end
- if base and getid(base)==glyph_code and getfont(base)==currentfont and getsubtype(base)<256 then
- local basechar=getchar(base)
- local baseanchors=descriptions[basechar].anchors
- if baseanchors then
- baseanchors=baseanchors['basemark']
- if baseanchors then
- local al=anchorlookups[lookupname]
- for anchor,ba in next,baseanchors do
- if al[anchor] then
- local ma=markanchors[anchor]
- if ma then
- local dx,dy,bound=setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma,characters[basechar],true)
- if trace_marks then
- logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%p,%p)",
- cref(kind,chainname,chainlookupname,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy)
- end
- return head,start,true
- end
+ if base then
+ local basechar=ischar(base,currentfont)
+ if basechar then
+ local ba=markanchors[1][basechar]
+ if ba then
+ local ma=markanchors[2]
+ if ma then
+ local dx,dy,bound=setmark(start,base,factor,rlmode,ba,ma,characters[basechar],true)
+ if trace_marks then
+ logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%p,%p)",
+ cref(dataset,sequence),anchor,bound,gref(markchar),gref(basechar),dx,dy)
end
- end
- if trace_bugs then
- logwarning("%s: no matching anchors for mark %s and basemark %s",gref(kind,chainname,chainlookupname,lookupname),gref(markchar),gref(basechar))
+ return head,start,true
end
end
+ elseif trace_bugs then
+ logwarning("%s: prev node is no mark, case %i",cref(dataset,sequence),1)
end
elseif trace_bugs then
- logwarning("%s: prev node is no mark",cref(kind,chainname,chainlookupname,lookupname))
+ logwarning("%s: prev node is no mark, case %i",cref(dataset,sequence),2)
end
elseif trace_bugs then
- logwarning("%s: mark %s has no anchors",cref(kind,chainname,chainlookupname,lookupname),gref(markchar))
+ logwarning("%s: mark %s has no anchors",cref(dataset,sequence),gref(markchar))
end
elseif trace_bugs then
- logwarning("%s: mark %s is no mark",cref(kind,chainname,chainlookupname),gref(markchar))
+ logwarning("%s: mark %s is no mark",cref(dataset,sequence),gref(markchar))
end
return head,start,false
end
-function chainprocs.gpos_cursive(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname)
- local alreadydone=cursonce and getprop(start,a_cursbase)
- if not alreadydone then
- local startchar=getchar(start)
- local subtables=currentlookup.subtables
- local lookupname=subtables[1]
- local exitanchors=lookuphash[lookupname]
- if exitanchors then
- exitanchors=exitanchors[startchar]
- end
- if exitanchors then
- local done=false
- if marks[startchar] then
- if trace_cursive then
- logprocess("%s: ignoring cursive for mark %s",pref(kind,lookupname),gref(startchar))
- end
- else
- local nxt=getnext(start)
- while not done and nxt and getid(nxt)==glyph_code and getfont(nxt)==currentfont and getsubtype(nxt)<256 do
- local nextchar=getchar(nxt)
- if marks[nextchar] then
- nxt=getnext(nxt)
- else
- local entryanchors=descriptions[nextchar]
- if entryanchors then
- entryanchors=entryanchors.anchors
- if entryanchors then
- entryanchors=entryanchors['centry']
- if entryanchors then
- local al=anchorlookups[lookupname]
- for anchor,entry in next,entryanchors do
- if al[anchor] then
- local exit=exitanchors[anchor]
- if exit then
- local dx,dy,bound=setcursive(start,nxt,tfmdata.parameters.factor,rlmode,exit,entry,characters[startchar],characters[nextchar])
- if trace_cursive then
- logprocess("%s: moving %s to %s cursive (%p,%p) using anchor %s and bound %s in rlmode %s",pref(kind,lookupname),gref(startchar),gref(nextchar),dx,dy,anchor,bound,rlmode)
- end
- done=true
- break
- end
- end
- end
+function chainprocs.gpos_cursive(head,start,stop,dataset,sequence,currentlookup,rlmode)
+ local steps=currentlookup.steps
+ local nofsteps=currentlookup.nofsteps
+ if nofsteps>1 then
+ reportmoresteps(dataset,sequence)
+ end
+ local startchar=getchar(start)
+ local exitanchors=steps[1].coverage[startchar]
+ if exitanchors then
+ local done=false
+ if marks[startchar] then
+ if trace_cursive then
+ logprocess("%s: ignoring cursive for mark %s",pref(dataset,sequence),gref(startchar))
+ end
+ else
+ local nxt=getnext(start)
+ while not done and nxt do
+ local nextchar=ischar(nxt,currentfont)
+ if not nextchar then
+ break
+ elseif marks[nextchar] then
+ nxt=getnext(nxt)
+ else
+ local exit=exitanchors[3]
+ if exit then
+ local entry=exitanchors[1][nextchar]
+ if entry then
+ entry=entry[2]
+ if entry then
+ local dx,dy,bound=setcursive(start,nxt,factor,rlmode,exit,entry,characters[startchar],characters[nextchar])
+ if trace_cursive then
+ logprocess("%s: moving %s to %s cursive (%p,%p) using anchor %s and bound %s in %s mode",pref(dataset,sequence),gref(startchar),gref(nextchar),dx,dy,anchor,bound,mref(rlmode))
end
+ done=true
+ break
end
- elseif trace_bugs then
- onetimemessage(currentfont,startchar,"no entry anchors",report_fonts)
end
- break
+ elseif trace_bugs then
+ onetimemessage(currentfont,startchar,"no entry anchors",report_fonts)
end
+ break
end
end
- return head,start,done
- else
- if trace_cursive and trace_details then
- logprocess("%s, cursive %s is already done",pref(kind,lookupname),gref(getchar(start)),alreadydone)
- end
- return head,start,false
end
- end
- return head,start,false
-end
-local function show_skip(kind,chainname,char,ck,class)
- if ck[9] then
- logwarning("%s: skipping char %s, class %a, rule %a, lookuptype %a, %a => %a",cref(kind,chainname),gref(char),class,ck[1],ck[2],ck[9],ck[10])
+ return head,start,done
else
- logwarning("%s: skipping char %s, class %a, rule %a, lookuptype %a",cref(kind,chainname),gref(char),class,ck[1],ck[2])
+ if trace_cursive and trace_details then
+ logprocess("%s, cursive %s is already done",pref(dataset,sequence),gref(getchar(start)),alreadydone)
+ end
+ return head,start,false
end
end
-local function chaindisk(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,chainindex,sequence,chainproc)
+local function show_skip(dataset,sequence,char,ck,class)
+ logwarning("%s: skipping char %s, class %a, rule %a, lookuptype %a",cref(dataset,sequence),gref(char),class,ck[1],ck[8] or ck[2])
+end
+local function chaindisk(head,start,last,dataset,sequence,chainlookup,rlmode,k,ck,chainproc)
if not start then
return head,start,false
end
@@ -13152,6 +19424,8 @@ local function chaindisk(head,start,last,kind,chainname,ck,lookuphash,chainlooku
else
break
end
+ else
+ break
end
end
if sweepoverflow then
@@ -13249,11 +19523,16 @@ local function chaindisk(head,start,last,kind,chainname,ck,lookuphash,chainlooku
local cl=getprev(lookaheaddisc)
local cprev=getprev(start)
local insertedmarks=0
- while cprev and getid(cf)==glyph_code and getfont(cf)==currentfont and getsubtype(cf)<256 and marks[getchar(cf)] do
- insertedmarks=insertedmarks+1
- cf=cprev
- startishead=cf==head
- cprev=getprev(cprev)
+ while cprev do
+ local char=ischar(cf,currentfont)
+ if char and marks[char] then
+ insertedmarks=insertedmarks+1
+ cf=cprev
+ startishead=cf==head
+ cprev=getprev(cprev)
+ else
+ break
+ end
end
setprev(lookaheaddisc,cprev)
if cprev then
@@ -13264,8 +19543,7 @@ local function chaindisk(head,start,last,kind,chainname,ck,lookuphash,chainlooku
if startishead then
head=lookaheaddisc
end
- local replace=getfield(lookaheaddisc,"replace")
- local pre=getfield(lookaheaddisc,"pre")
+ local pre,post,replace=getdisc(lookaheaddisc)
local new=copy_node_list(cf)
local cnew=new
for i=1,insertedmarks do
@@ -13276,22 +19554,19 @@ local function chaindisk(head,start,last,kind,chainname,ck,lookuphash,chainlooku
clast=getnext(clast)
end
if not notmatchpre[lookaheaddisc] then
- cf,start,ok=chainproc(cf,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence)
+ cf,start,ok=chainproc(cf,start,last,dataset,sequence,chainlookup,rlmode,k)
end
if not notmatchreplace[lookaheaddisc] then
- new,cnew,ok=chainproc(new,cnew,clast,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence)
+ new,cnew,ok=chainproc(new,cnew,clast,dataset,sequence,chainlookup,rlmode,k)
end
if pre then
- setnext(cl,pre)
- setprev(pre,cl)
+ setlink(cl,pre)
end
if replace then
local tail=find_node_tail(new)
- setnext(tail,replace)
- setprev(replace,tail)
+ setlink(tail,replace)
end
- setfield(lookaheaddisc,"pre",cf)
- setfield(lookaheaddisc,"replace",new)
+ setdisc(lookaheaddisc,cf,post,new)
start=getprev(lookaheaddisc)
sweephead[cf]=getnext(clast)
sweephead[new]=getnext(last)
@@ -13300,10 +19575,15 @@ local function chaindisk(head,start,last,kind,chainname,ck,lookuphash,chainlooku
local cl=start
local cnext=getnext(start)
local insertedmarks=0
- while cnext and getid(cnext)==glyph_code and getfont(cnext)==currentfont and getsubtype(cnext)<256 and marks[getchar(cnext)] do
- insertedmarks=insertedmarks+1
- cl=cnext
- cnext=getnext(cnext)
+ while cnext do
+ local char=ischar(cnext,currentfont)
+ if char and marks[char] then
+ insertedmarks=insertedmarks+1
+ cl=cnext
+ cnext=getnext(cnext)
+ else
+ break
+ end
end
if cnext then
setprev(cnext,backtrackdisc)
@@ -13311,8 +19591,7 @@ local function chaindisk(head,start,last,kind,chainname,ck,lookuphash,chainlooku
setnext(backtrackdisc,cnext)
setprev(cf,nil)
setnext(cl,nil)
- local replace=getfield(backtrackdisc,"replace")
- local post=getfield(backtrackdisc,"post")
+ local pre,post,replace,pretail,posttail,replacetail=getdisc(backtrackdisc,true)
local new=copy_node_list(cf)
local cnew=find_node_tail(new)
for i=1,insertedmarks do
@@ -13323,41 +19602,38 @@ local function chaindisk(head,start,last,kind,chainname,ck,lookuphash,chainlooku
clast=getnext(clast)
end
if not notmatchpost[backtrackdisc] then
- cf,start,ok=chainproc(cf,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence)
+ cf,start,ok=chainproc(cf,start,last,dataset,sequence,chainlookup,rlmode,k)
end
if not notmatchreplace[backtrackdisc] then
- new,cnew,ok=chainproc(new,cnew,clast,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence)
+ new,cnew,ok=chainproc(new,cnew,clast,dataset,sequence,chainlookup,rlmode,k)
end
if post then
- local tail=find_node_tail(post)
- setnext(tail,cf)
- setprev(cf,tail)
+ setlink(posttail,cf)
else
post=cf
end
if replace then
- local tail=find_node_tail(replace)
- setnext(tail,new)
- setprev(new,tail)
+ setlink(replacetail,new)
else
replace=new
end
- setfield(backtrackdisc,"post",post)
- setfield(backtrackdisc,"replace",replace)
+ setdisc(backtrackdisc,pre,post,replace)
start=getprev(backtrackdisc)
sweephead[post]=getnext(clast)
sweephead[replace]=getnext(last)
else
- head,start,ok=chainproc(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence)
+ head,start,ok=chainproc(head,start,last,dataset,sequence,chainlookup,rlmode,k)
end
return head,start,ok
end
-local function normal_handle_contextchain(head,start,kind,chainname,contexts,sequence,lookuphash)
+local noflags={ false,false,false,false }
+local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode)
local sweepnode=sweepnode
local sweeptype=sweeptype
+ local currentfont=currentfont
local diskseen=false
local checkdisc=getprev(head)
- local flags=sequence.flags
+ local flags=sequence.flags or noflags
local done=false
local skipmark=flags[1]
local skipligature=flags[2]
@@ -13372,7 +19648,10 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
local seq=ck[3]
local s=#seq
if s==1 then
- match=getid(current)==glyph_code and getfont(current)==currentfont and getsubtype(current)<256 and seq[1][getchar(current)]
+ local char=ischar(current,currentfont)
+ if char then
+ match=seq[1][char]
+ end
else
local f=ck[4]
local l=ck[5]
@@ -13389,33 +19668,22 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
sweeptype=nil
end
if last then
- local id=getid(last)
- if id==glyph_code then
- if getfont(last)==currentfont and getsubtype(last)<256 then
- local char=getchar(last)
- local ccd=descriptions[char]
- if ccd then
- local class=ccd.class or "base"
- if class==skipmark or class==skipligature or class==skipbase or (markclass and class=="mark" and not markclass[char]) then
- skipped=true
- if trace_skips then
- show_skip(kind,chainname,char,ck,class)
- end
+ local char,id=ischar(last,currentfont)
+ if char then
+ local ccd=descriptions[char]
+ if ccd then
+ local class=ccd.class or "base"
+ if class==skipmark or class==skipligature or class==skipbase or (markclass and class=="mark" and not markclass[char]) then
+ skipped=true
+ if trace_skips then
+ show_skip(dataset,sequence,char,ck,class)
+ end
+ last=getnext(last)
+ elseif seq[n][char] then
+ if n<l then
last=getnext(last)
- elseif seq[n][char] then
- if n<l then
- last=getnext(last)
- end
- n=n+1
- else
- if discfound then
- notmatchreplace[discfound]=true
- match=not notmatchpre[discfound]
- else
- match=false
- end
- break
end
+ n=n+1
else
if discfound then
notmatchreplace[discfound]=true
@@ -13434,14 +19702,22 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
end
break
end
+ last=getnext(last)
+ elseif char==false then
+ if discfound then
+ notmatchreplace[discfound]=true
+ match=not notmatchpre[discfound]
+ else
+ match=false
+ end
+ break
elseif id==disc_code then
diskseen=true
discfound=last
notmatchpre[last]=nil
notmatchpost[last]=true
notmatchreplace[last]=nil
- local pre=getfield(last,"pre")
- local replace=getfield(last,"replace")
+ local pre,post,replace=getdisc(last)
if pre then
local n=n
while pre do
@@ -13501,29 +19777,18 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
local n=f-1
while n>=1 do
if prev then
- local id=getid(prev)
- if id==glyph_code then
- if getfont(prev)==currentfont and getsubtype(prev)<256 then
- local char=getchar(prev)
- local ccd=descriptions[char]
- if ccd then
- local class=ccd.class
- if class==skipmark or class==skipligature or class==skipbase or (markclass and class=="mark" and not markclass[char]) then
- skipped=true
- if trace_skips then
- show_skip(kind,chainname,char,ck,class)
- end
- elseif seq[n][char] then
- n=n -1
- else
- if discfound then
- notmatchreplace[discfound]=true
- match=not notmatchpost[discfound]
- else
- match=false
- end
- break
+ local char,id=ischar(prev,currentfont)
+ if char then
+ local ccd=descriptions[char]
+ if ccd then
+ local class=ccd.class
+ if class==skipmark or class==skipligature or class==skipbase or (markclass and class=="mark" and not markclass[char]) then
+ skipped=true
+ if trace_skips then
+ show_skip(dataset,sequence,char,ck,class)
end
+ elseif seq[n][char] then
+ n=n -1
else
if discfound then
notmatchreplace[discfound]=true
@@ -13542,19 +19807,25 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
end
break
end
+ prev=getprev(prev)
+ elseif char==false then
+ if discfound then
+ notmatchreplace[discfound]=true
+ match=not notmatchpost[discfound]
+ else
+ match=false
+ end
+ break
elseif id==disc_code then
diskseen=true
discfound=prev
notmatchpre[prev]=true
notmatchpost[prev]=nil
notmatchreplace[prev]=nil
- local pre=getfield(prev,"pre")
- local post=getfield(prev,"post")
- local replace=getfield(prev,"replace")
+ local pre,post,replace,pretail,posttail,replacetail=getdisc(prev,true)
if pre~=start and post~=start and replace~=start then
if post then
local n=n
- local posttail=find_node_tail(post)
while posttail do
if seq[n][getchar(posttail)] then
n=n-1
@@ -13578,7 +19849,6 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
notmatchpost[prev]=true
end
if replace then
- local replacetail=find_node_tail(replace)
while replacetail do
if seq[n][getchar(replacetail)] then
n=n-1
@@ -13604,7 +19874,7 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
else
end
elseif seq[n][32] then
- n=n -1
+ n=n-1
else
match=false
break
@@ -13636,29 +19906,18 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
local n=l+1
while n<=s do
if current then
- local id=getid(current)
- if id==glyph_code then
- if getfont(current)==currentfont and getsubtype(current)<256 then
- local char=getchar(current)
- local ccd=descriptions[char]
- if ccd then
- local class=ccd.class
- if class==skipmark or class==skipligature or class==skipbase or (markclass and class=="mark" and not markclass[char]) then
- skipped=true
- if trace_skips then
- show_skip(kind,chainname,char,ck,class)
- end
- elseif seq[n][char] then
- n=n+1
- else
- if discfound then
- notmatchreplace[discfound]=true
- match=not notmatchpre[discfound]
- else
- match=false
- end
- break
+ local char,id=ischar(current,currentfont)
+ if char then
+ local ccd=descriptions[char]
+ if ccd then
+ local class=ccd.class
+ if class==skipmark or class==skipligature or class==skipbase or (markclass and class=="mark" and not markclass[char]) then
+ skipped=true
+ if trace_skips then
+ show_skip(dataset,sequence,char,ck,class)
end
+ elseif seq[n][char] then
+ n=n+1
else
if discfound then
notmatchreplace[discfound]=true
@@ -13677,14 +19936,22 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
end
break
end
+ current=getnext(current)
+ elseif char==false then
+ if discfound then
+ notmatchreplace[discfound]=true
+ match=not notmatchpre[discfound]
+ else
+ match=false
+ end
+ break
elseif id==disc_code then
diskseen=true
discfound=current
notmatchpre[current]=nil
notmatchpost[current]=true
notmatchreplace[current]=nil
- local pre=getfield(current,"pre")
- local replace=getfield(current,"replace")
+ local pre,post,replace=getdisc(current)
if pre then
local n=n
while pre do
@@ -13733,6 +20000,7 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
current=getnext(current)
elseif seq[n][32] then
n=n+1
+current=getnext(current)
else
match=false
break
@@ -13746,45 +20014,39 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
if match then
local diskchain=diskseen or sweepnode
if trace_contexts then
- local rule,lookuptype,f,l=ck[1],ck[2],ck[4],ck[5]
+ local rule=ck[1]
+ local lookuptype=ck[8] or ck[2]
+ local first=ck[4]
+ local last=ck[5]
local char=getchar(start)
- if ck[9] then
- logwarning("%s: rule %s matches at char %s for (%s,%s,%s) chars, lookuptype %a, %a => %a",
- cref(kind,chainname),rule,gref(char),f-1,l-f+1,s-l,lookuptype,ck[9],ck[10])
- else
- logwarning("%s: rule %s matches at char %s for (%s,%s,%s) chars, lookuptype %a",
- cref(kind,chainname),rule,gref(char),f-1,l-f+1,s-l,lookuptype)
- end
+ logwarning("%s: rule %s matches at char %s for (%s,%s,%s) chars, lookuptype %a",
+ cref(dataset,sequence),rule,gref(char),first-1,last-first+1,s-last,lookuptype)
end
local chainlookups=ck[6]
if chainlookups then
local nofchainlookups=#chainlookups
if nofchainlookups==1 then
- local chainlookupname=chainlookups[1]
- local chainlookup=lookuptable[chainlookupname]
- if chainlookup then
- local chainproc=chainprocs[chainlookup.type]
- if chainproc then
- local ok
- if diskchain then
- head,start,ok=chaindisk(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence,chainproc)
- else
- head,start,ok=chainproc(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence)
- end
- if ok then
- done=true
- end
+ local chainlookup=chainlookups[1]
+ local chainkind=chainlookup.type
+ local chainproc=chainprocs[chainkind]
+ if chainproc then
+ local ok
+ if diskchain then
+ head,start,ok=chaindisk(head,start,last,dataset,sequence,chainlookup,rlmode,1,ck,chainproc)
else
- logprocess("%s: %s is not yet supported",cref(kind,chainname,chainlookupname),chainlookup.type)
+ head,start,ok=chainproc(head,start,last,dataset,sequence,chainlookup,rlmode,1)
end
- else
- logprocess("%s is not yet supported",cref(kind,chainname,chainlookupname))
+ if ok then
+ done=true
+ end
+ else
+ logprocess("%s: %s is not yet supported (1)",cref(dataset,sequence),chainkind)
end
else
local i=1
while start and true do
if skipped then
- while true do
+ while start do
local char=getchar(start)
local ccd=descriptions[char]
if ccd then
@@ -13799,21 +20061,18 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
end
end
end
- local chainlookupname=chainlookups[i]
- local chainlookup=lookuptable[chainlookupname]
+ local chainlookup=chainlookups[1]
if not chainlookup then
- i=i+1
+ i=i+1
else
- local chainproc=chainprocs[chainlookup.type]
- if not chainproc then
- logprocess("%s: %s is not yet supported",cref(kind,chainname,chainlookupname),chainlookup.type)
- i=i+1
- else
+ local chainkind=chainlookup.type
+ local chainproc=chainprocs[chainkind]
+ if chainproc then
local ok,n
if diskchain then
- head,start,ok=chaindisk(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence,chainproc)
+ head,start,ok=chaindisk(head,start,last,dataset,sequence,chainlookup,rlmode,i,ck,chainproc)
else
- head,start,ok,n=chainproc(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,i,sequence)
+ head,start,ok,n=chainproc(head,start,last,dataset,sequence,chainlookup,rlmode,i)
end
if ok then
done=true
@@ -13824,8 +20083,10 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
end
end
end
- i=i+1
+ else
+ logprocess("%s: %s is not yet supported (2)",cref(dataset,sequence),chainkind)
end
+ i=i+1
end
if i>nofchainlookups or not start then
break
@@ -13837,11 +20098,11 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
else
local replacements=ck[7]
if replacements then
- head,start,done=chainprocs.reversesub(head,start,last,kind,chainname,ck,lookuphash,replacements)
+ head,start,done=reversesub(head,start,last,dataset,sequence,replacements,rlmode)
else
done=quit_on_no_replacement
if trace_contexts then
- logprocess("%s: skipping match",cref(kind,chainname))
+ logprocess("%s: skipping match",cref(dataset,sequence))
end
end
end
@@ -13850,58 +20111,32 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
end
end
end
- if diskseen then
+ if diskseen then
notmatchpre={}
notmatchpost={}
notmatchreplace={}
end
return head,start,done
end
-local verbose_handle_contextchain=function(font,...)
- logwarning("no verbose handler installed, reverting to 'normal'")
- otf.setcontextchain()
- return normal_handle_contextchain(...)
-end
-otf.chainhandlers={
- normal=normal_handle_contextchain,
- verbose=verbose_handle_contextchain,
-}
-local handle_contextchain=nil
-function chained_contextchain(head,start,stop,...)
+handlers.gsub_context=handle_contextchain
+handlers.gsub_contextchain=handle_contextchain
+handlers.gsub_reversecontextchain=handle_contextchain
+handlers.gpos_contextchain=handle_contextchain
+handlers.gpos_context=handle_contextchain
+local function chained_contextchain(head,start,stop,dataset,sequence,currentlookup,rlmode)
local steps=currentlookup.steps
local nofsteps=currentlookup.nofsteps
if nofsteps>1 then
reportmoresteps(dataset,sequence)
end
- return handle_contextchain(head,start,...)
-end
-function otf.setcontextchain(method)
- if not method or method=="normal" or not otf.chainhandlers[method] then
- if handle_contextchain then
- logwarning("installing normal contextchain handler")
- end
- handle_contextchain=normal_handle_contextchain
- else
- logwarning("installing contextchain handler %a",method)
- local handler=otf.chainhandlers[method]
- handle_contextchain=function(...)
- return handler(currentfont,...)
- end
- end
- handlers.gsub_context=handle_contextchain
- handlers.gsub_contextchain=handle_contextchain
- handlers.gsub_reversecontextchain=handle_contextchain
- handlers.gpos_contextchain=handle_contextchain
- handlers.gpos_context=handle_contextchain
- handlers.contextchain=handle_contextchain
+ return handle_contextchain(head,start,dataset,sequence,currentlookup,rlmode)
end
chainprocs.gsub_context=chained_contextchain
chainprocs.gsub_contextchain=chained_contextchain
chainprocs.gsub_reversecontextchain=chained_contextchain
chainprocs.gpos_contextchain=chained_contextchain
chainprocs.gpos_context=chained_contextchain
-otf.setcontextchain()
-local missing={}
+local missing=setmetatableindex("table")
local function logprocess(...)
if trace_steps then
registermessage(...)
@@ -13909,23 +20144,22 @@ local function logprocess(...)
report_process(...)
end
local logwarning=report_process
-local function report_missing_cache(typ,lookup)
- local f=missing[currentfont] if not f then f={} missing[currentfont]=f end
- local t=f[typ] if not t then t={} f[typ]=t end
- if not t[lookup] then
- t[lookup]=true
- logwarning("missing cache for lookup %a, type %a, font %a, name %a",lookup,typ,currentfont,tfmdata.properties.fullname)
+local function report_missing_coverage(dataset,sequence)
+ local t=missing[currentfont]
+ if not t[sequence] then
+ t[sequence]=true
+ logwarning("missing coverage for feature %a, lookup %a, type %a, font %a, name %a",
+ dataset[4],sequence.name,sequence.type,currentfont,tfmdata.properties.fullname)
end
end
local resolved={}
-local lookuphashes={}
-setmetatableindex(lookuphashes,function(t,font)
- local lookuphash=fontdata[font].resources.lookuphash
- if not lookuphash or not next(lookuphash) then
- lookuphash=false
- end
- t[font]=lookuphash
- return lookuphash
+local sequencelists=setmetatableindex(function(t,font)
+ local sequences=fontdata[font].resources.sequences
+ if not sequences or not next(sequences) then
+ sequences=false
+ end
+ t[font]=sequences
+ return sequences
end)
local autofeatures=fonts.analyzers.features
local featuretypes=otf.tables.featuretypes
@@ -13995,244 +20229,501 @@ function otf.dataset(tfmdata,font)
end
return rl
end
-local function kernrun(disc,run)
+local function report_disc(n)
+ report_run("kern: %s > %s",disc,languages.serializediscretionary(disc))
+end
+local function kernrun(disc,k_run,font,attr,...)
if trace_kernruns then
- report_run("kern")
+ report_disc("kern")
end
- local prev=getprev(disc)
- local next=getnext(disc)
- local pre=getfield(disc,"pre")
- local post=getfield(disc,"post")
- local replace=getfield(disc,"replace")
+ local prev,next=getboth(disc)
+ local nextstart=next
+ local done=false
+ local pre,post,replace,pretail,posttail,replacetail=getdisc(disc,true)
local prevmarks=prev
- while prevmarks and getid(prevmarks)==glyph_code and marks[getchar(prevmarks)] and getfont(prevmarks)==currentfont and getsubtype(prevmarks)<256 do
- prevmarks=getprev(prevmarks)
+ while prevmarks do
+ local char=ischar(prevmarks,font)
+ if char and marks[char] then
+ prevmarks=getprev(prevmarks)
+ else
+ break
+ end
end
- if prev and (pre or replace) and not (getid(prev)==glyph_code and getfont(prev)==currentfont and getsubtype(prev)<256) then
+ if prev and (pre or replace) and not ischar(prev,font) then
prev=false
end
- if next and (post or replace) and not (getid(next)==glyph_code and getfont(next)==currentfont and getsubtype(next)<256) then
+ if next and (post or replace) and not ischar(next,font) then
next=false
end
- if not pre then
- elseif prev then
- local nest=getprev(pre)
- setprev(pre,prev)
- setnext(prev,pre)
- run(prevmarks,"preinjections")
- setprev(pre,nest)
- setnext(prev,disc)
- else
- run(pre,"preinjections")
- end
- if not post then
- elseif next then
- local tail=find_node_tail(post)
- setnext(tail,next)
- setprev(next,tail)
- run(post,"postinjections",next)
- setnext(tail,nil)
- setprev(next,disc)
- else
- run(post,"postinjections")
- end
- if not replace and prev and next then
- setnext(prev,next)
- setprev(next,prev)
- run(prevmarks,"injections",next)
- setnext(prev,disc)
- setprev(next,disc)
+ if pre then
+ if k_run(pre,"injections",nil,font,attr,...) then
+ done=true
+ end
+ if prev then
+ local nest=getprev(pre)
+ setlink(prev,pre)
+ if k_run(prevmarks,"preinjections",pre,font,attr,...) then
+ done=true
+ end
+ setprev(pre,nest)
+ setnext(prev,disc)
+ end
+ end
+ if post then
+ if k_run(post,"injections",nil,font,attr,...) then
+ done=true
+ end
+ if next then
+ setlink(posttail,next)
+ if k_run(posttail,"postinjections",next,font,attr,...) then
+ done=true
+ end
+ setnext(posttail,nil)
+ setprev(next,disc)
+ end
+ end
+ if replace then
+ if k_run(replace,"injections",nil,font,attr,...) then
+ done=true
+ end
+ if prev then
+ local nest=getprev(replace)
+ setlink(prev,replace)
+ if k_run(prevmarks,"replaceinjections",replace,font,attr,...) then
+ done=true
+ end
+ setprev(replace,nest)
+ setnext(prev,disc)
+ end
+ if next then
+ setlink(replacetail,next)
+ if k_run(replacetail,"replaceinjections",next,font,attr,...) then
+ done=true
+ end
+ setnext(replacetail,nil)
+ setprev(next,disc)
+ end
elseif prev and next then
- local tail=find_node_tail(replace)
- local nest=getprev(replace)
- setprev(replace,prev)
- setnext(prev,replace)
- setnext(tail,next)
- setprev(next,tail)
- run(prevmarks,"replaceinjections",next)
- setprev(replace,nest)
- setnext(prev,disc)
- setnext(tail,nil)
- setprev(next,disc)
- elseif prev then
- local nest=getprev(replace)
- setprev(replace,prev)
- setnext(prev,replace)
- run(prevmarks,"replaceinjections")
- setprev(replace,nest)
- setnext(prev,disc)
- elseif next then
- local tail=find_node_tail(replace)
- setnext(tail,next)
- setprev(next,tail)
- run(replace,"replaceinjections",next)
- setnext(tail,nil)
- setprev(next,disc)
- else
- run(replace,"replaceinjections")
+ setlink(prev,next)
+ if k_run(prevmarks,"emptyinjections",next,font,attr,...) then
+ done=true
+ end
+ setlink(prev,disc)
+ setlink(disc,next)
end
+ return nextstart,done
end
-local function comprun(disc,run)
+local function comprun(disc,c_run,...)
if trace_compruns then
- report_run("comp: %s",languages.serializediscretionary(disc))
+ report_disc("comp")
end
- local pre=getfield(disc,"pre")
+ local pre,post,replace=getdisc(disc)
+ local renewed=false
if pre then
sweepnode=disc
sweeptype="pre"
- local new,done=run(pre)
+ local new,done=c_run(pre,...)
if done then
- setfield(disc,"pre",new)
+ pre=new
+ renewed=true
end
end
- local post=getfield(disc,"post")
if post then
sweepnode=disc
sweeptype="post"
- local new,done=run(post)
+ local new,done=c_run(post,...)
if done then
- setfield(disc,"post",new)
+ post=new
+ renewed=true
end
end
- local replace=getfield(disc,"replace")
if replace then
sweepnode=disc
sweeptype="replace"
- local new,done=run(replace)
+ local new,done=c_run(replace,...)
if done then
- setfield(disc,"replace",new)
+ replace=new
+ renewed=true
end
end
sweepnode=nil
sweeptype=nil
+ if renewed then
+ setdisc(disc,pre,post,replace)
+ end
+ return getnext(disc),done
end
-local function testrun(disc,trun,crun)
- local next=getnext(disc)
- if next then
- local replace=getfield(disc,"replace")
- if replace then
- local prev=getprev(disc)
- if prev then
- local tail=find_node_tail(replace)
- setnext(tail,next)
- setprev(next,tail)
- if trun(replace,next) then
- setfield(disc,"replace",nil)
- setnext(prev,replace)
- setprev(replace,prev)
- setprev(next,tail)
- setnext(tail,next)
- setprev(disc,nil)
- setnext(disc,nil)
- flush_node_list(disc)
- return replace
- else
- setnext(tail,nil)
- setprev(next,disc)
+local function testrun(disc,t_run,c_run,...)
+ if trace_testruns then
+ report_disc("test")
+ end
+ local prev,next=getboth(disc)
+ if not next then
+ return
+ end
+ local pre,post,replace,pretail,posttail,replacetail=getdisc(disc)
+ local done=false
+ if replace and prev then
+ setlink(replacetail,next)
+ if t_run(replace,next,...) then
+ setfield(disc,"replace",nil)
+ setlink(prev,replace)
+ setlink(replacetail,next)
+ setboth(disc)
+ flush_node_list(disc)
+ return replace,true
+ else
+ setnext(replacetail)
+ setprev(next,disc)
+ end
+ end
+ local renewed=false
+ if pre then
+ sweepnode=disc
+ sweeptype="pre"
+ local new,ok=c_run(pre,...)
+ if ok then
+ pre=new
+ renewed=true
+ end
+ end
+ if post then
+ sweepnode=disc
+ sweeptype="post"
+ local new,ok=c_run(post,...)
+ if ok then
+ post=new
+ renewed=true
+ end
+ end
+ if replace then
+ sweepnode=disc
+ sweeptype="replace"
+ local new,ok=c_run(replace,...)
+ if ok then
+ replace=new
+ renewed=true
+ end
+ end
+ sweepnode=nil
+ sweeptype=nil
+ if renewed then
+ setdisc(disc,pre,post,replace)
+ return next,true
+ else
+ return next,done
+ end
+end
+local nesting=0
+local function c_run_single(head,font,attr,lookupcache,step,dataset,sequence,rlmode,handler)
+ local done=false
+ local start=sweephead[head]
+ if start then
+ sweephead[head]=nil
+ else
+ start=head
+ end
+ while start do
+ local char=ischar(start,font)
+ if char then
+ local a=getattr(start,0)
+ if not a or (a==attr) then
+ local lookupmatch=lookupcache[char]
+ if lookupmatch then
+ local ok
+ head,start,ok=handler(head,start,dataset,sequence,lookupmatch,rlmode,step,1)
+ if ok then
+ done=true
+ end
+ end
+ if start then
+ start=getnext(start)
end
else
+ start=getnext(start)
end
+ elseif char==false then
+ return head,done
else
+ start=getnext(start)
+ end
+ end
+ return head,done
+end
+local function t_run_single(start,stop,font,attr,lookupcache)
+ while start~=stop do
+ local char=ischar(start,font)
+ if char then
+ local a=getattr(start,0)
+ if not a or (a==attr) then
+ local lookupmatch=lookupcache[char]
+ if lookupmatch then
+ local s=getnext(start)
+ local l=nil
+ while s do
+ local lg=lookupmatch[getchar(s)]
+ if lg then
+ l=lg
+ s=getnext(s)
+ else
+ break
+ end
+ end
+ if l and l.ligature then
+ return true
+ end
+ end
+ end
+ start=getnext(start)
+ else
+ break
+ end
+ end
+end
+local function k_run_single(sub,injection,last,font,attr,lookupcache,step,dataset,sequence,rlmode,handler)
+ local a=getattr(sub,0)
+ if not a or (a==attr) then
+ for n in traverse_nodes(sub) do
+ if n==last then
+ break
+ end
+ local char=ischar(n)
+ if char then
+ local lookupmatch=lookupcache[char]
+ if lookupmatch then
+ local h,d,ok=handler(sub,n,dataset,sequence,lookupmatch,rlmode,step,1,injection)
+ if ok then
+ return true
+ end
+ end
+ end
end
+ end
+end
+local function c_run_multiple(head,font,attr,steps,nofsteps,dataset,sequence,rlmode,handler)
+ local done=false
+ local start=sweephead[head]
+ if start then
+ sweephead[head]=nil
else
+ start=head
+ end
+ while start do
+ local char=ischar(start,font)
+ if char then
+ local a=getattr(start,0)
+ if not a or (a==attr) then
+ for i=1,nofsteps do
+ local step=steps[i]
+ local lookupcache=step.coverage
+ if lookupcache then
+ local lookupmatch=lookupcache[char]
+ if lookupmatch then
+ local ok
+ head,start,ok=handler(head,start,dataset,sequence,lookupmatch,rlmode,step,i)
+ if ok then
+ done=true
+ break
+ elseif not start then
+ break
+ end
+ end
+ else
+ report_missing_coverage(dataset,sequence)
+ end
+ end
+ if start then
+ start=getnext(start)
+ end
+ else
+ start=getnext(start)
+ end
+ elseif char==false then
+ return head,done
+ else
+ start=getnext(start)
+ end
end
- comprun(disc,crun)
- return next
+ return head,done
end
-local function discrun(disc,drun,krun)
- local next=getnext(disc)
- local prev=getprev(disc)
- if trace_discruns then
- report_run("disc")
+local function t_run_multiple(start,stop,font,attr,steps,nofsteps)
+ while start~=stop do
+ local char=ischar(start,font)
+ if char then
+ local a=getattr(start,0)
+ if not a or (a==attr) then
+ for i=1,nofsteps do
+ local step=steps[i]
+ local lookupcache=step.coverage
+ if lookupcache then
+ local lookupmatch=lookupcache[char]
+ if lookupmatch then
+ local s=getnext(start)
+ local l=nil
+ while s do
+ local lg=lookupmatch[getchar(s)]
+ if lg then
+ l=lg
+ s=getnext(s)
+ else
+ break
+ end
+ end
+ if l and l.ligature then
+ return true
+ end
+ end
+ else
+ report_missing_coverage(dataset,sequence)
+ end
+ end
+ end
+ start=getnext(start)
+ else
+ break
+ end
end
- if next and prev then
- setnext(prev,next)
- drun(prev)
- setnext(prev,disc)
+end
+local function k_run_multiple(sub,injection,last,font,attr,steps,nofsteps,dataset,sequence,rlmode,handler)
+ local a=getattr(sub,0)
+ if not a or (a==attr) then
+ for n in traverse_nodes(sub) do
+ if n==last then
+ break
+ end
+ local char=ischar(n)
+ if char then
+ for i=1,nofsteps do
+ local step=steps[i]
+ local lookupcache=step.coverage
+ if lookupcache then
+ local lookupmatch=lookupcache[char]
+ if lookupmatch then
+ local h,d,ok=handler(head,n,dataset,sequence,lookupmatch,step,rlmode,i,injection)
+ if ok then
+ return true
+ end
+ end
+ else
+ report_missing_coverage(dataset,sequence)
+ end
+ end
+ end
+ end
end
- local pre=getfield(disc,"pre")
- if not pre then
- elseif prev then
- local nest=getprev(pre)
- setprev(pre,prev)
- setnext(prev,pre)
- krun(prev,"preinjections")
- setprev(pre,nest)
- setnext(prev,disc)
+end
+local function txtdirstate(start,stack,top,rlparmode)
+ local dir=getfield(start,"dir")
+ local new=1
+ if dir=="+TRT" then
+ top=top+1
+ stack[top]=dir
+ new=-1
+ elseif dir=="+TLT" then
+ top=top+1
+ stack[top]=dir
+ elseif dir=="-TRT" or dir=="-TLT" then
+ top=top-1
+ if stack[top]=="+TRT" then
+ new=-1
+ end
else
- krun(pre,"preinjections")
+ new=rlparmode
end
- return next
+ if trace_directions then
+ report_process("directions after txtdir %a: parmode %a, txtmode %a, level %a",dir,mref(rlparmode),mref(new),topstack)
+ end
+ return getnext(start),top,new
+end
+local function pardirstate(start)
+ local dir=getfield(start,"dir")
+ local new=0
+ if dir=="TLT" then
+ new=1
+ elseif dir=="TRT" then
+ new=-1
+ end
+ if trace_directions then
+ report_process("directions after pardir %a: parmode %a",dir,mref(new))
+ end
+ return getnext(start),new,new
end
local function featuresprocessor(head,font,attr)
- local lookuphash=lookuphashes[font]
- if not lookuphash then
+ local sequences=sequencelists[font]
+ if not sequencelists then
+ return head,false
+ end
+ nesting=nesting+1
+ if nesting==1 then
+ currentfont=font
+ tfmdata=fontdata[font]
+ descriptions=tfmdata.descriptions
+ characters=tfmdata.characters
+ marks=tfmdata.resources.marks
+ factor=tfmdata.parameters.factor
+ threshold=tfmdata.parameters.spacing.width or 65536*10
+ elseif currentfont~=font then
+ report_warning("nested call with a different font, level %s, quitting",nesting)
+ nesting=nesting-1
return head,false
end
head=tonut(head)
if trace_steps then
checkstep(head)
end
- tfmdata=fontdata[font]
- descriptions=tfmdata.descriptions
- characters=tfmdata.characters
- resources=tfmdata.resources
- marks=resources.marks
- anchorlookups=resources.lookup_to_anchor
- lookuptable=resources.lookups
- lookuptypes=resources.lookuptypes
- lookuptags=resources.lookuptags
- currentfont=font
- rlmode=0
- sweephead={}
- local sequences=resources.sequences
+ local rlmode=0
local done=false
local datasets=otf.dataset(tfmdata,font,attr)
- local dirstack={}
+ local dirstack={}
+ sweephead={}
for s=1,#datasets do
local dataset=datasets[s]
- featurevalue=dataset[1]
local attribute=dataset[2]
local sequence=dataset[3]
- local kind=dataset[4]
local rlparmode=0
local topstack=0
local success=false
local typ=sequence.type
local gpossing=typ=="gpos_single" or typ=="gpos_pair"
- local subtables=sequence.subtables
local handler=handlers[typ]
- if typ=="gsub_reversecontextchain" then
- local start=find_node_tail(head)
+ local steps=sequence.steps
+ local nofsteps=sequence.nofsteps
+ if not steps then
+ local h,d,ok=handler(head,start,dataset,sequence,nil,nil,nil,0,font,attr)
+ if ok then
+ success=true
+ if h then
+ head=h
+ end
+ if d then
+ start=d
+ end
+ end
+ elseif typ=="gsub_reversecontextchain" then
+ local start=find_node_tail(head)
while start do
- local id=getid(start)
- if id==glyph_code then
- if getfont(start)==font and getsubtype(start)<256 then
- local a=getattr(start,0)
- if a then
- a=a==attr
- else
- a=true
- end
- if a then
- local char=getchar(start)
- for i=1,#subtables do
- local lookupname=subtables[i]
- local lookupcache=lookuphash[lookupname]
- if lookupcache then
- local lookupmatch=lookupcache[char]
- if lookupmatch then
- head,start,success=handler(head,start,kind,lookupname,lookupmatch,sequence,lookuphash,i)
- if success then
- break
- end
+ local char=ischar(start,font)
+ if char then
+ local a=getattr(start,0)
+ if not a or (a==attr) then
+ for i=1,nofsteps do
+ local step=steps[i]
+ local lookupcache=step.coverage
+ if lookupcache then
+ local lookupmatch=lookupcache[char]
+ if lookupmatch then
+ local ok
+ head,start,ok=handler(head,start,dataset,sequence,lookupmatch,rlmode,step,i)
+ if ok then
+ success=true
+ break
end
- else
- report_missing_cache(typ,lookupname)
end
+ else
+ report_missing_coverage(dataset,sequence)
end
- if start then start=getprev(start) end
- else
+ end
+ if start then
start=getprev(start)
end
else
@@ -14243,467 +20734,119 @@ local function featuresprocessor(head,font,attr)
end
end
else
- local ns=#subtables
local start=head
rlmode=0
- if ns==1 then
- local lookupname=subtables[1]
- local lookupcache=lookuphash[lookupname]
- if not lookupcache then
- report_missing_cache(typ,lookupname)
+ if nofsteps==1 then
+ local step=steps[1]
+ local lookupcache=step.coverage
+ if not lookupcache then
+ report_missing_coverage(dataset,sequence)
else
- local function c_run(head)
- local done=false
- local start=sweephead[head]
- if start then
- sweephead[head]=nil
- else
- start=head
- end
- while start do
- local id=getid(start)
- if id~=glyph_code then
- start=getnext(start)
- elseif getfont(start)==font and getsubtype(start)<256 then
- local a=getattr(start,0)
- if a then
- a=(a==attr) and (not attribute or getprop(start,a_state)==attribute)
- else
- a=not attribute or getprop(start,a_state)==attribute
- end
- if a then
- local lookupmatch=lookupcache[getchar(start)]
- if lookupmatch then
- local ok
- head,start,ok=handler(head,start,kind,lookupname,lookupmatch,sequence,lookuphash,1)
- if ok then
- done=true
- end
- end
- if start then start=getnext(start) end
- else
- start=getnext(start)
- end
- else
- return head,false
- end
- end
- if done then
- success=true
- end
- return head,done
- end
- local function t_run(start,stop)
- while start~=stop do
- local id=getid(start)
- if id==glyph_code and getfont(start)==font and getsubtype(start)<256 then
- local a=getattr(start,0)
- if a then
- a=(a==attr) and (not attribute or getprop(start,a_state)==attribute)
- else
- a=not attribute or getprop(start,a_state)==attribute
- end
- if a then
- local lookupmatch=lookupcache[getchar(start)]
- if lookupmatch then
- local s=getnext(start)
- local l=nil
- while s do
- local lg=lookupmatch[getchar(s)]
- if lg then
- l=lg
- s=getnext(s)
- else
- break
- end
- end
- if l and l.ligature then
- return true
- end
- end
- end
- start=getnext(start)
+ while start do
+ local char,id=ischar(start,font)
+ if char then
+ local a=getattr(start,0)
+ if a then
+ a=(a==attr) and (not attribute or getprop(start,a_state)==attribute)
else
- break
- end
- end
- end
- local function d_run(prev)
- local a=getattr(prev,0)
- if a then
- a=(a==attr) and (not attribute or getprop(prev,a_state)==attribute)
- else
- a=not attribute or getprop(prev,a_state)==attribute
- end
- if a then
- local lookupmatch=lookupcache[getchar(prev)]
- if lookupmatch then
- local h,d,ok=handler(head,prev,kind,lookupname,lookupmatch,sequence,lookuphash,1)
- if ok then
- done=true
- success=true
- end
+ a=not attribute or getprop(start,a_state)==attribute
end
- end
- end
- local function k_run(sub,injection,last)
- local a=getattr(sub,0)
- if a then
- a=(a==attr) and (not attribute or getprop(sub,a_state)==attribute)
- else
- a=not attribute or getprop(sub,a_state)==attribute
- end
- if a then
- for n in traverse_nodes(sub) do
- if n==last then
- break
- end
- local id=getid(n)
- if id==glyph_code then
- local lookupmatch=lookupcache[getchar(n)]
- if lookupmatch then
- local h,d,ok=handler(sub,n,kind,lookupname,lookupmatch,sequence,lookuphash,1,injection)
- if ok then
- done=true
- success=true
- end
+ if a then
+ local lookupmatch=lookupcache[char]
+ if lookupmatch then
+ local ok
+ head,start,ok=handler(head,start,dataset,sequence,lookupmatch,rlmode,step,1)
+ if ok then
+ success=true
end
- else
end
- end
- end
- end
- while start do
- local id=getid(start)
- if id==glyph_code then
- if getfont(start)==font and getsubtype(start)<256 then
- local a=getattr(start,0)
- if a then
- a=(a==attr) and (not attribute or getprop(start,a_state)==attribute)
- else
- a=not attribute or getprop(start,a_state)==attribute
- end
- if a then
- local char=getchar(start)
- local lookupmatch=lookupcache[char]
- if lookupmatch then
- local ok
- head,start,ok=handler(head,start,kind,lookupname,lookupmatch,sequence,lookuphash,1)
- if ok then
- success=true
- elseif gpossing and zwnjruns and char==zwnj then
- discrun(start,d_run)
- end
- elseif gpossing and zwnjruns and char==zwnj then
- discrun(start,d_run)
- end
- if start then start=getnext(start) end
- else
+ if start then
start=getnext(start)
end
else
start=getnext(start)
end
+ elseif char==false then
+ start=getnext(start)
elseif id==disc_code then
+ local ok
if gpossing then
- kernrun(start,k_run)
- start=getnext(start)
+ start,ok=kernrun(start,k_run_single,font,attr,lookupcache,step,dataset,sequence,rlmode,handler)
elseif typ=="gsub_ligature" then
- start=testrun(start,t_run,c_run)
+ start,ok=testrun(start,t_run_single,c_run_single,font,attr,lookupcache,step,dataset,sequence,rlmode,handler)
else
- comprun(start,c_run)
- start=getnext(start)
+ start,ok=comprun(start,c_run_single,font,attr,lookupcache,step,dataset,sequence,rlmode,handler)
+ end
+ if ok then
+ success=true
end
elseif id==math_code then
start=getnext(end_of_math(start))
elseif id==dir_code then
- local dir=getfield(start,"dir")
- if dir=="+TLT" then
- topstack=topstack+1
- dirstack[topstack]=dir
- rlmode=1
- elseif dir=="+TRT" then
- topstack=topstack+1
- dirstack[topstack]=dir
- rlmode=-1
- elseif dir=="-TLT" or dir=="-TRT" then
- topstack=topstack-1
- rlmode=dirstack[topstack]=="+TRT" and -1 or 1
- else
- rlmode=rlparmode
- end
- if trace_directions then
- report_process("directions after txtdir %a: parmode %a, txtmode %a, # stack %a, new dir %a",dir,rlparmode,rlmode,topstack,newdir)
- end
- start=getnext(start)
+ start,topstack,rlmode=txtdirstate(start,dirstack,topstack,rlparmode)
elseif id==localpar_code then
- local dir=getfield(start,"dir")
- if dir=="TRT" then
- rlparmode=-1
- elseif dir=="TLT" then
- rlparmode=1
- else
- rlparmode=0
- end
- rlmode=rlparmode
- if trace_directions then
- report_process("directions after pardir %a: parmode %a, txtmode %a",dir,rlparmode,rlmode)
- end
- start=getnext(start)
+ start,rlparmode,rlmode=pardirstate(start)
else
start=getnext(start)
end
end
end
else
- local function c_run(head)
- local done=false
- local start=sweephead[head]
- if start then
- sweephead[head]=nil
- else
- start=head
- end
- while start do
- local id=getid(start)
- if id~=glyph_code then
- start=getnext(start)
- elseif getfont(start)==font and getsubtype(start)<256 then
- local a=getattr(start,0)
- if a then
- a=(a==attr) and (not attribute or getprop(start,a_state)==attribute)
- else
- a=not attribute or getprop(start,a_state)==attribute
- end
- if a then
- local char=getchar(start)
- for i=1,ns do
- local lookupname=subtables[i]
- local lookupcache=lookuphash[lookupname]
- if lookupcache then
- local lookupmatch=lookupcache[char]
- if lookupmatch then
- local ok
- head,start,ok=handler(head,start,kind,lookupname,lookupmatch,sequence,lookuphash,i)
- if ok then
- done=true
- break
- elseif not start then
- break
- end
- end
- else
- report_missing_cache(typ,lookupname)
- end
- end
- if start then start=getnext(start) end
- else
- start=getnext(start)
- end
+ while start do
+ local char,id=ischar(start,font)
+ if char then
+ local a=getattr(start,0)
+ if a then
+ a=(a==attr) and (not attribute or getprop(start,a_state)==attribute)
else
- return head,false
- end
- end
- if done then
- success=true
- end
- return head,done
- end
- local function d_run(prev)
- local a=getattr(prev,0)
- if a then
- a=(a==attr) and (not attribute or getprop(prev,a_state)==attribute)
- else
- a=not attribute or getprop(prev,a_state)==attribute
- end
- if a then
- local char=getchar(prev)
- for i=1,ns do
- local lookupname=subtables[i]
- local lookupcache=lookuphash[lookupname]
- if lookupcache then
- local lookupmatch=lookupcache[char]
- if lookupmatch then
- local h,d,ok=handler(head,prev,kind,lookupname,lookupmatch,sequence,lookuphash,i)
- if ok then
- done=true
- break
- end
- end
- else
- report_missing_cache(typ,lookupname)
- end
+ a=not attribute or getprop(start,a_state)==attribute
end
- end
- end
- local function k_run(sub,injection,last)
- local a=getattr(sub,0)
- if a then
- a=(a==attr) and (not attribute or getprop(sub,a_state)==attribute)
- else
- a=not attribute or getprop(sub,a_state)==attribute
- end
- if a then
- for n in traverse_nodes(sub) do
- if n==last then
- break
- end
- local id=getid(n)
- if id==glyph_code then
- local char=getchar(n)
- for i=1,ns do
- local lookupname=subtables[i]
- local lookupcache=lookuphash[lookupname]
- if lookupcache then
- local lookupmatch=lookupcache[char]
- if lookupmatch then
- local h,d,ok=handler(head,n,kind,lookupname,lookupmatch,sequence,lookuphash,i,injection)
- if ok then
- done=true
- break
- end
- end
- else
- report_missing_cache(typ,lookupname)
- end
- end
- else
- end
- end
- end
- end
- local function t_run(start,stop)
- while start~=stop do
- local id=getid(start)
- if id==glyph_code and getfont(start)==font and getsubtype(start)<256 then
- local a=getattr(start,0)
- if a then
- a=(a==attr) and (not attribute or getprop(start,a_state)==attribute)
- else
- a=not attribute or getprop(start,a_state)==attribute
- end
- if a then
- local char=getchar(start)
- for i=1,ns do
- local lookupname=subtables[i]
- local lookupcache=lookuphash[lookupname]
- if lookupcache then
- local lookupmatch=lookupcache[char]
- if lookupmatch then
- local s=getnext(start)
- local l=nil
- while s do
- local lg=lookupmatch[getchar(s)]
- if lg then
- l=lg
- s=getnext(s)
- else
- break
- end
- end
- if l and l.ligature then
- return true
- end
+ if a then
+ for i=1,nofsteps do
+ local step=steps[i]
+ local lookupcache=step.coverage
+ if lookupcache then
+ local lookupmatch=lookupcache[char]
+ if lookupmatch then
+ local ok
+ head,start,ok=handler(head,start,dataset,sequence,lookupmatch,rlmode,step,i)
+ if ok then
+ success=true
+ break
+ elseif not start then
+ break
end
- else
- report_missing_cache(typ,lookupname)
end
+ else
+ report_missing_coverage(dataset,sequence)
end
end
- start=getnext(start)
- else
- break
- end
- end
- end
- while start do
- local id=getid(start)
- if id==glyph_code then
- if getfont(start)==font and getsubtype(start)<256 then
- local a=getattr(start,0)
- if a then
- a=(a==attr) and (not attribute or getprop(start,a_state)==attribute)
- else
- a=not attribute or getprop(start,a_state)==attribute
- end
- if a then
- for i=1,ns do
- local lookupname=subtables[i]
- local lookupcache=lookuphash[lookupname]
- if lookupcache then
- local char=getchar(start)
- local lookupmatch=lookupcache[char]
- if lookupmatch then
- local ok
- head,start,ok=handler(head,start,kind,lookupname,lookupmatch,sequence,lookuphash,i)
- if ok then
- success=true
- break
- elseif not start then
- break
- elseif gpossing and zwnjruns and char==zwnj then
- discrun(start,d_run)
- end
- elseif gpossing and zwnjruns and char==zwnj then
- discrun(start,d_run)
- end
- else
- report_missing_cache(typ,lookupname)
- end
- end
- if start then start=getnext(start) end
- else
+ if start then
start=getnext(start)
end
else
start=getnext(start)
end
+ elseif char==false then
+ start=getnext(start)
elseif id==disc_code then
+ local ok
if gpossing then
- kernrun(start,k_run)
- start=getnext(start)
+ start,ok=kernrun(start,k_run_multiple,font,attr,steps,nofsteps,dataset,sequence,rlmode,handler)
elseif typ=="gsub_ligature" then
- start=testrun(start,t_run,c_run)
+ start,ok=testrun(start,t_run_multiple,c_run_multiple,font,attr,steps,nofsteps,dataset,sequence,rlmode,handler)
else
- comprun(start,c_run)
- start=getnext(start)
+ start,ok=comprun(start,c_run_multiple,font,attr,steps,nofsteps,dataset,sequence,rlmode,handler)
+ end
+ if ok then
+ success=true
end
elseif id==math_code then
start=getnext(end_of_math(start))
elseif id==dir_code then
- local dir=getfield(start,"dir")
- if dir=="+TLT" then
- topstack=topstack+1
- dirstack[topstack]=dir
- rlmode=1
- elseif dir=="+TRT" then
- topstack=topstack+1
- dirstack[topstack]=dir
- rlmode=-1
- elseif dir=="-TLT" or dir=="-TRT" then
- topstack=topstack-1
- rlmode=dirstack[topstack]=="+TRT" and -1 or 1
- else
- rlmode=rlparmode
- end
- if trace_directions then
- report_process("directions after txtdir %a: parmode %a, txtmode %a, # stack %a, new dir %a",dir,rlparmode,rlmode,topstack,newdir)
- end
- start=getnext(start)
+ start,topstack,rlmode=txtdirstate(start,dirstack,topstack,rlparmode)
elseif id==localpar_code then
- local dir=getfield(start,"dir")
- if dir=="TRT" then
- rlparmode=-1
- elseif dir=="TLT" then
- rlparmode=1
- else
- rlparmode=0
- end
- rlmode=rlparmode
- if trace_directions then
- report_process("directions after pardir %a: parmode %a, txtmode %a",dir,rlparmode,rlmode)
- end
- start=getnext(start)
+ start,rlparmode,rlmode=pardirstate(start)
else
start=getnext(start)
end
@@ -14717,1090 +20860,2147 @@ local function featuresprocessor(head,font,attr)
registerstep(head)
end
end
+ nesting=nesting-1
head=tonode(head)
return head,done
end
-local function generic(lookupdata,lookupname,unicode,lookuphash)
- local target=lookuphash[lookupname]
- if target then
- target[unicode]=lookupdata
- else
- lookuphash[lookupname]={ [unicode]=lookupdata }
- end
-end
-local function ligature(lookupdata,lookupname,unicode,lookuphash)
- local target=lookuphash[lookupname]
- if not target then
- target={}
- lookuphash[lookupname]=target
- end
- for i=1,#lookupdata do
- local li=lookupdata[i]
- local tu=target[li]
- if not tu then
- tu={}
- target[li]=tu
- end
- target=tu
- end
- target.ligature=unicode
+local function featuresinitializer(tfmdata,value)
end
-local function pair(lookupdata,lookupname,unicode,lookuphash)
- local target=lookuphash[lookupname]
- if not target then
- target={}
- lookuphash[lookupname]=target
- end
- local others=target[unicode]
- local paired=lookupdata[1]
- if others then
- others[paired]=lookupdata
- else
- others={ [paired]=lookupdata }
- target[unicode]=others
- end
-end
-local action={
- substitution=generic,
- multiple=generic,
- alternate=generic,
- position=generic,
- ligature=ligature,
- pair=pair,
- kern=pair,
+registerotffeature {
+ name="features",
+ description="features",
+ default=true,
+ initializers={
+ position=1,
+ node=featuresinitializer,
+ },
+ processors={
+ node=featuresprocessor,
+ }
}
-local function prepare_lookups(tfmdata)
- local rawdata=tfmdata.shared.rawdata
- local resources=rawdata.resources
- local lookuphash=resources.lookuphash
- local anchor_to_lookup=resources.anchor_to_lookup
- local lookup_to_anchor=resources.lookup_to_anchor
- local lookuptypes=resources.lookuptypes
- local characters=tfmdata.characters
- local descriptions=tfmdata.descriptions
- local duplicates=resources.duplicates
- for unicode,character in next,characters do
- local description=descriptions[unicode]
- if description then
- local lookups=description.slookups
- if lookups then
- for lookupname,lookupdata in next,lookups do
- action[lookuptypes[lookupname]](lookupdata,lookupname,unicode,lookuphash,duplicates)
- end
- end
- local lookups=description.mlookups
- if lookups then
- for lookupname,lookuplist in next,lookups do
- local lookuptype=lookuptypes[lookupname]
- for l=1,#lookuplist do
- local lookupdata=lookuplist[l]
- action[lookuptype](lookupdata,lookupname,unicode,lookuphash,duplicates)
- end
- end
- end
- local list=description.kerns
- if list then
- for lookup,krn in next,list do
- local target=lookuphash[lookup]
- if target then
- target[unicode]=krn
- else
- lookuphash[lookup]={ [unicode]=krn }
- end
- end
- end
- local list=description.anchors
- if list then
- for typ,anchors in next,list do
- if typ=="mark" or typ=="cexit" then
- for name,anchor in next,anchors do
- local lookups=anchor_to_lookup[name]
- if lookups then
- for lookup in next,lookups do
- local target=lookuphash[lookup]
- if target then
- target[unicode]=anchors
- else
- lookuphash[lookup]={ [unicode]=anchors }
- end
- end
- end
+otf.handlers=handlers
+local setspacekerns=nodes.injections.setspacekerns if not setspacekerns then os.exit() end
+function otf.handlers.trigger_space_kerns(head,start,dataset,sequence,_,_,_,_,font,attr)
+ setspacekerns(font,sequence)
+ return head,start,true
+end
+local function hasspacekerns(data)
+ local sequences=data.resources.sequences
+ for i=1,#sequences do
+ local sequence=sequences[i]
+ local steps=sequence.steps
+ if steps and sequence.features.kern then
+ for i=1,#steps do
+ local coverage=steps[i].coverage
+ if not coverage then
+ elseif coverage[32] then
+ return true
+ else
+ for k,v in next,coverage do
+ if v[32] then
+ return true
end
end
end
end
end
end
+ return false
end
-local function split(replacement,original)
- local result={}
- for i=1,#replacement do
- result[original[i]]=replacement[i]
+otf.readers.registerextender {
+ name="spacekerns",
+ action=function(data)
+ data.properties.hasspacekerns=hasspacekerns(data)
end
- return result
-end
-local valid={
- coverage={ chainsub=true,chainpos=true,contextsub=true,contextpos=true },
- reversecoverage={ reversesub=true },
- glyphs={ chainsub=true,chainpos=true,contextsub=true,contextpos=true },
}
-local function prepare_contextchains(tfmdata)
- local rawdata=tfmdata.shared.rawdata
- local resources=rawdata.resources
- local lookuphash=resources.lookuphash
- local lookuptags=resources.lookuptags
- local lookups=rawdata.lookups
- if lookups then
- for lookupname,lookupdata in next,rawdata.lookups do
- local lookuptype=lookupdata.type
- if lookuptype then
- local rules=lookupdata.rules
- if rules then
- local format=lookupdata.format
- local validformat=valid[format]
- if not validformat then
- report_prepare("unsupported format %a",format)
- elseif not validformat[lookuptype] then
- report_prepare("unsupported format %a, lookuptype %a, lookupname %a",format,lookuptype,lookuptags[lookupname])
- else
- local contexts=lookuphash[lookupname]
- if not contexts then
- contexts={}
- lookuphash[lookupname]=contexts
- end
- local t,nt={},0
- for nofrules=1,#rules do
- local rule=rules[nofrules]
- local current=rule.current
- local before=rule.before
- local after=rule.after
- local replacements=rule.replacements
- local sequence={}
- local nofsequences=0
- if before then
- for n=1,#before do
- nofsequences=nofsequences+1
- sequence[nofsequences]=before[n]
- end
- end
- local start=nofsequences+1
- for n=1,#current do
- nofsequences=nofsequences+1
- sequence[nofsequences]=current[n]
- end
- local stop=nofsequences
- if after then
- for n=1,#after do
- nofsequences=nofsequences+1
- sequence[nofsequences]=after[n]
+local function spaceinitializer(tfmdata,value)
+ local resources=tfmdata.resources
+ local spacekerns=resources and resources.spacekerns
+ if spacekerns==nil then
+ local properties=tfmdata.properties
+ if properties and properties.hasspacekerns then
+ local sequences=resources.sequences
+ local left={}
+ local right={}
+ local last=0
+ local feat=nil
+ for i=1,#sequences do
+ local sequence=sequences[i]
+ local steps=sequence.steps
+ if steps then
+ local kern=sequence.features.kern
+ if kern then
+ feat=feat or kern
+ for i=1,#steps do
+ local step=steps[i]
+ local coverage=step.coverage
+ if coverage then
+ local kerns=coverage[32]
+ if kerns then
+ for k,v in next,kerns do
+ if type(v)=="table" then
+ right[k]=v[3]
+ else
+ right[k]=v
+ end
+ end
end
- end
- if sequence[1] then
- nt=nt+1
- t[nt]={ nofrules,lookuptype,sequence,start,stop,rule.lookups,replacements }
- for unic in next,sequence[start] do
- local cu=contexts[unic]
- if not cu then
- contexts[unic]=t
+ for k,v in next,coverage do
+ local kern=v[32]
+ if kern then
+ if type(kern)=="table" then
+ left[k]=kern[3]
+ else
+ left[k]=kern
+ end
end
end
end
end
+ last=i
end
else
end
- else
- report_prepare("missing lookuptype for lookupname %a",lookuptags[lookupname])
end
- end
- end
-end
-local function featuresinitializer(tfmdata,value)
- if true then
- local rawdata=tfmdata.shared.rawdata
- local properties=rawdata.properties
- if not properties.initialized then
- local starttime=trace_preparing and os.clock()
- local resources=rawdata.resources
- resources.lookuphash=resources.lookuphash or {}
- prepare_contextchains(tfmdata)
- prepare_lookups(tfmdata)
- properties.initialized=true
- if trace_preparing then
- report_prepare("preparation time is %0.3f seconds for %a",os.clock()-starttime,tfmdata.properties.fullname)
+ left=next(left) and left or false
+ right=next(right) and right or false
+ if left or right then
+ spacekerns={
+ left=left,
+ right=right,
+ }
+ if last>0 then
+ local triggersequence={
+ features={ kern=feat or { dflt={ dflt=true,} } },
+ flags=noflags,
+ name="trigger_space_kerns",
+ order={ "kern" },
+ type="trigger_space_kerns",
+ left=left,
+ right=right,
+ }
+ insert(sequences,last,triggersequence)
+ end
+ else
+ spacekerns=false
end
+ else
+ spacekerns=false
end
+ resources.spacekerns=spacekerns
end
+ return spacekerns
end
registerotffeature {
- name="features",
- description="features",
+ name="spacekern",
+ description="space kern injection",
default=true,
initializers={
- position=1,
- node=featuresinitializer,
+ node=spaceinitializer,
},
- processors={
- node=featuresprocessor,
- }
}
-otf.handlers=handlers
end -- closure
do -- begin closure to overcome local limits and interference
-if not modules then modules={} end modules ['font-otp']={
+if not modules then modules={} end modules ['font-osd']={
version=1.001,
- comment="companion to font-otf.lua (packing)",
- author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright="PRAGMA ADE / ConTeXt Development Team",
+ comment="companion to font-ini.mkiv",
+ author="Kai Eigner, TAT Zetwerk / Hans Hagen, PRAGMA ADE",
+ copyright="TAT Zetwerk / PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
-local next,type,tostring=next,type,tostring
-local sort,concat=table.sort,table.concat
-local trace_packing=false trackers.register("otf.packing",function(v) trace_packing=v end)
-local trace_loading=false trackers.register("otf.loading",function(v) trace_loading=v end)
-local report_otf=logs.reporter("fonts","otf loading")
-fonts=fonts or {}
-local handlers=fonts.handlers or {}
-fonts.handlers=handlers
-local otf=handlers.otf or {}
-handlers.otf=otf
-local enhancers=otf.enhancers or {}
-otf.enhancers=enhancers
-local glists=otf.glists or { "gsub","gpos" }
-otf.glists=glists
-local criterium=1
-local threshold=0
-local function tabstr_normal(t)
- local s={}
- local n=0
- for k,v in next,t do
- n=n+1
- if type(v)=="table" then
- s[n]=k..">"..tabstr_normal(v)
- elseif v==true then
- s[n]=k.."+"
- elseif v then
- s[n]=k.."="..v
- else
- s[n]=k.."-"
- end
- end
- if n==0 then
- return ""
- elseif n==1 then
- return s[1]
- else
- sort(s)
- return concat(s,",")
- end
-end
-local function tabstr_flat(t)
- local s={}
- local n=0
- for k,v in next,t do
- n=n+1
- s[n]=k.."="..v
+local insert,imerge,copy=table.insert,table.imerge,table.copy
+local next,type=next,type
+local report_devanagari=logs.reporter("otf","devanagari")
+fonts=fonts or {}
+fonts.analyzers=fonts.analyzers or {}
+fonts.analyzers.methods=fonts.analyzers.methods or { node={ otf={} } }
+local otf=fonts.handlers.otf
+local nodecodes=nodes.nodecodes
+local glyph_code=nodecodes.glyph
+local handlers=otf.handlers
+local methods=fonts.analyzers.methods
+local otffeatures=fonts.constructors.newfeatures("otf")
+local registerotffeature=otffeatures.register
+local nuts=nodes.nuts
+local tonode=nuts.tonode
+local tonut=nuts.tonut
+local getnext=nuts.getnext
+local getprev=nuts.getprev
+local getboth=nuts.getboth
+local getid=nuts.getid
+local getchar=nuts.getchar
+local getfont=nuts.getfont
+local getsubtype=nuts.getsubtype
+local setlink=nuts.setlink
+local setnext=nuts.setnext
+local setprev=nuts.setprev
+local setchar=nuts.setchar
+local getprop=nuts.getprop
+local setprop=nuts.setprop
+local ischar=nuts.is_char
+local insert_node_after=nuts.insert_after
+local copy_node=nuts.copy
+local free_node=nuts.free
+local remove_node=nuts.remove
+local flush_list=nuts.flush_list
+local copyinjection=nodes.injections.copy
+local unsetvalue=attributes.unsetvalue
+local fontdata=fonts.hashes.identifiers
+local a_state=attributes.private('state')
+local a_syllabe=attributes.private('syllabe')
+local dotted_circle=0x25CC
+local states=fonts.analyzers.states
+local s_rphf=states.rphf
+local s_half=states.half
+local s_pref=states.pref
+local s_blwf=states.blwf
+local s_pstf=states.pstf
+local replace_all_nbsp=nil
+replace_all_nbsp=function(head)
+ replace_all_nbsp=typesetters and typesetters.characters and typesetters.characters.replacenbspaces or function(head)
+ return head
+ end
+ return replace_all_nbsp(head)
+end
+local xprocesscharacters=nil
+if context then
+ xprocesscharacters=function(head,font)
+ xprocesscharacters=nodes.handlers.characters
+ return xprocesscharacters(head,font)
end
- if n==0 then
- return ""
- elseif n==1 then
- return s[1]
- else
- sort(s)
- return concat(s,",")
+else
+ xprocesscharacters=function(head,font)
+ xprocesscharacters=nodes.handlers.nodepass
+ return xprocesscharacters(head,font)
+ end
+end
+local function processcharacters(head,font)
+ return tonut(xprocesscharacters(tonode(head)))
+end
+local consonant={
+ [0x0915]=true,[0x0916]=true,[0x0917]=true,[0x0918]=true,
+ [0x0919]=true,[0x091A]=true,[0x091B]=true,[0x091C]=true,
+ [0x091D]=true,[0x091E]=true,[0x091F]=true,[0x0920]=true,
+ [0x0921]=true,[0x0922]=true,[0x0923]=true,[0x0924]=true,
+ [0x0925]=true,[0x0926]=true,[0x0927]=true,[0x0928]=true,
+ [0x0929]=true,[0x092A]=true,[0x092B]=true,[0x092C]=true,
+ [0x092D]=true,[0x092E]=true,[0x092F]=true,[0x0930]=true,
+ [0x0931]=true,[0x0932]=true,[0x0933]=true,[0x0934]=true,
+ [0x0935]=true,[0x0936]=true,[0x0937]=true,[0x0938]=true,
+ [0x0939]=true,[0x0958]=true,[0x0959]=true,[0x095A]=true,
+ [0x095B]=true,[0x095C]=true,[0x095D]=true,[0x095E]=true,
+ [0x095F]=true,[0x0979]=true,[0x097A]=true,
+ [0x0C95]=true,[0x0C96]=true,[0x0C97]=true,[0x0C98]=true,
+ [0x0C99]=true,[0x0C9A]=true,[0x0C9B]=true,[0x0C9C]=true,
+ [0x0C9D]=true,[0x0C9E]=true,[0x0C9F]=true,[0x0CA0]=true,
+ [0x0CA1]=true,[0x0CA2]=true,[0x0CA3]=true,[0x0CA4]=true,
+ [0x0CA5]=true,[0x0CA6]=true,[0x0CA7]=true,[0x0CA8]=true,
+ [0x0CA9]=true,[0x0CAA]=true,[0x0CAB]=true,[0x0CAC]=true,
+ [0x0CAD]=true,[0x0CAE]=true,[0x0CAF]=true,[0x0CB0]=true,
+ [0x0CB1]=true,[0x0CB2]=true,[0x0CB3]=true,[0x0CB4]=true,
+ [0x0CB5]=true,[0x0CB6]=true,[0x0CB7]=true,[0x0CB8]=true,
+ [0x0CB9]=true,
+ [0x0CDE]=true,
+ [0x0D15]=true,[0x0D16]=true,[0x0D17]=true,[0x0D18]=true,
+ [0x0D19]=true,[0x0D1A]=true,[0x0D1B]=true,[0x0D1C]=true,
+ [0x0D1D]=true,[0x0D1E]=true,[0x0D1F]=true,[0x0D20]=true,
+ [0x0D21]=true,[0x0D22]=true,[0x0D23]=true,[0x0D24]=true,
+ [0x0D25]=true,[0x0D26]=true,[0x0D27]=true,[0x0D28]=true,
+ [0x0D29]=true,[0x0D2A]=true,[0x0D2B]=true,[0x0D2C]=true,
+ [0x0D2D]=true,[0x0D2E]=true,[0x0D2F]=true,[0x0D30]=true,
+ [0x0D31]=true,[0x0D32]=true,[0x0D33]=true,[0x0D34]=true,
+ [0x0D35]=true,[0x0D36]=true,[0x0D37]=true,[0x0D38]=true,
+ [0x0D39]=true,[0x0D3A]=true,
+}
+local independent_vowel={
+ [0x0904]=true,[0x0905]=true,[0x0906]=true,[0x0907]=true,
+ [0x0908]=true,[0x0909]=true,[0x090A]=true,[0x090B]=true,
+ [0x090C]=true,[0x090D]=true,[0x090E]=true,[0x090F]=true,
+ [0x0910]=true,[0x0911]=true,[0x0912]=true,[0x0913]=true,
+ [0x0914]=true,[0x0960]=true,[0x0961]=true,[0x0972]=true,
+ [0x0973]=true,[0x0974]=true,[0x0975]=true,[0x0976]=true,
+ [0x0977]=true,
+ [0x0C85]=true,[0x0C86]=true,[0x0C87]=true,[0x0C88]=true,
+ [0x0C89]=true,[0x0C8A]=true,[0x0C8B]=true,[0x0C8C]=true,
+ [0x0C8D]=true,[0x0C8E]=true,[0x0C8F]=true,[0x0C90]=true,
+ [0x0C91]=true,[0x0C92]=true,[0x0C93]=true,[0x0C94]=true,
+ [0x0D05]=true,[0x0D06]=true,[0x0D07]=true,[0x0D08]=true,
+ [0x0D09]=true,[0x0D0A]=true,[0x0D0B]=true,[0x0D0C]=true,
+ [0x0D0E]=true,[0x0D0F]=true,[0x0D10]=true,[0x0D12]=true,
+ [0x0D13]=true,[0x0D14]=true,
+}
+local dependent_vowel={
+ [0x093A]=true,[0x093B]=true,[0x093E]=true,[0x093F]=true,
+ [0x0940]=true,[0x0941]=true,[0x0942]=true,[0x0943]=true,
+ [0x0944]=true,[0x0945]=true,[0x0946]=true,[0x0947]=true,
+ [0x0948]=true,[0x0949]=true,[0x094A]=true,[0x094B]=true,
+ [0x094C]=true,[0x094E]=true,[0x094F]=true,[0x0955]=true,
+ [0x0956]=true,[0x0957]=true,[0x0962]=true,[0x0963]=true,
+ [0x0CBE]=true,[0x0CBF]=true,[0x0CC0]=true,[0x0CC1]=true,
+ [0x0CC2]=true,[0x0CC3]=true,[0x0CC4]=true,[0x0CC5]=true,
+ [0x0CC6]=true,[0x0CC7]=true,[0x0CC8]=true,[0x0CC9]=true,
+ [0x0CCA]=true,[0x0CCB]=true,[0x0CCC]=true,
+ [0x0D3E]=true,[0x0D3F]=true,[0x0D40]=true,[0x0D41]=true,
+ [0x0D42]=true,[0x0D43]=true,[0x0D44]=true,[0x0D46]=true,
+ [0x0D47]=true,[0x0D48]=true,[0x0D4A]=true,[0x0D4B]=true,
+ [0x0D4C]=true,[0x0D57]=true,
+}
+local vowel_modifier={
+ [0x0900]=true,[0x0901]=true,[0x0902]=true,[0x0903]=true,
+ [0xA8E0]=true,[0xA8E1]=true,[0xA8E2]=true,[0xA8E3]=true,
+ [0xA8E4]=true,[0xA8E5]=true,[0xA8E6]=true,[0xA8E7]=true,
+ [0xA8E8]=true,[0xA8E9]=true,[0xA8EA]=true,[0xA8EB]=true,
+ [0xA8EC]=true,[0xA8ED]=true,[0xA8EE]=true,[0xA8EF]=true,
+ [0xA8F0]=true,[0xA8F1]=true,
+ [0x0D02]=true,[0x0D03]=true,
+}
+local stress_tone_mark={
+ [0x0951]=true,[0x0952]=true,[0x0953]=true,[0x0954]=true,
+ [0x0CCD]=true,
+ [0x0D4D]=true,
+}
+local nukta={
+ [0x093C]=true,
+ [0x0CBC]=true,
+}
+local halant={
+ [0x094D]=true,
+ [0x0CCD]=true,
+ [0x0D4D]=true,
+}
+local ra={
+ [0x0930]=true,
+ [0x0CB0]=true,
+ [0x0D30]=true,
+}
+local c_anudatta=0x0952
+local c_nbsp=0x00A0
+local c_zwnj=0x200C
+local c_zwj=0x200D
+local zw_char={
+ [0x200C]=true,
+ [0x200D]=true,
+}
+local pre_mark={
+ [0x093F]=true,[0x094E]=true,
+ [0x0D46]=true,[0x0D47]=true,[0x0D48]=true,
+}
+local above_mark={
+ [0x0900]=true,[0x0901]=true,[0x0902]=true,[0x093A]=true,
+ [0x0945]=true,[0x0946]=true,[0x0947]=true,[0x0948]=true,
+ [0x0951]=true,[0x0953]=true,[0x0954]=true,[0x0955]=true,
+ [0xA8E0]=true,[0xA8E1]=true,[0xA8E2]=true,[0xA8E3]=true,
+ [0xA8E4]=true,[0xA8E5]=true,[0xA8E6]=true,[0xA8E7]=true,
+ [0xA8E8]=true,[0xA8E9]=true,[0xA8EA]=true,[0xA8EB]=true,
+ [0xA8EC]=true,[0xA8ED]=true,[0xA8EE]=true,[0xA8EF]=true,
+ [0xA8F0]=true,[0xA8F1]=true,
+ [0x0D4E]=true,
+}
+local below_mark={
+ [0x093C]=true,[0x0941]=true,[0x0942]=true,[0x0943]=true,
+ [0x0944]=true,[0x094D]=true,[0x0952]=true,[0x0956]=true,
+ [0x0957]=true,[0x0962]=true,[0x0963]=true,
+}
+local post_mark={
+ [0x0903]=true,[0x093B]=true,[0x093E]=true,[0x0940]=true,
+ [0x0949]=true,[0x094A]=true,[0x094B]=true,[0x094C]=true,
+ [0x094F]=true,
+}
+local twopart_mark={
+ [0x0D4A]={ 0x0D46,0x0D3E,},
+ [0x0D4B]={ 0x0D47,0x0D3E,},
+ [0x0D4C]={ 0x0D46,0x0D57,},
+}
+local mark_four={}
+for k,v in next,pre_mark do mark_four[k]=pre_mark end
+for k,v in next,above_mark do mark_four[k]=above_mark end
+for k,v in next,below_mark do mark_four[k]=below_mark end
+for k,v in next,post_mark do mark_four[k]=post_mark end
+local mark_above_below_post={}
+for k,v in next,above_mark do mark_above_below_post[k]=above_mark end
+for k,v in next,below_mark do mark_above_below_post[k]=below_mark end
+for k,v in next,post_mark do mark_above_below_post[k]=post_mark end
+local reorder_class={
+ [0x0930]="before postscript",
+ [0x093F]="before half",
+ [0x0940]="after subscript",
+ [0x0941]="after subscript",
+ [0x0942]="after subscript",
+ [0x0943]="after subscript",
+ [0x0944]="after subscript",
+ [0x0945]="after subscript",
+ [0x0946]="after subscript",
+ [0x0947]="after subscript",
+ [0x0948]="after subscript",
+ [0x0949]="after subscript",
+ [0x094A]="after subscript",
+ [0x094B]="after subscript",
+ [0x094C]="after subscript",
+ [0x0962]="after subscript",
+ [0x0963]="after subscript",
+ [0x093E]="after subscript",
+ [0x0CB0]="after postscript",
+ [0x0CBF]="before subscript",
+ [0x0CC6]="before subscript",
+ [0x0CCC]="before subscript",
+ [0x0CBE]="before subscript",
+ [0x0CE2]="before subscript",
+ [0x0CE3]="before subscript",
+ [0x0CC1]="before subscript",
+ [0x0CC2]="before subscript",
+ [0x0CC3]="after subscript",
+ [0x0CC4]="after subscript",
+ [0x0CD5]="after subscript",
+ [0x0CD6]="after subscript",
+}
+local dflt_true={
+ dflt=true
+}
+local dev2_defaults={
+ dev2=dflt_true,
+}
+local deva_defaults={
+ dev2=dflt_true,
+ deva=dflt_true,
+}
+local false_flags={ false,false,false,false }
+local both_joiners_true={
+ [0x200C]=true,
+ [0x200D]=true,
+}
+local sequence_reorder_matras={
+ features={ dv01=dev2_defaults },
+ flags=false_flags,
+ name="dv01_reorder_matras",
+ order={ "dv01" },
+ type="devanagari_reorder_matras",
+ nofsteps=1,
+ steps={
+ {
+ osdstep=true,
+ coverage=pre_mark,
+ }
+ }
+}
+local sequence_reorder_reph={
+ features={ dv02=dev2_defaults },
+ flags=false_flags,
+ name="dv02_reorder_reph",
+ order={ "dv02" },
+ type="devanagari_reorder_reph",
+ nofsteps=1,
+ steps={
+ {
+ osdstep=true,
+ coverage={},
+ }
+ }
+}
+local sequence_reorder_pre_base_reordering_consonants={
+ features={ dv03=dev2_defaults },
+ flags=false_flags,
+ name="dv03_reorder_pre_base_reordering_consonants",
+ order={ "dv03" },
+ type="devanagari_reorder_pre_base_reordering_consonants",
+ nofsteps=1,
+ steps={
+ {
+ osdstep=true,
+ coverage={},
+ }
+ }
+}
+local sequence_remove_joiners={
+ features={ dv04=deva_defaults },
+ flags=false_flags,
+ name="dv04_remove_joiners",
+ order={ "dv04" },
+ type="devanagari_remove_joiners",
+ nofsteps=1,
+ steps={
+ { osdstep=true,
+ coverage=both_joiners_true,
+ },
+ }
+}
+local basic_shaping_forms={
+ nukt=true,
+ akhn=true,
+ rphf=true,
+ pref=true,
+ rkrf=true,
+ blwf=true,
+ half=true,
+ pstf=true,
+ vatu=true,
+ cjct=true,
+}
+local valid={
+ akhn=true,
+ rphf=true,
+ pref=true,
+ half=true,
+ blwf=true,
+ pstf=true,
+ pres=true,
+ blws=true,
+ psts=true,
+}
+local function initializedevanagi(tfmdata)
+ local script,language=otf.scriptandlanguage(tfmdata,attr)
+ if script=="deva" or script=="dev2" or script=="mlym" or script=="mlm2" then
+ local resources=tfmdata.resources
+ local devanagari=resources.devanagari
+ if not devanagari then
+ report_devanagari("adding devanagari features to font")
+ local gsubfeatures=resources.features.gsub
+ local sequences=resources.sequences
+ local sharedfeatures=tfmdata.shared.features
+ local lastmatch=0
+ for s=1,#sequences do
+ local features=sequences[s].features
+ if features then
+ for k,v in next,features do
+ if basic_shaping_forms[k] then
+ lastmatch=s
+ end
+ end
+ end
+ end
+ local insertindex=lastmatch+1
+ gsubfeatures["dv01"]=dev2_defaults
+ gsubfeatures["dv02"]=dev2_defaults
+ gsubfeatures["dv03"]=dev2_defaults
+ gsubfeatures["dv04"]=deva_defaults
+ local reorder_pre_base_reordering_consonants=copy(sequence_reorder_pre_base_reordering_consonants)
+ local reorder_reph=copy(sequence_reorder_reph)
+ local reorder_matras=copy(sequence_reorder_matras)
+ local remove_joiners=copy(sequence_remove_joiners)
+ insert(sequences,insertindex,reorder_pre_base_reordering_consonants)
+ insert(sequences,insertindex,reorder_reph)
+ insert(sequences,insertindex,reorder_matras)
+ insert(sequences,insertindex,remove_joiners)
+ local blwfcache={}
+ local seqsubset={}
+ local rephstep={
+ coverage={}
+ }
+ local devanagari={
+ reph=false,
+ vattu=false,
+ blwfcache=blwfcache,
+ seqsubset=seqsubset,
+ reorderreph=rephstep,
+ }
+ reorder_reph.steps={ rephstep }
+ local pre_base_reordering_consonants={}
+ reorder_pre_base_reordering_consonants.steps[1].coverage=pre_base_reordering_consonants
+ resources.devanagari=devanagari
+ for s=1,#sequences do
+ local sequence=sequences[s]
+ local steps=sequence.steps
+ local nofsteps=sequence.nofsteps
+ local features=sequence.features
+ if features["rphf"] then
+ devanagari.reph=true
+ elseif features["blwf"] then
+ devanagari.vattu=true
+ for i=1,nofsteps do
+ local step=steps[i]
+ local coverage=step.coverage
+ if coverage then
+ for k,v in next,coverage do
+ if not blwfcache[k] then
+ blwfcache[k]=v
+ end
+ end
+ end
+ end
+ end
+ if valid[kind] then
+ for i=1,nofsteps do
+ local step=steps[i]
+ local coverage=step.coverage
+ if coverage then
+ local reph=false
+ if step.osdstep then
+ for k,v in next,ra do
+ local r=coverage[k]
+ if r then
+ local h=false
+ for k,v in next,halant do
+ local h=r[k]
+ if h then
+ reph=h.ligature or false
+ break
+ end
+ end
+ if reph then
+ break
+ end
+ end
+ end
+ else
+ end
+ seqsubset[#seqsubset+1]={ kind,coverage,reph }
+ end
+ end
+ end
+ if kind=="pref" then
+ local sequence=dataset[3]
+ local steps=sequence.steps
+ local nofsteps=sequence.nofsteps
+ for i=1,nofsteps do
+ local step=steps[i]
+ local coverage=step.coverage
+ if coverage then
+ for k,v in next,halant do
+ local h=coverage[k]
+ if h then
+ local found=false
+ for k,v in next,h do
+ found=v and v.ligature
+ if found then
+ pre_base_reordering_consonants[k]=found
+ break
+ end
+ end
+ if found then
+ break
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ if script=="deva" then
+ sharedfeatures["dv04"]=true
+ elseif script=="dev2" then
+ sharedfeatures["dv01"]=true
+ sharedfeatures["dv02"]=true
+ sharedfeatures["dv03"]=true
+ sharedfeatures["dv04"]=true
+ elseif script=="mlym" then
+ sharedfeatures["pstf"]=true
+ elseif script=="mlm2" then
+ sharedfeatures["pstf"]=true
+ sharedfeatures["pref"]=true
+ sharedfeatures["dv03"]=true
+ gsubfeatures ["dv03"]=dev2_defaults
+ insert(sequences,insertindex,sequence_reorder_pre_base_reordering_consonants)
+ end
+ end
end
end
-local function tabstr_mixed(t)
- local s={}
- local n=#t
- if n==0 then
- return ""
- elseif n==1 then
- local k=t[1]
- if k==true then
- return "++"
- elseif k==false then
- return "--"
- else
- return tostring(k)
- end
- else
- for i=1,n do
- local k=t[i]
- if k==true then
- s[i]="++"
- elseif k==false then
- s[i]="--"
- else
- s[i]=k
+registerotffeature {
+ name="devanagari",
+ description="inject additional features",
+ default=true,
+ initializers={
+ node=initializedevanagi,
+ },
+}
+local function deva_initialize(font,attr)
+ local tfmdata=fontdata[font]
+ local datasets=otf.dataset(tfmdata,font,attr)
+ local devanagaridata=datasets.devanagari
+ if not devanagaridata then
+ devanagaridata={
+ reph=false,
+ vattu=false,
+ blwfcache={},
+ }
+ datasets.devanagari=devanagaridata
+ local resources=tfmdata.resources
+ local devanagari=resources.devanagari
+ for s=1,#datasets do
+ local dataset=datasets[s]
+ if dataset and dataset[1] then
+ local kind=dataset[4]
+ if kind=="rphf" then
+ devanagaridata.reph=true
+ elseif kind=="blwf" then
+ devanagaridata.vattu=true
+ devanagaridata.blwfcache=devanagari.blwfcache
+ end
end
end
- return concat(s,",")
end
+ return devanagaridata.reph,devanagaridata.vattu,devanagaridata.blwfcache
end
-local function tabstr_boolean(t)
- local s={}
- local n=0
- for k,v in next,t do
- n=n+1
- if v then
- s[n]=k.."+"
+local function deva_reorder(head,start,stop,font,attr,nbspaces)
+ local reph,vattu,blwfcache=deva_initialize(font,attr)
+ local current=start
+ local n=getnext(start)
+ local base=nil
+ local firstcons=nil
+ local lastcons=nil
+ local basefound=false
+ if reph and ra[getchar(start)] and halant[getchar(n)] then
+ if n==stop then
+ return head,stop,nbspaces
+ end
+ if getchar(getnext(n))==c_zwj then
+ current=start
else
- s[n]=k.."-"
+ current=getnext(n)
+ setprop(start,a_state,s_rphf)
end
end
- if n==0 then
- return ""
- elseif n==1 then
- return s[1]
- else
- sort(s)
- return concat(s,",")
- end
-end
-local function packdata(data)
- if data then
- local h,t,c={},{},{}
- local hh,tt,cc={},{},{}
- local nt,ntt=0,0
- local function pack_normal(v)
- local tag=tabstr_normal(v)
- local ht=h[tag]
- if ht then
- c[ht]=c[ht]+1
- return ht
- else
- nt=nt+1
- t[nt]=v
- h[tag]=nt
- c[nt]=1
- return nt
+ if getchar(current)==c_nbsp then
+ if current==stop then
+ stop=getprev(stop)
+ head=remove_node(head,current)
+ free_node(current)
+ return head,stop,nbspaces
+ else
+ nbspaces=nbspaces+1
+ base=current
+ firstcons=current
+ lastcons=current
+ current=getnext(current)
+ if current~=stop then
+ if nukta[getchar(current)] then
+ current=getnext(current)
+ end
+ if getchar(current)==c_zwj then
+ if current~=stop then
+ local next=getnext(current)
+ if next~=stop and halant[getchar(next)] then
+ current=next
+ next=getnext(current)
+ local tmp=next and getnext(next) or nil
+ local changestop=next==stop
+ local tempcurrent=copy_node(next)
+ copyinjection(tempcurrent,next)
+ local nextcurrent=copy_node(current)
+ copyinjection(nextcurrent,current)
+ setlink(tempcurrent,nextcurrent)
+ setprop(tempcurrent,a_state,s_blwf)
+ tempcurrent=processcharacters(tempcurrent,font)
+ setprop(tempcurrent,a_state,unsetvalue)
+ if getchar(next)==getchar(tempcurrent) then
+ flush_list(tempcurrent)
+ local n=copy_node(current)
+ copyinjection(n,current)
+ setchar(current,dotted_circle)
+ head=insert_node_after(head,current,n)
+ else
+ setchar(current,getchar(tempcurrent))
+ local freenode=getnext(current)
+ setlink(current,tmp)
+ free_node(freenode)
+ flush_list(tempcurrent)
+ if changestop then
+ stop=current
+ end
+ end
+ end
+ end
+ end
end
end
- local function pack_flat(v)
- local tag=tabstr_flat(v)
- local ht=h[tag]
- if ht then
- c[ht]=c[ht]+1
- return ht
- else
- nt=nt+1
- t[nt]=v
- h[tag]=nt
- c[nt]=1
- return nt
+ end
+ while not basefound do
+ local char=getchar(current)
+ if consonant[char] then
+ setprop(current,a_state,s_half)
+ if not firstcons then
+ firstcons=current
end
- end
- local function pack_boolean(v)
- local tag=tabstr_boolean(v)
- local ht=h[tag]
- if ht then
- c[ht]=c[ht]+1
- return ht
+ lastcons=current
+ if not base then
+ base=current
+ elseif blwfcache[char] then
+ setprop(current,a_state,s_blwf)
else
- nt=nt+1
- t[nt]=v
- h[tag]=nt
- c[nt]=1
- return nt
+ base=current
end
end
- local function pack_indexed(v)
- local tag=concat(v," ")
- local ht=h[tag]
- if ht then
- c[ht]=c[ht]+1
- return ht
- else
- nt=nt+1
- t[nt]=v
- h[tag]=nt
- c[nt]=1
- return nt
- end
+ basefound=current==stop
+ current=getnext(current)
+ end
+ if base~=lastcons then
+ local np=base
+ local n=getnext(base)
+ local ch=getchar(n)
+ if nukta[ch] then
+ np=n
+ n=getnext(n)
+ ch=getchar(n)
+ end
+ if halant[ch] then
+ if lastcons~=stop then
+ local ln=getnext(lastcons)
+ if nukta[getchar(ln)] then
+ lastcons=ln
+ end
+ end
+ local nn=getnext(n)
+ local ln=getnext(lastcons)
+ setlink(np,nn)
+ setnext(lastcons,n)
+ if ln then
+ setprev(ln,n)
+ end
+ setnext(n,ln)
+ setprev(n,lastcons)
+ if lastcons==stop then
+ stop=n
+ end
+ end
+ end
+ n=getnext(start)
+ if n~=stop and ra[getchar(start)] and halant[getchar(n)] and not zw_char[getchar(getnext(n))] then
+ local matra=base
+ if base~=stop then
+ local next=getnext(base)
+ if dependent_vowel[getchar(next)] then
+ matra=next
+ end
+ end
+ local sp=getprev(start)
+ local nn=getnext(n)
+ local mn=getnext(matra)
+ setlink(sp,nn)
+ setlink(matra,start)
+ setlink(n,mn)
+ if head==start then
+ head=nn
end
- local function pack_mixed(v)
- local tag=tabstr_mixed(v)
- local ht=h[tag]
- if ht then
- c[ht]=c[ht]+1
- return ht
- else
- nt=nt+1
- t[nt]=v
- h[tag]=nt
- c[nt]=1
- return nt
- end
+ start=nn
+ if matra==stop then
+ stop=n
end
- local function pack_final(v)
- if c[v]<=criterium then
- return t[v]
- else
- local hv=hh[v]
- if hv then
- return hv
- else
- ntt=ntt+1
- tt[ntt]=t[v]
- hh[v]=ntt
- cc[ntt]=c[v]
- return ntt
+ end
+ local current=start
+ while current~=stop do
+ local next=getnext(current)
+ if next~=stop and halant[getchar(next)] and getchar(getnext(next))==c_zwnj then
+ setprop(current,a_state,unsetvalue)
+ end
+ current=next
+ end
+ if base~=stop and getprop(base,a_state) then
+ local next=getnext(base)
+ if halant[getchar(next)] and not (next~=stop and getchar(getnext(next))==c_zwj) then
+ setprop(base,a_state,unsetvalue)
+ end
+ end
+ local current,allreordered,moved=start,false,{ [base]=true }
+ local a,b,p,bn=base,base,base,getnext(base)
+ if base~=stop and nukta[getchar(bn)] then
+ a,b,p=bn,bn,bn
+ end
+ while not allreordered do
+ local c=current
+ local n=getnext(current)
+ local l=nil
+ if c~=stop then
+ local ch=getchar(n)
+ if nukta[ch] then
+ c=n
+ n=getnext(n)
+ ch=getchar(n)
+ end
+ if c~=stop then
+ if halant[ch] then
+ c=n
+ n=getnext(n)
+ ch=getchar(n)
+ end
+ while c~=stop and dependent_vowel[ch] do
+ c=n
+ n=getnext(n)
+ ch=getchar(n)
+ end
+ if c~=stop then
+ if vowel_modifier[ch] then
+ c=n
+ n=getnext(n)
+ ch=getchar(n)
+ end
+ if c~=stop and stress_tone_mark[ch] then
+ c=n
+ n=getnext(n)
+ end
end
end
end
- local function success(stage,pass)
- if nt==0 then
- if trace_loading or trace_packing then
- report_otf("pack quality: nothing to pack")
+ local bp=getprev(firstcons)
+ local cn=getnext(current)
+ local last=getnext(c)
+ while cn~=last do
+ if pre_mark[getchar(cn)] then
+ if bp then
+ setnext(bp,cn)
end
- return false
- elseif nt>=threshold then
- local one,two,rest=0,0,0
- if pass==1 then
- for k,v in next,c do
- if v==1 then
- one=one+1
- elseif v==2 then
- two=two+1
- else
- rest=rest+1
- end
- end
- else
- for k,v in next,cc do
- if v>20 then
- rest=rest+1
- elseif v>10 then
- two=two+1
- else
- one=one+1
- end
- end
- data.tables=tt
+ local prev,next=getboth(cn)
+ if next then
+ setprev(next,prev)
end
- if trace_loading or trace_packing then
- report_otf("pack quality: stage %s, pass %s, %s packed, 1-10:%s, 11-20:%s, rest:%s (criterium: %s)",stage,pass,one+two+rest,one,two,rest,criterium)
+ setnext(prev,next)
+ if cn==stop then
+ stop=prev
end
- return true
- else
- if trace_loading or trace_packing then
- report_otf("pack quality: stage %s, pass %s, %s packed, aborting pack (threshold: %s)",stage,pass,nt,threshold)
+ setprev(cn,bp)
+ setlink(cn,firstcons)
+ if firstcons==start then
+ if head==start then
+ head=cn
+ end
+ start=cn
end
- return false
- end
- end
- local function packers(pass)
- if pass==1 then
- return pack_normal,pack_indexed,pack_flat,pack_boolean,pack_mixed
- else
- return pack_final,pack_final,pack_final,pack_final,pack_final
- end
- end
- local resources=data.resources
- local lookuptypes=resources.lookuptypes
- for pass=1,2 do
- if trace_packing then
- report_otf("start packing: stage 1, pass %s",pass)
+ break
end
- local pack_normal,pack_indexed,pack_flat,pack_boolean,pack_mixed=packers(pass)
- for unicode,description in next,data.descriptions do
- local boundingbox=description.boundingbox
- if boundingbox then
- description.boundingbox=pack_indexed(boundingbox)
- end
- local slookups=description.slookups
- if slookups then
- for tag,slookup in next,slookups do
- local what=lookuptypes[tag]
- if what=="pair" then
- local t=slookup[2] if t then slookup[2]=pack_indexed(t) end
- local t=slookup[3] if t then slookup[3]=pack_indexed(t) end
- elseif what~="substitution" then
- slookups[tag]=pack_indexed(slookup)
- end
+ cn=getnext(cn)
+ end
+ allreordered=c==stop
+ current=getnext(c)
+ end
+ if reph or vattu then
+ local current,cns=start,nil
+ while current~=stop do
+ local c=current
+ local n=getnext(current)
+ if ra[getchar(current)] and halant[getchar(n)] then
+ c=n
+ n=getnext(n)
+ local b,bn=base,base
+ while bn~=stop do
+ local next=getnext(bn)
+ if dependent_vowel[getchar(next)] then
+ b=next
end
+ bn=next
end
- local mlookups=description.mlookups
- if mlookups then
- for tag,mlookup in next,mlookups do
- local what=lookuptypes[tag]
- if what=="pair" then
- for i=1,#mlookup do
- local lookup=mlookup[i]
- local t=lookup[2] if t then lookup[2]=pack_indexed(t) end
- local t=lookup[3] if t then lookup[3]=pack_indexed(t) end
- end
- elseif what~="substitution" then
- for i=1,#mlookup do
- mlookup[i]=pack_indexed(mlookup[i])
+ if getprop(current,a_state)==s_rphf then
+ if b~=current then
+ if current==start then
+ if head==start then
+ head=n
end
+ start=n
+ end
+ if b==stop then
+ stop=c
end
+ local prev=getprev(current)
+ setlink(prev,n)
+ local next=getnext(b)
+ setlink(c,next)
+ setlink(b,current)
end
- end
- local kerns=description.kerns
- if kerns then
- for tag,kern in next,kerns do
- kerns[tag]=pack_flat(kern)
+ elseif cns and getnext(cns)~=current then
+ local cp=getprev(current)
+ local cnsn=getnext(cns)
+ setlink(cp,n)
+ setlink(cns,current)
+ setlink(c,cnsn)
+ if c==stop then
+ stop=cp
+ break
end
+ current=getprev(n)
end
- local math=description.math
- if math then
- local kerns=math.kerns
- if kerns then
- for tag,kern in next,kerns do
- kerns[tag]=pack_normal(kern)
- end
+ else
+ local char=getchar(current)
+ if consonant[char] then
+ cns=current
+ local next=getnext(cns)
+ if halant[getchar(next)] then
+ cns=next
end
- end
- local anchors=description.anchors
- if anchors then
- for what,anchor in next,anchors do
- if what=="baselig" then
- for _,a in next,anchor do
- for k=1,#a do
- a[k]=pack_indexed(a[k])
- end
- end
- else
- for k,v in next,anchor do
- anchor[k]=pack_indexed(v)
- end
- end
+ elseif char==c_nbsp then
+ nbspaces=nbspaces+1
+ cns=current
+ local next=getnext(cns)
+ if halant[getchar(next)] then
+ cns=next
end
end
- local altuni=description.altuni
- if altuni then
- for i=1,#altuni do
- altuni[i]=pack_flat(altuni[i])
+ end
+ current=getnext(current)
+ end
+ end
+ if getchar(base)==c_nbsp then
+ nbspaces=nbspaces-1
+ head=remove_node(head,base)
+ free_node(base)
+ end
+ return head,stop,nbspaces
+end
+function handlers.devanagari_reorder_matras(head,start)
+ local current=start
+ local startfont=getfont(start)
+ local startattr=getprop(start,a_syllabe)
+ while current do
+ local char=ischar(current,startfont)
+ local next=getnext(current)
+ if char and getprop(current,a_syllabe)==startattr then
+ if halant[char] and not getprop(current,a_state) then
+ if next then
+ local char=ischar(next,startfont)
+ if char and zw_char[char] and getprop(next,a_syllabe)==startattr then
+ current=next
+ next=getnext(current)
end
end
+ local startnext=getnext(start)
+ head=remove_node(head,start)
+ setlink(start,next)
+ setlink(current,start)
+ start=startnext
+ break
end
- local lookups=data.lookups
- if lookups then
- for _,lookup in next,lookups do
- local rules=lookup.rules
- if rules then
- for i=1,#rules do
- local rule=rules[i]
- local r=rule.before if r then for i=1,#r do r[i]=pack_boolean(r[i]) end end
- local r=rule.after if r then for i=1,#r do r[i]=pack_boolean(r[i]) end end
- local r=rule.current if r then for i=1,#r do r[i]=pack_boolean(r[i]) end end
- local r=rule.replacements if r then rule.replacements=pack_flat (r) end
- local r=rule.lookups if r then rule.lookups=pack_indexed(r) end
- end
+ end
+ current=next
+ end
+ return head,start,true
+end
+function handlers.devanagari_reorder_reph(head,start)
+ local current=getnext(start)
+ local startnext=nil
+ local startprev=nil
+ local startfont=getfont(start)
+ local startattr=getprop(start,a_syllabe)
+ while current do
+ local char=ischar(current,font)
+ if char and getprop(current,a_syllabe)==startattr then
+ if halant[char] and not getprop(current,a_state) then
+ local next=getnext(current)
+ if next then
+ local nextchar=ischar(next,font)
+ if nextchar and zw_char[nextchar] and getprop(next,a_syllabe)==startattr then
+ current=next
+ next=getnext(current)
end
end
+ startnext=getnext(start)
+ head=remove_node(head,start)
+ setlink(start,next)
+ setlink(current,start)
+ start=startnext
+ startattr=getprop(start,a_syllabe)
+ break
end
- local anchor_to_lookup=resources.anchor_to_lookup
- if anchor_to_lookup then
- for anchor,lookup in next,anchor_to_lookup do
- anchor_to_lookup[anchor]=pack_normal(lookup)
+ current=getnext(current)
+ else
+ break
+ end
+ end
+ if not startnext then
+ current=getnext(start)
+ while current do
+ local char=ischar(current,font)
+ if char and getprop(current,a_syllabe)==startattr then
+ if getprop(current,a_state)==s_pstf then
+ startnext=getnext(start)
+ head=remove_node(head,start)
+ local prev=getprev(current)
+ setlink(prev,start)
+ setlink(start,current)
+ start=startnext
+ startattr=getprop(start,a_syllabe)
+ break
end
+ current=getnext(current)
+ else
+ break
end
- local lookup_to_anchor=resources.lookup_to_anchor
- if lookup_to_anchor then
- for lookup,anchor in next,lookup_to_anchor do
- lookup_to_anchor[lookup]=pack_normal(anchor)
+ end
+ end
+ if not startnext then
+ current=getnext(start)
+ local c=nil
+ while current do
+ local char=ischar(current,font)
+ if char and getprop(current,a_syllabe)==startattr then
+ if not c and mark_above_below_post[char] and reorder_class[char]~="after subscript" then
+ c=current
end
+ current=getnext(current)
+ else
+ break
end
- local sequences=resources.sequences
- if sequences then
- for feature,sequence in next,sequences do
- local flags=sequence.flags
- if flags then
- sequence.flags=pack_normal(flags)
- end
- local subtables=sequence.subtables
- if subtables then
- sequence.subtables=pack_normal(subtables)
- end
- local features=sequence.features
- if features then
- for script,feature in next,features do
- features[script]=pack_normal(feature)
+ end
+ if c then
+ startnext=getnext(start)
+ head=remove_node(head,start)
+ local prev=getprev(c)
+ setlink(prev,start)
+ setlink(start,c)
+ start=startnext
+ startattr=getprop(start,a_syllabe)
+ end
+ end
+ if not startnext then
+ current=start
+ local next=getnext(current)
+ while next do
+ local nextchar=ischar(next,font)
+ if nextchar and getprop(next,a_syllabe)==startattr then
+ current=next
+ next=getnext(current)
+ else
+ break
+ end
+ end
+ if start~=current then
+ startnext=getnext(start)
+ head=remove_node(head,start)
+ local next=getnext(current)
+ setlink(start,next)
+ setlink(current,"next",start)
+ start=startnext
+ end
+ end
+ return head,start,true
+end
+function handlers.devanagari_reorder_pre_base_reordering_consonants(head,start)
+ local current=start
+ local startnext=nil
+ local startprev=nil
+ local startfont=getfont(start)
+ local startattr=getprop(start,a_syllabe)
+ while current do
+ local char=ischar(current,font)
+ if char and getprop(current,a_syllabe)==startattr then
+ local next=getnext(current)
+ if halant[char] and not getprop(current,a_state) then
+ if next then
+ local nextchar=ischar(next,font)
+ if nextchar and getprop(next,a_syllabe)==startattr then
+ if nextchar==c_zwnj or nextchar==c_zwj then
+ current=next
+ next=getnext(current)
end
end
- local order=sequence.order
- if order then
- sequence.order=pack_indexed(order)
- end
- local markclass=sequence.markclass
- if markclass then
- sequence.markclass=pack_boolean(markclass)
- end
end
+ startnext=getnext(start)
+ removenode(start,start)
+ setlink(start,next)
+ setlink(current,start)
+ start=startnext
+ break
end
- local lookups=resources.lookups
- if lookups then
- for name,lookup in next,lookups do
- local flags=lookup.flags
- if flags then
- lookup.flags=pack_normal(flags)
+ current=next
+ else
+ break
+ end
+ end
+ if not startnext then
+ current=getnext(start)
+ startattr=getprop(start,a_syllabe)
+ while current do
+ local char=ischar(current,font)
+ if char and getprop(current,a_syllabe)==startattr then
+ if not consonant[char] and getprop(current,a_state) then
+ startnext=getnext(start)
+ removenode(start,start)
+ local prev=getprev(current)
+ setlink(start,prev)
+ setlink(start,current)
+ start=startnext
+ break
+ end
+ current=getnext(current)
+ else
+ break
+ end
+ end
+ end
+ return head,start,true
+end
+function handlers.devanagari_remove_joiners(head,start,kind,lookupname,replacement)
+ local stop=getnext(start)
+ local font=getfont(start)
+ local last=start
+ while stop do
+ local char=ischar(stop,font)
+ if char and (char==c_zwnj or char==c_zwj) then
+ last=stop
+ stop=getnext(stop)
+ else
+ break
+ end
+ end
+ local prev=getprev(start)
+ if stop then
+ setnext(last)
+ setlink(prev,stop)
+ elseif prev then
+ setnext(prev)
+ end
+ if head==start then
+ head=stop
+ end
+ flush_list(start)
+ return head,stop,true
+end
+local function dev2_initialize(font,attr)
+ local devanagari=fontdata[font].resources.devanagari
+ if devanagari then
+ return devanagari.seqsubset or {},devanagari.reorderreph or {}
+ else
+ return {},{}
+ end
+end
+local function dev2_reorder(head,start,stop,font,attr,nbspaces)
+ local seqsubset,reorderreph=dev2_initialize(font,attr)
+ local reph=false
+ local halfpos=nil
+ local basepos=nil
+ local subpos=nil
+ local postpos=nil
+ local locl={}
+ for i=1,#seqsubset do
+ local subset=seqsubset[i]
+ local kind=subset[1]
+ local lookupcache=subset[2]
+ if kind=="rphf" then
+ for k,v in next,ra do
+ local r=lookupcache[k]
+ if r then
+ for k,v in next,halant do
+ local h=r[k]
+ if h then
+ reph=h.ligature or false
+ break
+ end
end
- local subtables=lookup.subtables
- if subtables then
- lookup.subtables=pack_normal(subtables)
+ if reph then
+ break
end
end
end
- local features=resources.features
- if features then
- for _,what in next,glists do
- local list=features[what]
- if list then
- for feature,spec in next,list do
- list[feature]=pack_normal(spec)
+ local current=start
+ local last=getnext(stop)
+ while current~=last do
+ if current~=stop then
+ local c=locl[current] or getchar(current)
+ local found=lookupcache[c]
+ if found then
+ local next=getnext(current)
+ local n=locl[next] or getchar(next)
+ if found[n] then
+ local afternext=next~=stop and getnext(next)
+ if afternext and zw_char[getchar(afternext)] then
+ current=next
+ current=getnext(current)
+ elseif current==start then
+ setprop(current,a_state,s_rphf)
+ current=next
+ else
+ current=next
+ end
end
end
end
+ current=getnext(current)
end
- if not success(1,pass) then
- return
- end
- end
- if nt>0 then
- for pass=1,2 do
- if trace_packing then
- report_otf("start packing: stage 2, pass %s",pass)
- end
- local pack_normal,pack_indexed,pack_flat,pack_boolean,pack_mixed=packers(pass)
- for unicode,description in next,data.descriptions do
- local kerns=description.kerns
- if kerns then
- description.kerns=pack_normal(kerns)
- end
- local math=description.math
- if math then
- local kerns=math.kerns
- if kerns then
- math.kerns=pack_normal(kerns)
+ elseif kind=="pref" then
+ local current=start
+ local last=getnext(stop)
+ while current~=last do
+ if current~=stop then
+ local c=locl[current] or getchar(current)
+ local found=lookupcache[c]
+ if found then
+ local next=getnext(current)
+ local n=locl[next] or getchar(next)
+ if found[n] then
+ setprop(current,a_state,s_pref)
+ setprop(next,a_state,s_pref)
+ current=next
end
end
- local anchors=description.anchors
- if anchors then
- description.anchors=pack_normal(anchors)
+ end
+ current=getnext(current)
+ end
+ elseif kind=="half" then
+ local current=start
+ local last=getnext(stop)
+ while current~=last do
+ if current~=stop then
+ local c=locl[current] or getchar(current)
+ local found=lookupcache[c]
+ if found then
+ local next=getnext(current)
+ local n=locl[next] or getchar(next)
+ if found[n] then
+ if next~=stop and getchar(getnext(next))==c_zwnj then
+ current=next
+ else
+ setprop(current,a_state,s_half)
+ if not halfpos then
+ halfpos=current
+ end
+ end
+ current=getnext(current)
+ end
end
- local mlookups=description.mlookups
- if mlookups then
- for tag,mlookup in next,mlookups do
- mlookups[tag]=pack_normal(mlookup)
+ end
+ current=getnext(current)
+ end
+ elseif kind=="blwf" then
+ local current=start
+ local last=getnext(stop)
+ while current~=last do
+ if current~=stop then
+ local c=locl[current] or getchar(current)
+ local found=lookupcache[c]
+ if found then
+ local next=getnext(current)
+ local n=locl[next] or getchar(next)
+ if found[n] then
+ setprop(current,a_state,s_blwf)
+ setprop(next,a_state,s_blwf)
+ current=next
+ subpos=current
end
end
- local altuni=description.altuni
- if altuni then
- description.altuni=pack_normal(altuni)
+ end
+ current=getnext(current)
+ end
+ elseif kind=="pstf" then
+ local current=start
+ local last=getnext(stop)
+ while current~=last do
+ if current~=stop then
+ local c=locl[current] or getchar(current)
+ local found=lookupcache[c]
+ if found then
+ local next=getnext(current)
+ local n=locl[next] or getchar(next)
+ if found[n] then
+ setprop(current,a_state,s_pstf)
+ setprop(next,a_state,s_pstf)
+ current=next
+ postpos=current
+ end
end
end
- local lookups=data.lookups
- if lookups then
- for _,lookup in next,lookups do
- local rules=lookup.rules
- if rules then
- for i=1,#rules do
- local rule=rules[i]
- local r=rule.before if r then rule.before=pack_normal(r) end
- local r=rule.after if r then rule.after=pack_normal(r) end
- local r=rule.current if r then rule.current=pack_normal(r) end
+ current=getnext(current)
+ end
+ end
+ end
+ reorderreph.coverage={ [reph]=true }
+ local current,base,firstcons=start,nil,nil
+ if getprop(start,a_state)==s_rphf then
+ current=getnext(getnext(start))
+ end
+ if current~=getnext(stop) and getchar(current)==c_nbsp then
+ if current==stop then
+ stop=getprev(stop)
+ head=remove_node(head,current)
+ free_node(current)
+ return head,stop,nbspaces
+ else
+ nbspaces=nbspaces+1
+ base=current
+ current=getnext(current)
+ if current~=stop then
+ local char=getchar(current)
+ if nukta[char] then
+ current=getnext(current)
+ char=getchar(current)
+ end
+ if char==c_zwj then
+ local next=getnext(current)
+ if current~=stop and next~=stop and halant[getchar(next)] then
+ current=next
+ next=getnext(current)
+ local tmp=getnext(next)
+ local changestop=next==stop
+ setnext(next,nil)
+ setprop(current,a_state,s_pref)
+ current=processcharacters(current,font)
+ setprop(current,a_state,s_blwf)
+ current=processcharacters(current,font)
+ setprop(current,a_state,s_pstf)
+ current=processcharacters(current,font)
+ setprop(current,a_state,unsetvalue)
+ if halant[getchar(current)] then
+ setnext(getnext(current),tmp)
+ local nc=copy_node(current)
+ copyinjection(nc,current)
+ setchar(current,dotted_circle)
+ head=insert_node_after(head,current,nc)
+ else
+ setnext(current,tmp)
+ if changestop then
+ stop=current
end
end
end
end
- local sequences=resources.sequences
- if sequences then
- for feature,sequence in next,sequences do
- sequence.features=pack_normal(sequence.features)
+ end
+ end
+ else
+ local last=getnext(stop)
+ while current~=last do
+ local next=getnext(current)
+ if consonant[getchar(current)] then
+ if not (current~=stop and next~=stop and halant[getchar(next)] and getchar(getnext(next))==c_zwj) then
+ if not firstcons then
+ firstcons=current
+ end
+ local a=getprop(current,a_state)
+ if not (a==s_pref or a==s_blwf or a==s_pstf) then
+ base=current
end
- end
- if not success(2,pass) then
end
end
- for pass=1,2 do
- local pack_normal,pack_indexed,pack_flat,pack_boolean,pack_mixed=packers(pass)
- for unicode,description in next,data.descriptions do
- local slookups=description.slookups
- if slookups then
- description.slookups=pack_normal(slookups)
+ current=next
+ end
+ if not base then
+ base=firstcons
+ end
+ end
+ if not base then
+ if getprop(start,a_state)==s_rphf then
+ setprop(start,a_state,unsetvalue)
+ end
+ return head,stop,nbspaces
+ else
+ if getprop(base,a_state) then
+ setprop(base,a_state,unsetvalue)
+ end
+ basepos=base
+ end
+ if not halfpos then
+ halfpos=base
+ end
+ if not subpos then
+ subpos=base
+ end
+ if not postpos then
+ postpos=subpos or base
+ end
+ local moved={}
+ local current=start
+ local last=getnext(stop)
+ while current~=last do
+ local char,target,cn=locl[current] or getchar(current),nil,getnext(current)
+ local tpm=twopart_mark[char]
+ if tpm then
+ local extra=copy_node(current)
+ copyinjection(extra,current)
+ char=tpm[1]
+ setchar(current,char)
+ setchar(extra,tpm[2])
+ head=insert_node_after(head,current,extra)
+ end
+ if not moved[current] and dependent_vowel[char] then
+ if pre_mark[char] then
+ moved[current]=true
+ local prev,next=getboth(current)
+ setlink(prev,next)
+ if current==stop then
+ stop=getprev(current)
+ end
+ if halfpos==start then
+ if head==start then
+ head=current
end
- local mlookups=description.mlookups
- if mlookups then
- description.mlookups=pack_normal(mlookups)
+ start=current
+ end
+ local prev=getprev(halfpos)
+ setlink(prev,current)
+ setlink(current,halfpos)
+ halfpos=current
+ elseif above_mark[char] then
+ target=basepos
+ if subpos==basepos then
+ subpos=current
+ end
+ if postpos==basepos then
+ postpos=current
+ end
+ basepos=current
+ elseif below_mark[char] then
+ target=subpos
+ if postpos==subpos then
+ postpos=current
+ end
+ subpos=current
+ elseif post_mark[char] then
+ target=postpos
+ postpos=current
+ end
+ if mark_above_below_post[char] then
+ local prev=getprev(current)
+ if prev~=target then
+ local next=getnext(current)
+ setlink(next,prev)
+ if current==stop then
+ stop=prev
end
+ local next=getnext(target)
+ setlink(current,next)
+ setlink(target,current)
end
end
end
+ current=cn
end
-end
-local unpacked_mt={
- __index=function(t,k)
- t[k]=false
- return k
+ local current,c=start,nil
+ while current~=stop do
+ local char=getchar(current)
+ if halant[char] or stress_tone_mark[char] then
+ if not c then
+ c=current
+ end
+ else
+ c=nil
end
-}
-local function unpackdata(data)
- if data then
- local tables=data.tables
- if tables then
- local resources=data.resources
- local lookuptypes=resources.lookuptypes
- local unpacked={}
- setmetatable(unpacked,unpacked_mt)
- for unicode,description in next,data.descriptions do
- local tv=tables[description.boundingbox]
- if tv then
- description.boundingbox=tv
- end
- local slookups=description.slookups
- if slookups then
- local tv=tables[slookups]
- if tv then
- description.slookups=tv
- slookups=unpacked[tv]
- end
- if slookups then
- for tag,lookup in next,slookups do
- local what=lookuptypes[tag]
- if what=="pair" then
- local tv=tables[lookup[2]]
- if tv then
- lookup[2]=tv
- end
- local tv=tables[lookup[3]]
- if tv then
- lookup[3]=tv
- end
- elseif what~="substitution" then
- local tv=tables[lookup]
- if tv then
- slookups[tag]=tv
+ local next=getnext(current)
+ if c and nukta[getchar(next)] then
+ if head==c then
+ head=next
+ end
+ if stop==next then
+ stop=current
+ end
+ local prev=getprev(c)
+ setlink(next,prev)
+ local nextnext=getnext(next)
+ setnext(current,nextnext)
+ local nextnextnext=getnext(nextnext)
+ if nextnextnext then
+ setprev(nextnextnext,current)
+ end
+ setlink(nextnext,c)
+ end
+ if stop==current then break end
+ current=getnext(current)
+ end
+ if getchar(base)==c_nbsp then
+ nbspaces=nbspaces-1
+ head=remove_node(head,base)
+ free_node(base)
+ end
+ return head,stop,nbspaces
+end
+local separator={}
+imerge(separator,consonant)
+imerge(separator,independent_vowel)
+imerge(separator,dependent_vowel)
+imerge(separator,vowel_modifier)
+imerge(separator,stress_tone_mark)
+for k,v in next,nukta do separator[k]=true end
+for k,v in next,halant do separator[k]=true end
+local function analyze_next_chars_one(c,font,variant)
+ local n=getnext(c)
+ if not n then
+ return c
+ end
+ if variant==1 then
+ local v=ischar(n,font)
+ if v and nukta[v] then
+ n=getnext(n)
+ if n then
+ v=ischar(n,font)
+ end
+ end
+ if n and v then
+ local nn=getnext(n)
+ if nn then
+ local vv=ischar(nn,font)
+ if vv then
+ local nnn=getnext(nn)
+ if nnn then
+ local vvv=ischar(nnn,font)
+ if vvv then
+ if vv==c_zwj and consonant[vvv] then
+ c=nnn
+ elseif (vv==c_zwnj or vv==c_zwj) and halant[vvv] then
+ local nnnn=getnext(nnn)
+ if nnnn then
+ local vvvv=ischar(nnnn)
+ if vvvv and consonant[vvvv] then
+ c=nnnn
+ end
end
end
end
end
end
- local mlookups=description.mlookups
- if mlookups then
- local tv=tables[mlookups]
- if tv then
- description.mlookups=tv
- mlookups=unpacked[tv]
+ end
+ end
+ elseif variant==2 then
+ local v=ischar(n,font)
+ if v and nukta[v] then
+ c=n
+ end
+ n=getnext(c)
+ if n then
+ v=ischar(n,font)
+ if v then
+ local nn=getnext(n)
+ if nn then
+ local vv=ischar(nn,font)
+ if vv and zw_char[vv] then
+ n=nn
+ v=vv
+ nn=getnext(nn)
+ vv=nn and ischar(nn,font)
end
- if mlookups then
- for tag,list in next,mlookups do
- local tv=tables[list]
- if tv then
- mlookups[tag]=tv
- list=unpacked[tv]
- end
- if list then
- local what=lookuptypes[tag]
- if what=="pair" then
- for i=1,#list do
- local lookup=list[i]
- local tv=tables[lookup[2]]
- if tv then
- lookup[2]=tv
- end
- local tv=tables[lookup[3]]
- if tv then
- lookup[3]=tv
- end
- end
- elseif what~="substitution" then
- for i=1,#list do
- local tv=tables[list[i]]
- if tv then
- list[i]=tv
- end
- end
- end
- end
- end
+ if vv and halant[v] and consonant[vv] then
+ c=nn
end
end
- local kerns=description.kerns
- if kerns then
- local tm=tables[kerns]
- if tm then
- description.kerns=tm
- kerns=unpacked[tm]
+ end
+ end
+ end
+ local n=getnext(c)
+ if not n then
+ return c
+ end
+ local v=ischar(n,font)
+ if not v then
+ return c
+ end
+ if dependent_vowel[v] then
+ c=getnext(c)
+ n=getnext(c)
+ if not n then
+ return c
+ end
+ v=ischar(n,font)
+ if not v then
+ return c
+ end
+ end
+ if nukta[v] then
+ c=getnext(c)
+ n=getnext(c)
+ if not n then
+ return c
+ end
+ v=ischar(n,font)
+ if not v then
+ return c
+ end
+ end
+ if halant[v] then
+ c=getnext(c)
+ n=getnext(c)
+ if not n then
+ return c
+ end
+ v=ischar(n,font)
+ if not v then
+ return c
+ end
+ end
+ if vowel_modifier[v] then
+ c=getnext(c)
+ n=getnext(c)
+ if not n then
+ return c
+ end
+ v=ischar(n,font)
+ if not v then
+ return c
+ end
+ end
+ if stress_tone_mark[v] then
+ c=getnext(c)
+ n=getnext(c)
+ if not n then
+ return c
+ end
+ v=ischar(n,font)
+ if not v then
+ return c
+ end
+ end
+ if stress_tone_mark[v] then
+ return n
+ else
+ return c
+ end
+end
+local function analyze_next_chars_two(c,font)
+ local n=getnext(c)
+ if not n then
+ return c
+ end
+ local v=ischar(n,font)
+ if v and nukta[v] then
+ c=n
+ end
+ n=c
+ while true do
+ local nn=getnext(n)
+ if nn then
+ local vv=ischar(nn,font)
+ if vv then
+ if halant[vv] then
+ n=nn
+ local nnn=getnext(nn)
+ if nnn then
+ local vvv=ischar(nnn,font)
+ if vvv and zw_char[vvv] then
+ n=nnn
+ end
end
- if kerns then
- for k,kern in next,kerns do
- local tv=tables[kern]
- if tv then
- kerns[k]=tv
- end
+ elseif vv==c_zwnj or vv==c_zwj then
+ local nnn=getnext(nn)
+ if nnn then
+ local vvv=ischar(nnn,font)
+ if vvv and halant[vvv] then
+ n=nnn
end
end
+ else
+ break
end
- local math=description.math
- if math then
- local kerns=math.kerns
- if kerns then
- local tm=tables[kerns]
- if tm then
- math.kerns=tm
- kerns=unpacked[tm]
- end
- if kerns then
- for k,kern in next,kerns do
- local tv=tables[kern]
- if tv then
- kerns[k]=tv
- end
+ local nn=getnext(n)
+ if nn then
+ local vv=ischar(nn,font)
+ if vv and consonant[vv] then
+ n=nn
+ local nnn=getnext(nn)
+ if nnn then
+ local vvv=ischar(nnn,font)
+ if vvv and nukta[vvv] then
+ n=nnn
end
end
+ c=n
+ else
+ break
end
+ else
+ break
end
- local anchors=description.anchors
- if anchors then
- local ta=tables[anchors]
- if ta then
- description.anchors=ta
- anchors=unpacked[ta]
- end
- if anchors then
- for tag,anchor in next,anchors do
- if tag=="baselig" then
- for _,list in next,anchor do
- for i=1,#list do
- local tv=tables[list[i]]
- if tv then
- list[i]=tv
- end
- end
- end
- else
- for a,data in next,anchor do
- local tv=tables[data]
- if tv then
- anchor[a]=tv
- end
- end
- end
+ else
+ break
+ end
+ else
+ break
+ end
+ end
+ if not c then
+ return
+ end
+ local n=getnext(c)
+ if not n then
+ return c
+ end
+ local v=ischar(n,font)
+ if not v then
+ return c
+ end
+ if v==c_anudatta then
+ c=n
+ n=getnext(c)
+ if not n then
+ return c
+ end
+ v=ischar(n,font)
+ if not v then
+ return c
+ end
+ end
+ if halant[v] then
+ c=n
+ n=getnext(c)
+ if not n then
+ return c
+ end
+ v=ischar(n,font)
+ if not v then
+ return c
+ end
+ if v==c_zwnj or v==c_zwj then
+ c=n
+ n=getnext(c)
+ if not n then
+ return c
+ end
+ v=ischar(n,font)
+ if not v then
+ return c
+ end
+ end
+ else
+ if dependent_vowel[v] then
+ c=n
+ n=getnext(c)
+ if not n then
+ return c
+ end
+ v=ischar(n,font)
+ if not v then
+ return c
+ end
+ end
+ if nukta[v] then
+ c=n
+ n=getnext(c)
+ if not n then
+ return c
+ end
+ v=ischar(n,font)
+ if not v then
+ return c
+ end
+ end
+ if halant[v] then
+ c=n
+ n=getnext(c)
+ if not n then
+ return c
+ end
+ v=ischar(n,font)
+ if not v then
+ return c
+ end
+ end
+ end
+ if vowel_modifier[v] then
+ c=n
+ n=getnext(c)
+ if not n then
+ return c
+ end
+ v=ischar(n,font)
+ if not v then
+ return c
+ end
+ end
+ if stress_tone_mark[v] then
+ c=n
+ n=getnext(c)
+ if not n then
+ return c
+ end
+ v=ischar(n,font)
+ if not v then
+ return c
+ end
+ end
+ if stress_tone_mark[v] then
+ return n
+ else
+ return c
+ end
+end
+local function inject_syntax_error(head,current,mark)
+ local signal=copy_node(current)
+ copyinjection(signal,current)
+ if mark==pre_mark then
+ setchar(signal,dotted_circle)
+ else
+ setchar(current,dotted_circle)
+ end
+ return insert_node_after(head,current,signal)
+end
+function methods.deva(head,font,attr)
+ head=tonut(head)
+ local current=head
+ local start=true
+ local done=false
+ local nbspaces=0
+ while current do
+ local char=ischar(current,font)
+ if char then
+ done=true
+ local syllablestart=current
+ local syllableend=nil
+ local c=current
+ local n=getnext(c)
+ local first=char
+ if n and ra[first] then
+ local second=ischar(n,font)
+ if second and halant[second] then
+ local n=getnext(n)
+ if n then
+ local third=ischar(n,font)
+ if third then
+ c=n
+ first=third
end
end
end
- local altuni=description.altuni
- if altuni then
- local altuni=tables[altuni]
- if altuni then
- description.altuni=altuni
- for i=1,#altuni do
- local tv=tables[altuni[i]]
- if tv then
- altuni[i]=tv
- end
- end
+ end
+ local standalone=first==c_nbsp
+ if standalone then
+ local prev=getprev(current)
+ if prev then
+ local prevchar=ischar(prev,font)
+ if not prevchar then
+ elseif not separator[prevchar] then
+ else
+ standalone=false
end
+ else
end
end
- local lookups=data.lookups
- if lookups then
- for _,lookup in next,lookups do
- local rules=lookup.rules
- if rules then
- for i=1,#rules do
- local rule=rules[i]
- local before=rule.before
- if before then
- local tv=tables[before]
- if tv then
- rule.before=tv
- before=unpacked[tv]
- end
- if before then
- for i=1,#before do
- local tv=tables[before[i]]
- if tv then
- before[i]=tv
- end
- end
- end
+ if standalone then
+ local syllableend=analyze_next_chars_one(c,font,2)
+ current=getnext(syllableend)
+ if syllablestart~=syllableend then
+ head,current,nbspaces=deva_reorder(head,syllablestart,syllableend,font,attr,nbspaces)
+ current=getnext(current)
+ end
+ else
+ if consonant[char] then
+ local prevc=true
+ while prevc do
+ prevc=false
+ local n=getnext(current)
+ if not n then
+ break
+ end
+ local v=ischar(n,font)
+ if not v then
+ break
+ end
+ if nukta[v] then
+ n=getnext(n)
+ if not n then
+ break
end
- local after=rule.after
- if after then
- local tv=tables[after]
- if tv then
- rule.after=tv
- after=unpacked[tv]
- end
- if after then
- for i=1,#after do
- local tv=tables[after[i]]
- if tv then
- after[i]=tv
- end
- end
- end
+ v=ischar(n,font)
+ if not v then
+ break
end
- local current=rule.current
- if current then
- local tv=tables[current]
- if tv then
- rule.current=tv
- current=unpacked[tv]
- end
- if current then
- for i=1,#current do
- local tv=tables[current[i]]
- if tv then
- current[i]=tv
- end
- end
- end
+ end
+ if halant[v] then
+ n=getnext(n)
+ if not n then
+ break
end
- local replacements=rule.replacements
- if replacements then
- local tv=tables[replacements]
- if tv then
- rule.replacements=tv
- end
+ v=ischar(n,font)
+ if not v then
+ break
end
- local lookups=rule.lookups
- if lookups then
- local tv=tables[lookups]
- if tv then
- rule.lookups=tv
+ if v==c_zwnj or v==c_zwj then
+ n=getnext(n)
+ if not n then
+ break
+ end
+ v=ischar(n,font)
+ if not v then
+ break
end
end
+ if consonant[v] then
+ prevc=true
+ current=n
+ end
end
end
- end
- end
- local anchor_to_lookup=resources.anchor_to_lookup
- if anchor_to_lookup then
- for anchor,lookup in next,anchor_to_lookup do
- local tv=tables[lookup]
- if tv then
- anchor_to_lookup[anchor]=tv
- end
- end
- end
- local lookup_to_anchor=resources.lookup_to_anchor
- if lookup_to_anchor then
- for lookup,anchor in next,lookup_to_anchor do
- local tv=tables[anchor]
- if tv then
- lookup_to_anchor[lookup]=tv
- end
- end
- end
- local ls=resources.sequences
- if ls then
- for _,feature in next,ls do
- local flags=feature.flags
- if flags then
- local tv=tables[flags]
- if tv then
- feature.flags=tv
- end
- end
- local subtables=feature.subtables
- if subtables then
- local tv=tables[subtables]
- if tv then
- feature.subtables=tv
+ local n=getnext(current)
+ if n then
+ local v=ischar(n,font)
+ if v and nukta[v] then
+ current=n
+ n=getnext(current)
end
end
- local features=feature.features
- if features then
- local tv=tables[features]
- if tv then
- feature.features=tv
- features=unpacked[tv]
- end
- if features then
- for script,data in next,features do
- local tv=tables[data]
- if tv then
- features[script]=tv
+ syllableend=current
+ current=n
+ if current then
+ local v=ischar(current,font)
+ if not v then
+ elseif halant[v] then
+ local n=getnext(current)
+ if n then
+ local v=ischar(n,font)
+ if v and zw_char[v] then
+ syllableend=n
+ current=getnext(n)
+ else
+ syllableend=current
+ current=n
end
+ else
+ syllableend=current
+ current=n
+ end
+ else
+ if dependent_vowel[v] then
+ syllableend=current
+ current=getnext(current)
+ v=ischar(current,font)
+ end
+ if v and vowel_modifier[v] then
+ syllableend=current
+ current=getnext(current)
+ v=ischar(current,font)
+ end
+ if v and stress_tone_mark[v] then
+ syllableend=current
+ current=getnext(current)
end
end
end
- local order=feature.order
- if order then
- local tv=tables[order]
- if tv then
- feature.order=tv
- end
+ if syllablestart~=syllableend then
+ head,current,nbspaces=deva_reorder(head,syllablestart,syllableend,font,attr,nbspaces)
+ current=getnext(current)
end
- local markclass=feature.markclass
- if markclass then
- local tv=tables[markclass]
- if tv then
- feature.markclass=tv
+ elseif independent_vowel[char] then
+ syllableend=current
+ current=getnext(current)
+ if current then
+ local v=ischar(current,font)
+ if v then
+ if vowel_modifier[v] then
+ syllableend=current
+ current=getnext(current)
+ v=ischar(current,font)
+ end
+ if v and stress_tone_mark[v] then
+ syllableend=current
+ current=getnext(current)
+ end
end
end
+ else
+ local mark=mark_four[char]
+ if mark then
+ head,current=inject_syntax_error(head,current,mark)
+ end
+ current=getnext(current)
end
end
- local lookups=resources.lookups
- if lookups then
- for _,lookup in next,lookups do
- local flags=lookup.flags
- if flags then
- local tv=tables[flags]
- if tv then
- lookup.flags=tv
- end
- end
- local subtables=lookup.subtables
- if subtables then
- local tv=tables[subtables]
- if tv then
- lookup.subtables=tv
+ else
+ current=getnext(current)
+ end
+ start=false
+ end
+ if nbspaces>0 then
+ head=replace_all_nbsp(head)
+ end
+ head=tonode(head)
+ return head,done
+end
+function methods.dev2(head,font,attr)
+ head=tonut(head)
+ local current=head
+ local start=true
+ local done=false
+ local syllabe=0
+ local nbspaces=0
+ while current do
+ local syllablestart=nil
+ local syllableend=nil
+ local char=ischar(current,font)
+ if char then
+ done=true
+ syllablestart=current
+ local c=current
+ local n=getnext(current)
+ if n and ra[char] then
+ local nextchar=ischar(n,font)
+ if nextchar and halant[nextchar] then
+ local n=getnext(n)
+ if n then
+ local nextnextchar=ischar(n,font)
+ if nextnextchar then
+ c=n
+ char=nextnextchar
end
end
end
end
- local features=resources.features
- if features then
- for _,what in next,glists do
- local feature=features[what]
- if feature then
- for tag,spec in next,feature do
- local tv=tables[spec]
- if tv then
- feature[tag]=tv
- end
- end
+ if independent_vowel[char] then
+ current=analyze_next_chars_one(c,font,1)
+ syllableend=current
+ else
+ local standalone=char==c_nbsp
+ if standalone then
+ nbspaces=nbspaces+1
+ local p=getprev(current)
+ if not p then
+ elseif ischar(p,font) then
+ elseif not separator[getchar(p)] then
+ else
+ standalone=false
end
end
+ if standalone then
+ current=analyze_next_chars_one(c,font,2)
+ syllableend=current
+ elseif consonant[getchar(current)] then
+ current=analyze_next_chars_two(current,font)
+ syllableend=current
+ end
end
- data.tables=nil
end
+ if syllableend then
+ syllabe=syllabe+1
+ local c=syllablestart
+ local n=getnext(syllableend)
+ while c~=n do
+ setprop(c,a_syllabe,syllabe)
+ c=getnext(c)
+ end
+ end
+ if syllableend and syllablestart~=syllableend then
+ head,current,nbspaces=dev2_reorder(head,syllablestart,syllableend,font,attr,nbspaces)
+ end
+ if not syllableend then
+ local char=ischar(current,font)
+ if char and not getprop(current,a_state) then
+ local mark=mark_four[char]
+ if mark then
+ head,current=inject_syntax_error(head,current,mark)
+ end
+ end
+ end
+ start=false
+ current=getnext(current)
end
+ if nbspaces>0 then
+ head=replace_all_nbsp(head)
+ end
+ head=tonode(head)
+ return head,done
end
-if otf.enhancers.register then
- otf.enhancers.register("pack",packdata)
- otf.enhancers.register("unpack",unpackdata)
-end
-otf.enhancers.unpack=unpackdata
-otf.enhancers.pack=packdata
+methods.mlym=methods.deva
+methods.mlm2=methods.dev2
end -- closure
do -- begin closure to overcome local limits and interference
-if not modules then modules={} end modules ['luatex-fonts-lua']={
+if not modules then modules={} end modules ['font-lua']={
version=1.001,
- comment="companion to luatex-*.tex",
+ comment="companion to font-ini.mkiv",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright="PRAGMA ADE / ConTeXt Development Team",
license="see context related readme files"
}
-if context then
- texio.write_nl("fatal error: this module is not for context")
- os.exit()
-end
+local trace_defining=false trackers.register("fonts.defining",function(v) trace_defining=v end)
+local report_lua=logs.reporter("fonts","lua loading")
local fonts=fonts
+local readers=fonts.readers
fonts.formats.lua="lua"
-function fonts.readers.lua(specification)
+local function check_lua(specification,fullname)
+ local fullname=resolvers.findfile(fullname) or ""
+ if fullname~="" then
+ local loader=loadfile(fullname)
+ loader=loader and loader()
+ return loader and loader(specification)
+ end
+end
+readers.check_lua=check_lua
+function readers.lua(specification)
+ local original=specification.specification
+ if trace_defining then
+ report_lua("using lua reader for %a",original)
+ end
local fullname=specification.filename or ""
if fullname=="" then
local forced=specification.forced or ""
@@ -15810,12 +23010,7 @@ function fonts.readers.lua(specification)
fullname=specification.name
end
end
- local fullname=resolvers.findfile(fullname) or ""
- if fullname~="" then
- local loader=loadfile(fullname)
- loader=loader and loader()
- return loader and loader(specification)
- end
+ return check_lua(specification,fullname)
end
end -- closure
@@ -16189,7 +23384,7 @@ local filename_1=P("file:")/isfile*(namespec/thename)
local filename_2=P("[")*P(true)/isname*(((1-P("]"))^0)/thename)*P("]")
local fontname_1=P("name:")/isname*(namespec/thename)
local fontname_2=P(true)/issome*(namespec/thename)
-local sometext=(R("az","AZ","09")+S("+-."))^1
+local sometext=(R("az","AZ","09")+S("+-.{}"))^1
local truevalue=P("+")*spaces*(sometext/istrue)
local falsevalue=P("-")*spaces*(sometext/isfalse)
local keyvalue=(C(sometext)*spaces*P("=")*spaces*C(sometext))/iskey
@@ -16456,7 +23651,7 @@ end -- closure
do -- begin closure to overcome local limits and interference
-if not modules then modules={} end modules ['luatex-fonts-cbk']={
+if not modules then modules={} end modules ['font-gbn']={
version=1.001,
comment="companion to luatex-*.tex",
author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
@@ -16469,45 +23664,59 @@ if context then
end
local fonts=fonts
local nodes=nodes
-local traverse_id=node.traverse_id
-local free_node=node.free
-local remove_node=node.remove
+local nuts=nodes.nuts
+local traverse_id=nuts.traverse_id
+local free_node=nuts.free
+local remove_node=nuts.remove
local glyph_code=nodes.nodecodes.glyph
local disc_code=nodes.nodecodes.disc
-local ligaturing=node.ligaturing
-local kerning=node.kerning
-local basepass=true
+local tonode=nuts.tonode
+local tonut=nuts.tonut
+local getfont=nuts.getfont
+local getchar=nuts.getchar
+local getid=nuts.getid
+local getprev=nuts.getprev
+local getnext=nuts.getnext
+local getdisc=nuts.getdisc
+local setchar=nuts.setchar
+local setlink=nuts.setlink
+local n_ligaturing=node.ligaturing
+local n_kerning=node.kerning
+local ligaturing=nuts.ligaturing
+local kerning=nuts.kerning
+local basemodepass=true
local function l_warning() texio.write_nl("warning: node.ligaturing called directly") l_warning=nil end
local function k_warning() texio.write_nl("warning: node.kerning called directly") k_warning=nil end
function node.ligaturing(...)
- if basepass and l_warning then
+ if basemodepass and l_warning then
l_warning()
end
- return ligaturing(...)
+ return n_ligaturing(...)
end
function node.kerning(...)
- if basepass and k_warning then
+ if basemodepass and k_warning then
k_warning()
end
- return kerning(...)
+ return n_kerning(...)
end
-function nodes.handlers.setbasepass(v)
- basepass=v
+function nodes.handlers.setbasemodepass(v)
+ basemodepass=v
end
function nodes.handlers.nodepass(head)
local fontdata=fonts.hashes.identifiers
if fontdata then
+ local nuthead=tonut(head)
local usedfonts={}
local basefonts={}
local prevfont=nil
local basefont=nil
local variants=nil
local redundant=nil
- for n in traverse_id(glyph_code,head) do
- local font=n.font
+ for n in traverse_id(glyph_code,nuthead) do
+ local font=getfont(n)
if font~=prevfont then
if basefont then
- basefont[2]=n.prev
+ basefont[2]=getprev(n)
end
prevfont=font
local used=usedfonts[font]
@@ -16519,7 +23728,7 @@ function nodes.handlers.nodepass(head)
local processors=shared.processes
if processors and #processors>0 then
usedfonts[font]=processors
- elseif basepass then
+ elseif basemodepass then
basefont={ n,nil }
basefonts[#basefonts+1]=basefont
end
@@ -16538,15 +23747,15 @@ function nodes.handlers.nodepass(head)
end
end
if variants then
- local char=n.char
+ local char=getchar(n)
if char>=0xFE00 and (char<=0xFE0F or (char>=0xE0100 and char<=0xE01EF)) then
local hash=variants[char]
if hash then
- local p=n.prev
- if p and p.id==glyph_code then
- local variant=hash[p.char]
+ local p=getprev(n)
+ if p and getid(p)==glyph_code then
+ local variant=hash[getchar(p)]
if variant then
- p.char=variant
+ setchar(p,variant)
if not redundant then
redundant={ n }
else
@@ -16561,15 +23770,15 @@ function nodes.handlers.nodepass(head)
if redundant then
for i=1,#redundant do
local n=redundant[i]
- remove_node(head,n)
+ remove_node(nuthead,n)
free_node(n)
end
end
- for d in traverse_id(disc_code,head) do
- local r=d.replace
+ for d in traverse_id(disc_code,nuthead) do
+ local _,_,r=getdisc(d)
if r then
for n in traverse_id(glyph_code,r) do
- local font=n.font
+ local font=getfont(n)
if font~=prevfont then
prevfont=font
local used=usedfonts[font]
@@ -16596,34 +23805,31 @@ function nodes.handlers.nodepass(head)
end
end
end
- if basepass and #basefonts>0 then
+ if basemodepass and #basefonts>0 then
for i=1,#basefonts do
local range=basefonts[i]
local start=range[1]
local stop=range[2]
- if start or stop then
- local prev=nil
- local next=nil
- local front=start==head
+ if start then
+ local front=nuthead==start
+ local prev,next
if stop then
- next=stop.next
+ next=getnext(stop)
start,stop=ligaturing(start,stop)
start,stop=kerning(start,stop)
- elseif start then
- prev=start.prev
+ else
+ prev=getprev(start)
start=ligaturing(start)
start=kerning(start)
end
if prev then
- start.prev=prev
- prev.next=start
+ setlink(prev,start)
end
if next then
- stop.next=next
- next.prev=stop
+ setlink(stop,next)
end
- if front then
- head=start
+ if front and nuthead~=start then
+ head=tonode(start)
end
end
end
@@ -16634,9 +23840,9 @@ function nodes.handlers.nodepass(head)
end
end
function nodes.handlers.basepass(head)
- if basepass then
- head=ligaturing(head)
- head=kerning(head)
+ if not basemodepass then
+ head=n_ligaturing(head)
+ head=n_kerning(head)
end
return head,true
end
@@ -16648,7 +23854,9 @@ function nodes.simple_font_handler(head)
if head then
head=nodepass(head)
head=injectpass(head)
- head=basepass(head)
+ if not basemodepass then
+ head=basepass(head)
+ end
protectpass(head)
return head,true
else