Check-in [f19c56a4b9]
Not logged in

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Add table overrides, add ASCII aliases, fix typos, fix character orientation to match paper tape specification, and add tape edge lines.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: f19c56a4b901b417f861d1733cd850806bb6b771
User & Date: Ross 2016-03-15 00:02:49
Context
2016-03-19
02:35
Add support for additional raw XML tables provided by a dotfont file. This is experimental and will change. It is demonstrated in sevensegmentextended.dotfont as a way to provide teh kerning table. check-in: e68ddc1529 user: Ross tags: trunk
2016-03-15
00:02
Add table overrides, add ASCII aliases, fix typos, fix character orientation to match paper tape specification, and add tape edge lines. check-in: f19c56a4b9 user: Ross tags: trunk
2016-03-10
21:22
Pick up circle definition as improved for the punched paper tape font. Adjust Makefile to have sensible targets, and to build TTF files for each demo font by default. check-in: 5322fb3e47 user: Ross tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/dotfonter.lua.

65
66
67
68
69
70
71









72
73
74
75
76
77
78
    if glyph.mac then
        ttfont.MapGlyph("mac", glyph.mac, glyph.name)
    end
    ttfont.MapGlyph("win", glyph.win, glyph.name)
    n = n + 1
end
V('placed: 4 system glyphs and ', n, ' defined glyphs, total ', n+4, '.')










