diff options
author | Philipp Gesang <phg@phi-gamma.net> | 2016-04-17 12:45:14 +0200 |
---|---|---|
committer | Philipp Gesang <phg@phi-gamma.net> | 2016-04-17 12:45:14 +0200 |
commit | c8734018b81eb2120372493a3767617eeaf0299c (patch) | |
tree | 987d7791ae6f39bcf371c72f87d6e8cf759f0c75 /src/fontloader/misc/fontloader-font-ttf.lua | |
parent | fc973a6dde1a78a59e50bc3850dfd0d06e7b2a03 (diff) | |
parent | 97ec9e582e5be33001c136a9c69b5eebee4fdb2a (diff) | |
download | luaotfload-c8734018b81eb2120372493a3767617eeaf0299c.tar.gz |
Merge pull request #330 from phi-gamma/master
fontloader update
Diffstat (limited to 'src/fontloader/misc/fontloader-font-ttf.lua')
-rw-r--r-- | src/fontloader/misc/fontloader-font-ttf.lua | 480 |
1 files changed, 480 insertions, 0 deletions
diff --git a/src/fontloader/misc/fontloader-font-ttf.lua b/src/fontloader/misc/fontloader-font-ttf.lua new file mode 100644 index 0000000..6df3392 --- /dev/null +++ b/src/fontloader/misc/fontloader-font-ttf.lua @@ -0,0 +1,480 @@ +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 -- 8-bit unsigned integer +local readushort = streamreader.readcardinal2 -- 16-bit unsigned integer +local readulong = streamreader.readcardinal4 -- 24-bit unsigned integer +local readchar = streamreader.readinteger1 -- 8-bit signed integer +local readshort = streamreader.readinteger2 -- 16-bit signed integer +local read2dot14 = streamreader.read2dot14 -- 16-bit signed fixed number with the low 14 bits of fraction (2.14) (F2DOT14) + +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 + else + report("missing contours composite %s, component %s of %s, glyph %s",index,i,#components,subindex) + end + end + 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 + end + +end + +local function readnothing(f,nofcontours) + return { + type = "nothing", + } +end + +-- begin of converter + +-- make paths: the ff code is quite complex but it looks like we need to deal +-- with all kind of on curve border cases + +local function curveto(m_x,m_y,l_x,l_y,r_x,r_y) -- todo: inline this + 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" -- "curveto" + } +end + +-- We could omit the operator which saves some 10%: +-- +-- #2=lineto #4=quadratic #6=cubic #3=moveto (with "m") +-- +-- For the moment we keep the original outlines but that default might change +-- in the future. In any case, a backend should support both. +-- +-- The code is a bit messy. I looked at the ff code but it's messy too. It has +-- to do with the fact that we need to look at points on the curve and control +-- points in between. This also means that we start at point 2 and have to look at +-- point 1 when we're at the end. We still use a ps like storage with the operator +-- last in an entry. It's typical code that evolves stepwise till a point of no +-- comprehension. + +local function contours2outlines(glyphs,shapes) + local quadratic = true + -- local quadratic = false + 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] + -- todo no new tables but reuse lineto and quadratic + if nofcontour == 1 then + -- this can influence the boundingbox + first_pt[3] = "m" -- "moveto" + nofsegments = nofsegments + 1 + segments[nofsegments] = first_pt + else -- maybe also treat n == 2 special + 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" } -- "moveto" + 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 + -- both normal points + nofsegments = nofsegments + 1 + segments[nofsegments] = { current_pt[1], current_pt[2], "l" } -- "lineto" + 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" } -- "quadraticto" + 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" } -- "quadraticto" + 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 + -- we're already done, probably a simple curve + else + nofsegments = nofsegments + 1 + if not control_pt then + segments[nofsegments] = { first_pt[1], first_pt[2], "l" } -- "lineto" + elseif quadratic then + segments[nofsegments] = { control_pt[1], control_pt[2], first_pt[1], first_pt[2], "q" } -- "quadraticto" + 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 +end + +-- end of converter + +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) +-- f:seek("set",f:seek()+nofinstructions) + skipbytes(f,nofinstructions) + -- because flags can repeat we don't know the amount ... in fact this is + -- not that efficient (small files but more mem) + 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 + -- first come the x coordinates, and next the y coordinates and they + -- can be repeated + 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 + -- copy + 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 + -- copy + else + y = y + readshort(f) + end + points[i][2] = y + end + -- we could integrate this if needed + 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", + -- points = points, + contours = endpoints, + } +end + +local function readcomposite(f) + local components = { } + local nofcomponents = 0 + local instructions = false + while true do + local flags = readushort(f) + local index = readushort(f) + ----- f_words = bittest(flags,0x0001) + local f_xyarg = bittest(flags,0x0002) + ----- f_round = bittest(flags,0x0004+0x0002) + ----- f_scale = bittest(flags,0x0008) + ----- f_reserved = bittest(flags,0x0010) + ----- f_more = bittest(flags,0x0020) + ----- f_xyscale = bittest(flags,0x0040) + ----- f_matrix = bittest(flags,0x0080) + ----- f_instruct = bittest(flags,0x0100) + ----- f_usemine = bittest(flags,0x0200) + ----- f_overlap = bittest(flags,0x0400) + local f_offset = bittest(flags,0x0800) + ----- f_uoffset = bittest(flags,0x1000) + 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 -- f_words + xoffset = readshort(f) + yoffset = readshort(f) + else + xoffset = readchar(f) -- signed byte, stupid name + yoffset = readchar(f) -- signed byte, stupid name + end + else + if bittest(flags,0x0001) then -- f_words + base = readshort(f) + reference = readshort(f) + else + base = readchar(f) -- signed byte, stupid name + reference = readchar(f) -- signed byte, stupid name + end + end + if bittest(flags,0x0008) then -- f_scale + xscale = read2dot14(f) + yscale = xscale + if f_xyarg and f_offset then + xoffset = xoffset * xscale + yoffset = yoffset * yscale + end + elseif bittest(flags,0x0040) then -- f_xyscale + 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 -- f_matrix + 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), -- f_usemine + round = bittest(flags,0x0006), -- f_round, + 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 -- f_more + break + end + end + return { + type = "composite", + components = components, + } +end + +-- function readers.cff(f,offset,glyphs,doshapes) -- false == no shapes (nil or true otherwise) + +-- The glyf table depends on the loca table. We have one entry to much +-- in the locations table (the last one is a dummy) because we need to +-- calculate the size of a glyph blob from the delta, although we not +-- need it in our usage (yet). We can remove the locations table when +-- we're done (todo: cleanup finalizer). + +function readers.loca(f,fontdata,specification) + if specification.glyphs then + local datatable = fontdata.tables.loca + if datatable then + -- locations are relative to the glypdata table (glyf) + 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) -- part goes to cff module + 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), -- xmin + readshort(f), -- ymin + readshort(f), -- xmax + readshort(f), -- ymax + } + if not loadshapes then + -- save space + 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 + 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 |