--[[ Set the strings. 

Note that ffam, fsub, ufid, name, vers, and PSnm are required by Windows.
Of those, fsub defaults to 'Regular' and name gets a sensible defaults based
on ffam and fsub.








>
>
>
>
>
>
>
>
>







65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
    if glyph.mac then
        ttfont.MapGlyph("mac", glyph.mac, glyph.name)
    end
    ttfont.MapGlyph("win", glyph.win, glyph.name)
    n = n + 1
end
V('placed: 4 system glyphs and ', n, ' defined glyphs, total ', n+4, '.')


if dotfont.ASCIIAliasBase then
    ttfont.MapASCIIAliases("unicode", dotfont.ASCIIAliasBase)
    ttfont.MapASCIIAliases("mac", dotfont.ASCIIAliasBase)
    ttfont.MapASCIIAliases("win", dotfont.ASCIIAliasBase)
    V('aliased ASCII printable characters.')
end


--[[ Set the strings. 

Note that ffam, fsub, ufid, name, vers, and PSnm are required by Windows.
Of those, fsub defaults to 'Regular' and name gets a sensible defaults based
on ffam and fsub.

86
87
88
89
90
91
92
93
94
95
96
            V(("%0.f: %q"):format(i, names[i]))
        end
    end
end


-- get the finished font and write it as TTX
local font = ttfont.GetFont()
--print(font)
font:save(args.basename .. ".ttx")
V('wrote: ', args.basename, '.ttx')







|



95
96
97
98
99
100
101
102
103
104
105
            V(("%0.f: %q"):format(i, names[i]))
        end
    end
end


-- get the finished font and write it as TTX
local font = ttfont.GetFont(dotfont.Overrides)
--print(font)
font:save(args.basename .. ".ttx")
V('wrote: ', args.basename, '.ttx')

Changes to src/papertape.dotfont.

60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81

local holes = {
    data = circle(37,51,51),
    timing = circle(23,51,51)
}
local function hole(n,h)
    local t={}
    local no = math.floor(819.2-n*102.4+0.5)
    for i=1,#h do
        t[i] = { h[i][1], h[i][2]+no, h[i][3] }
    end
    return t
end

-- Normalize a a glyph in the character ROM. 
-- Create an array of countours from its pat entry and provide defaults 
-- for metrics, name, and character codes.
local function Glyph(t)
    assert(type(t)=='table')
    local pat = assert(t.pat)
    
    -- default metrics







|






|







60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81

local holes = {
    data = circle(37,51,51),
    timing = circle(23,51,51)
}
local function hole(n,h)
    local t={}
    local no = math.floor(n*102.4+51.2+0.5)
    for i=1,#h do
        t[i] = { h[i][1], h[i][2]+no, h[i][3] }
    end
    return t
end

-- Normalize a a glyph. 
-- Create an array of countours from its pat entry and provide defaults 
-- for metrics, name, and character codes.
local function Glyph(t)
    assert(type(t)=='table')
    local pat = assert(t.pat)
    
    -- default metrics
94
95
96
97
98
99
100


101
102
103






104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134

135
136
137
138
139
140
141
142
143
144
145
146
147
148















149

150


















151



    t[#t+1] = hole(3, holes.timing)
    for bit=0,7 do
        if n%2 == 1 then
            t[#t+1] = hole(bit > 2 and bit+1 or bit, holes.data)
        end
        n = math.floor(n / 2)
    end


    if t.unicode == 0xEE00 then
        t.lsb = 28
    end






    return t
end

--[[ 
##Punched Paper Tape

Glyphs are layed out so that the least bit is at the top, or the 
tape is read from left to right.

--]]

--[[
        Glyph{unicode=0x20, name='space', pat={
                '.....',
                '.....',
                '.....',
                '.....',
                '.....',
                '.....',
                '.....',
            }
        },
--]]
local Gs = {}
for i=0,255,1 do
    local c = Glyph{unicode=0xee00+i, name=("U+EE%02X"):format(i), pat=i}
    Gs[#Gs+1] = c
end

return {
    Glyphs = Gs,


    --[[ Set the strings. 

    Note that ffam, fsub, ufid, name, vers, and PSnm are required by Windows.
    Of those, fsub defaults to 'Regular' and name gets a sensible defaults based
    on ffam and fsub.

    --]]
    Names = {
        copy='Copyright Ross Berteig 2016.',
        ffam='Punched Paper Tape LtR',
        PSnm='PunchedPaperTapeLtR-Regular',
        ufid='Punched Paper Tape LtR '.. os.date"%Y-%m-%d" ,
        vers='Version 001.000',















        demo='0123456789',

    }


















}










>
>



>
>
>
>
>
>










<
<
<
<
<
<
<
<
<
<
<
<
<
|

|
|



|
>
|












|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
>
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121













122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
    t[#t+1] = hole(3, holes.timing)
    for bit=0,7 do
        if n%2 == 1 then
            t[#t+1] = hole(bit > 2 and bit+1 or bit, holes.data)
        end
        n = math.floor(n / 2)
    end
    
    -- correct LSB on code 0
    if t.unicode == 0xEE00 then
        t.lsb = 28
    end
    
    -- add tape edges with LSB to match
    t[#t+1] = {{-2,0},{-2,8},{105,8},{105,0}}
    t[#t+1] = {{-2,1016},{-2,1024},{105,1024},{105,1016}}
    t.lsb = -2
    
    return t
end

--[[ 
##Punched Paper Tape

Glyphs are layed out so that the least bit is at the top, or the 
tape is read from left to right.

--]]













local Glyphs = {}
for i=0,255,1 do
    local c = Glyph{unicode=0xee00+i, name=("uniEE%02X"):format(i), pat=i}
    Glyphs[#Glyphs+1] = c
end

return {
    Glyphs = Glyphs,
    ASCIIAliasBase = 0xee00,
    
    --[[ Set the strings. 

    Note that ffam, fsub, ufid, name, vers, and PSnm are required by Windows.
    Of those, fsub defaults to 'Regular' and name gets a sensible defaults based
    on ffam and fsub.

    --]]
    Names = {
        copy='Copyright Ross Berteig 2016.',
        ffam='Punched Paper Tape LtR',
        PSnm='PunchedPaperTapeLtR-Regular',
        ufid='Punched Paper Tape LtR '.. os.date"%Y-%m-%d" ,
        vers='Version 001.005',
        desc=[[
Each code point in range U+EE00 to U+EEFF (in the Unicode Private Use Area) maps
to a glyph showing a single row of punched paper tape with that byte punched. The
rows are layed out with the reference tape edge at the bottom, and if presented
bytes in the order they are to be read from tape, the image will be of the reference
face of a tape fed from left to right.
]],
        lice=[[
This font is licensed under the Creative Commons 
Attribution 4.0 International License. (CC-BY) 
]],
        URLl='https://creativecommons.org/licenses/by/4.0/',
        URLv='http://curioser.cheshireeng.com/',
        URLd='http://curioser.cheshireeng.com/category/projects/dotfonter/',
        desi='Ross Berteig',
        --demo='',
    },
    
    Overrides = {
        OS_2 = {
            xAvgCharWidth = "103",
            ulUnicodeRange1 = "00000000 00000000 00000000 00000001",
            ulUnicodeRange2 = "00010000 00000000 00000000 00000000", -- Private use area
            ulCodePageRange1 = "00000000 00000000 00000000 00000001",
        },
        head = {
            xMax = "103"
        },
        hhea = {
            descent = -1
        },
        hmtx = {
            [1] = { width = 103 }, -- override width of .notdef
            [3] = { width = 103 }, -- override width of nonmarkingreturn
            [4] = { width = 103 }, -- override width of space
        },
        
    }
    
}

Changes to src/ttfhead.lua.

43
44
45
46
47
48
49
50




51
52
53
54
55
56
57
58
59
60
M.lowestRecPPEM = 8
M.fontDirectionHint = 2
M.indexToLocFormat = 0
M.glyphDataFormat = 0
M.created = "Sun Feb 27 14:41:58 2011"
M.modified = M.created







function M.GetTable()
    return ttx.buildTable("head",{ 
            'tableVersion','fontRevision','checkSumAdjustment','magicNumber',
            'flags','unitsPerEm','created','modified','xMin','yMin','xMax','yMax','macStyle',
            'lowestRecPPEM','fontDirectionHint','indexToLocFormat','glyphDataFormat'},
        M)
end

return M







|
>
>
>
>










43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
M.lowestRecPPEM = 8
M.fontDirectionHint = 2
M.indexToLocFormat = 0
M.glyphDataFormat = 0
M.created = "Sun Feb 27 14:41:58 2011"
M.modified = M.created

function M.SetRevisions(version)
    local v = version:match"%d+.%d+"
    if v then M.fontRevision = v end
    M.modified = os.date"!%a %b %d %H:%M:%S %Y"
end

function M.GetTable()
    return ttx.buildTable("head",{ 
            'tableVersion','fontRevision','checkSumAdjustment','magicNumber',
            'flags','unitsPerEm','created','modified','xMin','yMin','xMax','yMax','macStyle',
            'lowestRecPPEM','fontDirectionHint','indexToLocFormat','glyphDataFormat'},
        M)
end

return M

Changes to src/ttfname.lua.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
--[[ 
#TTF name table

This is where all the text metadata about the font lives, and often in 
multiple copies for each string. Also possibly in multiple languages, but
this code isn't explicitly supporting that.

The strings are identified by an unsigned short value that the TTF spec
calls an "index". But it is normal for some indeces to be missing. I haven't 
found a clear statement of which strings are mandatory, and that might be
platform and consuming application dependent.

Indeces 1, 2, 3, 4, 6, 16, 17, 18 and 20 are all clearly intended to be 
seen or used by end users to choose this font by name. Index 19 ought to be 
used by anything that presents font specimins, but testing shows that Windows 
ignores it.

According to [this answer at Stack Overflow](http://stackoverflow.com/a/21815641/68204) strings 3 and 6 are mandatory for a 
font to be installed (or even Previewed) in Windows, which was not clear from
any text in any of the TTF or OTF documentation.












|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
--[[ 
#TTF name table

This is where all the text metadata about the font lives, and often in 
multiple copies for each string. Also possibly in multiple languages, but
this code isn't explicitly supporting that.

The strings are identified by an unsigned short value that the TTF spec
calls an "index". But it is normal for some indeces to be missing. I haven't 
found a clear statement of which strings are mandatory, and that might be
platform and consuming application dependent.

Indices 1, 2, 3, 4, 6, 16, 17, 18 and 20 are all clearly intended to be 
seen or used by end users to choose this font by name. Index 19 ought to be 
used by anything that presents font specimins, but testing shows that Windows 
ignores it.

According to [this answer at Stack Overflow](http://stackoverflow.com/a/21815641/68204) strings 3 and 6 are mandatory for a 
font to be installed (or even Previewed) in Windows, which was not clear from
any text in any of the TTF or OTF documentation.

Changes to src/ttfont.lua.

67
68
69
70
71
72
73

74
75
76
77
78
79
80
81
82
83
84








85
86
87
88
89
90
91
local ttfpost = require 'ttfpost'
local ttfos_2 = require 'ttfos_2'

M.maxX, M.minX, M.maxY, M.minY = 0,0,0,0
M.perEm = 1024
M.head = ttfhead
M.hhea = ttfhhea


local tables = {}

-- Rember the order glyphs get created so that the TTF file 
-- will define them in the same order. This works by using the
-- table GlyphOrder defined by TTX for the purpose
tables.GlyphOrder = X"GlyphOrder"
tables.nextid = 0

-- Horizontal metrics for each glyph.
tables.hmtx = X"hmtx"









-- Glyph outlines for each glyph, along with an empty 
-- index table that will be entirely filled by the
-- TTX to TTF compiler
tables.loca = X"loca"
tables.glyf = X"glyf"
    







>











>
>
>
>
>
>
>
>







67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
local ttfpost = require 'ttfpost'
local ttfos_2 = require 'ttfos_2'

M.maxX, M.minX, M.maxY, M.minY = 0,0,0,0
M.perEm = 1024
M.head = ttfhead
M.hhea = ttfhhea


local tables = {}

-- Rember the order glyphs get created so that the TTF file 
-- will define them in the same order. This works by using the
-- table GlyphOrder defined by TTX for the purpose
tables.GlyphOrder = X"GlyphOrder"
tables.nextid = 0

-- Horizontal metrics for each glyph.
tables.hmtx = X"hmtx"

-- Tables that can have entries overridden 
local overridable = {
    head = ttfhead,
    hhea = ttfhhea,
    OS_2 = ttfos_2,
    hmtx = tables.hmtx,
}

-- Glyph outlines for each glyph, along with an empty 
-- index table that will be entirely filled by the
-- TTX to TTF compiler
tables.loca = X"loca"
tables.glyf = X"glyf"
    
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
        {896,   0},
    },
    contour{
        {192,  64},
        {832,  64},
        {832, 704},
        {192, 704},
    },
    contour{
        {128,  45},
        {851, 768},
        {896, 723},
        {183,   0},
    },
    contour{
        {128, 723},
        {183, 768},
        {896,  45},
        {851,   0},
    }, 
    xml.new{[0]='instructions',xml.new'assembly'},
}
-- an empty glyph.
local nullglyph = xml.new{[0]="TTGlyph", name=".null" }
local crglyph = xml.new{[0]="TTGlyph", name="nonmarkingreturn" }
local spaceglyph = xml.new{[0]="TTGlyph", name="space" }








|











|







147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
        {896,   0},
    },
    contour{
        {192,  64},
        {832,  64},
        {832, 704},
        {192, 704},
    }, --[[
    contour{
        {128,  45},
        {851, 768},
        {896, 723},
        {183,   0},
    },
    contour{
        {128, 723},
        {183, 768},
        {896,  45},
        {851,   0},
    }, --]]
    xml.new{[0]='instructions',xml.new'assembly'},
}
-- an empty glyph.
local nullglyph = xml.new{[0]="TTGlyph", name=".null" }
local crglyph = xml.new{[0]="TTGlyph", name="nonmarkingreturn" }
local spaceglyph = xml.new{[0]="TTGlyph", name="space" }

238
239
240
241
242
243
244












245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265

266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288



























289
290
291
292
293
294
295
296
297
298
299
300
301

302
303
304
305
306
307
308
309
310
311
312
313
314
    map = maps[map] or tonumber(map)
    if not map then error"map not valid" end
    if map >= 2 and map <= 4 then
        setname(map, code, name)
    end
end














tables.name = ttfname.BuildNameTable{
    copy='Copyright Lloyd Fingal 2015.',
    ufid='xyzzy',
    ffam='Sample Silly Script',
    PSnm='SampleSillyScript-Regular',
--[=[    [1024]=[[
Lorem ipsum dolor sit amet, consectetur adipisicing elit, 
sed do eiusmod tempor incididunt ut labore et dolore magna 
aliqua. Ut enim ad minim veniam, quis nostrud exercitation 
ullamco laboris nisi ut aliquip ex ea commodo consequat. 
Duis aute irure dolor in reprehenderit in voluptate velit 
esse cillum dolore eu fugiat nulla pariatur. Excepteur sint 
occaecat cupidatat non proident, sunt in culpa qui officia 
deserunt mollit anim id est laborum.
]]
--]=]    
}

function M.SetNames(t)
    local names

    tables.name, names = ttfname.BuildNameTable(t)
    return names
end

function M.GetFont()
    
    ttfhead.xMin = M.minX
    ttfhead.yMin = M.minY
    ttfhead.xMax = M.maxX
    ttfhead.yMax = M.maxY
    
    ttfhhea.ascent = M.maxY
    ttfos_2.sTypoAscender = M.maxY
    ttfos_2.usWinAscent = M.maxY
    
    ttfhhea.descent = M.minY
    ttfos_2.sTypoDescender = M.minY
    ttfos_2.usWinDescent = math.abs(M.minY)
    
    ttfhhea.xMaxExtent = M.maxX
    ttfhhea.lineGap = 128
    ttfos_2.sTypoLineGap = 128





























    tables.head = ttfhead.GetTable()
    tables.hhea = ttfhhea.GetTable()
    tables.maxp = ttfmaxp.GetTable()
    tables.post = ttfpost.GetTable()
    tables.OS_2 = ttfos_2.GetTable()

    if not tables.loca then tables.loca = xml.new"loca" end
    
    --[[
    if not tables.gasp then tables.gasp = xml.new"gasp" end
    tables.gasp:append(xml.new{[0]="gaspRange", rangeMaxPPEM=65535, rangeGaspBehavior=2})
    --]]

    
    local font = xml.new{[0]="ttFont", sfntVersion="\\x00\\x01\\x00\\x00", ttLibVersion="3.0"}
    for _,tn in ipairs{'GlyphOrder','head','hhea','maxp',
        'OS_2','hmtx','cmap','loca','glyf','name','post', 'gasp'} do
        if tables[tn] then 
            font:append(tables[tn])
        end
    end
    return font
end


return M







>
>
>
>
>
>
>
>
>
>
>
>



















<
|
>




|


















>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>













>













247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284

285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
    map = maps[map] or tonumber(map)
    if not map then error"map not valid" end
    if map >= 2 and map <= 4 then
        setname(map, code, name)
    end
end

function M.MapASCIIAliases(map, base)
    local maps = {unicode=2, mac=3, win=4}
    map = maps[map] or tonumber(map)
    if not map then error"map not valid" end
    for ascii=0x21,0x7e do
        local name = ("uni%04X"):format(base+ascii)
        if map >= 2 and map <= 4 then
            setname(map, ascii, name)
        end
    end
end


tables.name = ttfname.BuildNameTable{
    copy='Copyright Lloyd Fingal 2015.',
    ufid='xyzzy',
    ffam='Sample Silly Script',
    PSnm='SampleSillyScript-Regular',
--[=[    [1024]=[[
Lorem ipsum dolor sit amet, consectetur adipisicing elit, 
sed do eiusmod tempor incididunt ut labore et dolore magna 
aliqua. Ut enim ad minim veniam, quis nostrud exercitation 
ullamco laboris nisi ut aliquip ex ea commodo consequat. 
Duis aute irure dolor in reprehenderit in voluptate velit 
esse cillum dolore eu fugiat nulla pariatur. Excepteur sint 
occaecat cupidatat non proident, sunt in culpa qui officia 
deserunt mollit anim id est laborum.
]]
--]=]    
}


local names
function M.SetNames(t)
    tables.name, names = ttfname.BuildNameTable(t)
    return names
end

function M.GetFont(overrides)
    
    ttfhead.xMin = M.minX
    ttfhead.yMin = M.minY
    ttfhead.xMax = M.maxX
    ttfhead.yMax = M.maxY
    
    ttfhhea.ascent = M.maxY
    ttfos_2.sTypoAscender = M.maxY
    ttfos_2.usWinAscent = M.maxY
    
    ttfhhea.descent = M.minY
    ttfos_2.sTypoDescender = M.minY
    ttfos_2.usWinDescent = math.abs(M.minY)
    
    ttfhhea.xMaxExtent = M.maxX
    ttfhhea.lineGap = 128
    ttfos_2.sTypoLineGap = 128

    ttfhead.SetRevisions(names[5])
    
    if overrides then
        for k,v in pairs(overrides) do 
            local t = overridable[k]
            if t then
                for k,v in pairs(v) do
                    local node = t[k]
                    if node then
                        if type(node)=='string' then
                            t[k] = tostring(v)
                        elseif type(node)=='number' then
                            t[k] = tonumber(v)
                        elseif type(v)=='table' and type(node)=='table' then
                            for k,v in pairs(v) do
                                node[k] = v
                            end
                        else
                            --can't override
                            if V then V("Can't override ", type(node), " with ", type(v), "\n") end
                        end
                    end
                end
            end
        end
    end


    tables.head = ttfhead.GetTable()
    tables.hhea = ttfhhea.GetTable()
    tables.maxp = ttfmaxp.GetTable()
    tables.post = ttfpost.GetTable()
    tables.OS_2 = ttfos_2.GetTable()

    if not tables.loca then tables.loca = xml.new"loca" end
    
    --[[
    if not tables.gasp then tables.gasp = xml.new"gasp" end
    tables.gasp:append(xml.new{[0]="gaspRange", rangeMaxPPEM=65535, rangeGaspBehavior=2})
    --]]
    
    
    local font = xml.new{[0]="ttFont", sfntVersion="\\x00\\x01\\x00\\x00", ttLibVersion="3.0"}
    for _,tn in ipairs{'GlyphOrder','head','hhea','maxp',
        'OS_2','hmtx','cmap','loca','glyf','name','post', 'gasp'} do
        if tables[tn] then 
            font:append(tables[tn])
        end
    end
    return font
end


return M