summaryrefslogtreecommitdiff
path: root/tex/context/base/mkiv/lpdf-emb.lua
diff options
context:
space:
mode:
authorHans Hagen <pragma@wxs.nl>2020-07-20 11:09:33 +0200
committerContext Git Mirror Bot <phg@phi-gamma.net>2020-07-20 11:09:33 +0200
commit81095dd1bf20eb5f7e126adbdc8047f940504180 (patch)
treec0cdf7b074e8a2d00c56fde09519e89f155bd425 /tex/context/base/mkiv/lpdf-emb.lua
parentf1129626606384a7a55a21a83531f51f8b5dee25 (diff)
downloadcontext-81095dd1bf20eb5f7e126adbdc8047f940504180.tar.gz
2020-07-20 10:42:00
Diffstat (limited to 'tex/context/base/mkiv/lpdf-emb.lua')
-rw-r--r--tex/context/base/mkiv/lpdf-emb.lua2225
1 files changed, 0 insertions, 2225 deletions
diff --git a/tex/context/base/mkiv/lpdf-emb.lua b/tex/context/base/mkiv/lpdf-emb.lua
deleted file mode 100644
index 0f0388af0..000000000
--- a/tex/context/base/mkiv/lpdf-emb.lua
+++ /dev/null
@@ -1,2225 +0,0 @@
-if not modules then modules = { } end modules ['lpdf-ini'] = {
- version = 1.001,
- optimize = true,
- comment = "companion to lpdf-ini.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- vkgoeswild: Pink Floyd - Shine on You Crazy Diamond - piano cover (around that
--- time I redid the code, a reminder so to say)
-
--- At some point I wanted to have access to the shapes so that we could use them in
--- metapost. So, after looking at the cff and ttf specifications, I decided to write
--- parsers. At somepoint we needed a cff parser anyway in order to calculate the
--- dimensions. Then variable fonts came around and a option was added to recreate
--- streams of operators and a logical next step was to do all inclusion that way. It
--- was only then that I found out that some of the juggling also happens in the the
--- backend, but spread over places, so I could have saved myself some time
--- deciphering the specifications. Anyway, here we go.
---
--- Color fonts are a bit messy. Catching issues with older fonts will break new ones
--- so I don't think that it's wise to build in too many catches (like for fonts with
--- zero boundingboxes, weird dimensions, transformations that in a next version are
--- fixed, etc.). Better is then to wait till something gets fixed. If a spec doesn't
--- tell me how to deal with it ... I'll happily wait till it does. After all we're
--- not in a hurry as these fonts are mostly meant for the web or special purposes
--- with manual tweaking in desk top publishing applications. Keep in mind that Emoji
--- can have funny dimensions (e.g. to be consistent within a font, so no tight ones).
-
--- When we have moved to lmtx I will document a bit more. Till then it's experimental
--- and subjected to change.
-
-local next, type, unpack, rawget = next, type, unpack, rawget
-local char, byte, gsub, sub, match, rep, gmatch = string.char, string.byte, string.gsub, string.sub, string.match, string.rep, string.gmatch
-local formatters = string.formatters
-local format = string.format
-local concat, sortedhash, sort = table.concat, table.sortedhash, table.sort
-local utfchar = utf.char
-local random, round, max, abs, ceiling = math.random, math.round, math.max, math.abs, math.ceiling
-local extract, lshift, rshift, band, bor = bit32.extract, bit32.lshift, bit32.rshift, bit32.band, bit32.bor
-local idiv = number.idiv
-local setmetatableindex = table.setmetatableindex
-
-local pdfnull = lpdf.null
-local pdfdictionary = lpdf.dictionary
-local pdfarray = lpdf.array
-local pdfconstant = lpdf.constant
-local pdfstring = lpdf.string
-local pdfreference = lpdf.reference
-local pdfreserveobject = lpdf.reserveobject
-local pdfflushobject = lpdf.flushobject
-local pdfflushstreamobject = lpdf.flushstreamobject
-
-local report_fonts = logs.reporter("backend","fonts")
-
-local trace_fonts = false
-local trace_details = false
-
-local dimenfactors = number.dimenfactors
-local bpfactor = dimenfactors.bp
-local ptfactor = dimenfactors.pt
-
-trackers.register("backend.pdf.fonts",function(v) trace_fonts = v end)
-
-local readers = fonts.handlers.otf.readers
-local getinfo = readers.getinfo
-
-local setposition = utilities.files.setposition
-local readstring = utilities.files.readstring
-local openfile = utilities.files.open
-local closefile = utilities.files.close
-
-
--- needs checking: signed vs unsigned
-
-local tocardinal1 = char
-
-local function tocardinal2(n)
- return char(extract(n,8,8),extract(n,0,8))
-end
-
-local function tocardinal3(n)
- return char(extract(n,16,8),extract(n,8,8),extract(n,0,8))
-end
-
-local function tocardinal4(n)
- return char(extract(n,24,8),extract(n,16,8),extract(n,8,8),extract(n,0,8))
-end
-
-local function tointeger2(n)
- return char(extract(n,8,8),extract(n,0,8))
-end
-
-local function tointeger3(n)
- return char(extract(n,16,8),extract(n,8,8),extract(n,0,8))
-end
-
-local function tointeger4(n)
- return char(extract(n,24,8),extract(n,16,8),extract(n,8,8),extract(n,0,8))
-end
-
-local function tocardinal8(n)
- local l = idiv(n,0x100000000)
- local r = n % 0x100000000
- return char(extract(l,24,8),extract(l,16,8),extract(l,8,8),extract(l,0,8),
- extract(r,24,8),extract(r,16,8),extract(r,8,8),extract(r,0,8))
-end
-
--- A couple of shared helpers.
-
-local tounicodedictionary, widtharray, collectindices, subsetname, includecidset, forcecidset, tocidsetdictionary
-
-do
-
- -- Because we supply tounicodes ourselves we only use bfchar mappings (as in the
- -- backend). In fact, we can now no longer pass the tounicodes to the frontend but
- -- pick them up from the descriptions.
-
- local f_mapping_2 = formatters["<%02X> <%s>"]
- local f_mapping_4 = formatters["<%04X> <%s>"]
-
- local tounicode = fonts.mappings.tounicode
-
-local tounicode_template = [[
-%%!PS-Adobe-3.0 Resource-CMap
-%%%%DocumentNeededResources: ProcSet (CIDInit)
-%%%%IncludeResource: ProcSet (CIDInit)
-%%%%BeginResource: CMap (TeX-%s-0)
-%%%%Title: (TeX-%s-0 TeX %s 0)|
-%%%%Version: 1.000
-%%%%EndComments
-/CIDInit /ProcSet findresource begin
- 12 dict begin
- begincmap
- /CIDSystemInfo
- << /Registry (TeX) /Ordering (%s) /Supplement 0 >>
- def
- /CMapName
- /TeX-Identity-%s
- def
- /CMapType
- 2
- def
- 1 begincodespacerange
- <%s> <%s>
- endcodespacerange
- %i beginbfchar
-%s
- endbfchar
- endcmap
- CMapName currentdict /CMap defineresource pop
- end
-end
-%%%%EndResource
-%%%%EOF]]
-
- tounicodedictionary = function(details,indices,maxindex,name,wide)
- local mapping = { }
- local length = 0
- if maxindex > 0 then
- local f_mapping = wide and f_mapping_4 or f_mapping_2
- for index=1,maxindex do
- local data = indices[index]
- if data then
- length = length + 1
- local unicode = data.unicode
- if unicode then
- unicode = tounicode(unicode)
- else
- unicode = "FFFD"
- end
- mapping[length] = f_mapping(index,unicode)
- end
- end
- end
- local name = gsub(name,"%+","-") -- like luatex does
- local first = wide and "0000" or "00"
- local last = wide and "FFFF" or "FF"
- local blob = format(tounicode_template,name,name,name,name,name,first,last,length,concat(mapping,"\n"))
- return blob
- end
-
- widtharray = function(details,indices,maxindex,units)
- local widths = pdfarray()
- local length = 0
- local factor = 10000 / units
- if maxindex > 0 then
- local lastindex = -1
- local sublist = nil
- for index=1,maxindex do
- local data = indices[index]
- if data then
- local width = data.width -- hm, is inaccurate for cff, so take from elsewhere
- if width then
- -- width = round(width * 10000 / units) / 10
- width = round(width * factor) / 10
- else
- width = 0
- end
- if index == lastindex + 1 then
- sublist[#sublist+1] = width
- else
- if sublist then
- length = length + 1
- widths[length] = sublist
- end
- sublist = pdfarray { width }
- length = length + 1
- widths[length] = index
- end
- lastindex = index
- end
- end
- length = length + 1
- widths[length] = sublist
- end
- return widths
- end
-
- collectindices = function(descriptions,indices)
- local minindex = 0xFFFF
- local maxindex = 0
- local reverse = { }
- -- todo: already at definition time trigger copying streams
- -- and add extra indices ... first i need a good example of
- -- a clash
- -- for unicode, data in next, descriptions do
- -- local i = data.index or unicode
- -- if reverse[i] then
- -- print("CLASH")
- -- else
- -- reverse[i] = data
- -- end
- -- end
- for unicode, data in next, descriptions do
- reverse[data.index or unicode] = data
- end
- for index in next, indices do
- if index > maxindex then
- maxindex = index
- end
- if index < minindex then
- minindex = index
- end
- indices[index] = reverse[index]
- end
- if minindex > maxindex then
- minindex = maxindex
- end
- return indices, minindex, maxindex
- end
-
- includecidset = false
- forcecidset = false -- for private testing only
-
- directives.register("backend.pdf.forcecidset",function(v)
- forcecidset = v
- end)
-
- tocidsetdictionary = function(indices,min,max)
- if includecidset or forcecidset then
- local b = { }
- local m = idiv(max+7,8)
- for i=0,max do
- b[i] = 0
- end
- b[0] = bor(b[0],lshift(1,7)) -- force notdef into the file
- for i=min,max do
- if indices[i] then
- local bi = idiv(i,8)
- local ni = i % 8
- b[bi] = bor(b[bi],lshift(1,7-ni))
- end
- end
- b = char(unpack(b,0,#b))
- return pdfreference(pdfflushstreamobject(b))
- end
- end
-
- -- Actually we can use the same as we only embed once.
-
- -- subsetname = function(name)
- -- return "CONTEXT" .. name
- -- end
-
- local prefixes = { } -- todo: set fixed one
-
- subsetname = function(name)
- local prefix
- while true do
- prefix = utfchar(random(65,90),random(65,90),random(65,90),random(65,90),random(65,90),random(65,90))
- if not prefixes[prefix] then
- prefixes[prefix] = true
- break
- end
- end
- return prefix .. "+" .. name
- end
-
-end
-
--- Map file mess.
-
-local loadmapfile, loadmapline, getmapentry do
-
- -- We only need to pick up the filename and optionally the enc file
- -- as we only use them for old school virtual math fonts. We might as
- -- we drop this completely.
-
- local find, match, splitlines = string.find, string.match, string.splitlines
-
-
- local mappings = { }
-
- loadmapline = function(n)
- if trace_fonts then
- report_fonts("mapline: %s",n)
- end
- local name, fullname, encfile, pfbfile = match(n,"(%S+)%s+(%S+).-<(.-%.enc).-<(.-%.pfb)")
- if name then
- mappings[name] = { fullname, encfile, pfbfile }
- end
- end
-
- loadmapfile = function(n)
- local okay, data = resolvers.loadbinfile(n,"map")
- if okay and data then
- data = splitlines(data)
- for i=1,#data do
- local d = data[i]
- if d ~= "" and not find(d,"^[#%%]") then
- loadmapline(d)
- end
- end
- end
- end
-
- getmapentry = function(n)
- local n = file.nameonly(n)
- local m = mappings[n]
- if m then
- local encfile = m[2]
- local encoding = fonts.encodings.load(encfile)
- if not encoding then
- return
- end
- local pfbfile = resolvers.findfile(m[3],"pfb")
- if not pfbfile or pfbfile == "" then
- return
- end
- return encoding, pfbfile, encfile
- end
- end
-
- lpdf.getmapentry = getmapentry
-
-end
-
--- The three writers: opentype, truetype and type1.
-
-local mainwriters = { }
-
-do
-
- -- advh = os2.ascender - os2.descender
- -- tsb = default_advh - os2.ascender
-
- -- truetype has the following tables:
-
- -- head : mandate
- -- hhea : mandate
- -- vhea : mandate
- -- hmtx : mandate
- -- maxp : mandate
- -- glyf : mandate
- -- loca : mandate
- --
- -- cvt : if needed (but we flatten)
- -- fpgm : if needed (but we flatten)
- -- prep : if needed (but we flatten)
- -- PCLT : if needed (but we flatten)
- --
- -- name : not needed for T2: backend does that
- -- post : not needed for T2: backend does that
- -- OS/2 : not needed for T2: backend does that
- -- cmap : not needed for T2: backend does that
-
- local streams = utilities.streams
- local openstring = streams.openstring
- local readcardinal2 = streams.readcardinal2
- ----- readcardinal4 = streams.readcardinal4
-
- local otfreaders = fonts.handlers.otf.readers
-
- local function readcardinal4(f) -- this needs to be sorted out
- local a = readcardinal2(f)
- local b = readcardinal2(f)
- if a and b then
- return a * 0x10000 + b
- end
- end
-
- -- -- --
-
- local tablereaders = { }
- local tablewriters = { }
- local tablecreators = { }
- local tableloaders = { }
-
- local openfontfile, closefontfile, makefontfile, makemetadata do
-
- local details = {
- details = true,
- platformnames = true,
- platformextras = true,
- }
-
- -- .022 sec on luatex manual, neglectable:
-
- -- local function checksum(data)
- -- local s = openstring(data)
- -- local n = 0
- -- local d = #data
- -- while true do
- -- local c = readcardinal4(s)
- -- if c then
- -- n = (n + c) % 0x100000000
- -- else
- -- break
- -- end
- -- end
- -- return n
- -- end
-
- local function checksum(data)
- local s = openstring(data)
- local n = 0
- local d = #data
- while true do
- local a = readcardinal2(s)
- local b = readcardinal2(s)
- if b then
- n = (n + a * 0x10000 + b) % 0x100000000
- else
- break
- end
- end
- return n
- end
-
- openfontfile = function(details)
- return {
- offset = 0,
- order = { },
- used = { },
- details = details,
- streams = details.streams,
- }
- end
-
- closefontfile = function(fontfile)
- for k, v in next, fontfile do
- fontfile[k] = nil -- so it can be collected asap
- end
- end
-
- local metakeys = {
- "uniqueid", "version",
- "copyright", "license", "licenseurl",
- "manufacturer", "vendorurl",
- "family", "subfamily",
- "typographicfamily", "typographicsubfamily",
- "fullname", "postscriptname",
- }
-
- local template = [[
-<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>
- <x:xmpmeta xmlns:x="adobe:ns:meta/">
- <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
- <rdf:Description rdf:about="" xmlns:pdfx="http://ns.adobe.com/pdfx/1.3/">
-
-%s
-
- </rdf:Description>
- </rdf:RDF>
- </x:xmpmeta>
-<?xpacket end="w"?>]]
-
- makemetadata = function(fontfile)
- local names = fontfile.streams.names
- local list = { }
- local f_name = formatters["<pdfx:%s>%s</pdfx:%s>"]
- for i=1,#metakeys do
- local m = metakeys[i]
- local n = names[m]
- if n then
- list[#list+1] = f_name(m,n,m)
- end
- end
- return format(template,concat(list,"\n"))
- end
-
- makefontfile = function(fontfile)
- local order = fontfile.order
- local used = fontfile.used
- local count = 0
- for i=1,#order do
- local tag = order[i]
- local data = fontfile[tag]
- if data and #data > 0 then
- count = count + 1
- else
- fontfile[tag] = false
- end
- end
- local offset = 12 + (count * 16)
- local headof = 0
- local list = {
- "" -- placeholder
- }
- local i = 1
- local k = 0
- while i <= count do
- i = lshift(i,1)
- k = k + 1
- end
- local searchrange = lshift(i,3)
- local entryselector = k - 1
- local rangeshift = lshift(count,4) - lshift(i,3)
- local index = {
- tocardinal4(0x00010000), -- tables.version
- tocardinal2(count),
- tocardinal2(searchrange),
- tocardinal2(entryselector),
- tocardinal2(rangeshift),
- }
- --
- local ni = #index
- local nl = #list
- for i=1,#order do
- local tag = order[i]
- local data = fontfile[tag]
- if data then
- local csum = checksum(data)
- local dlength = #data
- local length = idiv(dlength+3,4) * 4
- local padding = length - dlength
- nl = nl + 1 ; list[nl] = data
- for i=1,padding do
- nl = nl + 1 ; list[nl] = "\0"
- end
- if #tag == 3 then
- tag = tag .. " "
- end
- ni = ni + 1 ; index[ni] = tag -- must be 4 chars
- ni = ni + 1 ; index[ni] = tocardinal4(csum)
- ni = ni + 1 ; index[ni] = tocardinal4(offset)
- ni = ni + 1 ; index[ni] = tocardinal4(dlength)
- used[i] = offset -- not used
- if tag == "head" then
- headof = offset
- end
- offset = offset + length
- end
- end
- list[1] = concat(index)
- local off = #list[1] + headof + 1 + 8
- list = concat(list)
- local csum = (0xB1B0AFBA - checksum(list)) % 0x100000000
- list = sub(list,1,off-1) .. tocardinal4(csum) .. sub(list,off+4,#list)
- return list
- end
-
- local function register(fontfile,name)
- local u = fontfile.used
- local o = fontfile.order
- if not u[name] then
- o[#o+1] = name
- u[name] = true
- end
- end
-
- local function create(fontfile,name)
- local t = { }
- fontfile[name] = t
- return t
- end
-
- local function write(fontfile,name)
- local t = fontfile[name]
- if not t then
- return
- end
- register(fontfile,name)
- if type(t) == "table" then
- if t[0] then
- fontfile[name] = concat(t,"",0,#t)
- elseif #t > 0 then
- fontfile[name] = concat(t)
- else
- fontfile[name] = false
- end
- end
- end
-
- tablewriters.head = function(fontfile)
- register(fontfile,"head")
- local t = fontfile.streams.fontheader
- fontfile.head = concat {
- tocardinal4(t.version),
- tocardinal4(t.fontversionnumber),
- tocardinal4(t.checksum),
- tocardinal4(t.magic),
- tocardinal2(t.flags),
- tocardinal2(t.units),
- tocardinal8(t.created),
- tocardinal8(t.modified),
- tocardinal2(t.xmin),
- tocardinal2(t.ymin),
- tocardinal2(t.xmax),
- tocardinal2(t.ymax),
- tocardinal2(t.macstyle),
- tocardinal2(t.smallpixels),
- tocardinal2(t.directionhint),
- tocardinal2(t.indextolocformat),
- tocardinal2(t.glyphformat),
- }
- end
-
- tablewriters.hhea = function(fontfile)
- register(fontfile,"hhea")
- local t = fontfile.streams.horizontalheader
- local n = t and fontfile.nofglyphs or 0
- fontfile.hhea = concat {
- tocardinal4(t.version),
- tocardinal2(t.ascender),
- tocardinal2(t.descender),
- tocardinal2(t.linegap),
- tocardinal2(t.maxadvancewidth),
- tocardinal2(t.minleftsidebearing),
- tocardinal2(t.minrightsidebearing),
- tocardinal2(t.maxextent),
- tocardinal2(t.caretsloperise),
- tocardinal2(t.caretsloperun),
- tocardinal2(t.caretoffset),
- tocardinal2(t.reserved_1),
- tocardinal2(t.reserved_2),
- tocardinal2(t.reserved_3),
- tocardinal2(t.reserved_4),
- tocardinal2(t.metricdataformat),
- tocardinal2(n) -- t.nofmetrics
- }
- end
-
- tablewriters.vhea = function(fontfile)
- local t = fontfile.streams.verticalheader
- local n = t and fontfile.nofglyphs or 0
- register(fontfile,"vhea")
- fontfile.vhea = concat {
- tocardinal4(t.version),
- tocardinal2(t.ascender),
- tocardinal2(t.descender),
- tocardinal2(t.linegap),
- tocardinal2(t.maxadvanceheight),
- tocardinal2(t.mintopsidebearing),
- tocardinal2(t.minbottomsidebearing),
- tocardinal2(t.maxextent),
- tocardinal2(t.caretsloperise),
- tocardinal2(t.caretsloperun),
- tocardinal2(t.caretoffset),
- tocardinal2(t.reserved_1),
- tocardinal2(t.reserved_2),
- tocardinal2(t.reserved_3),
- tocardinal2(t.reserved_4),
- tocardinal2(t.metricdataformat),
- tocardinal2(n) -- t.nofmetrics
- }
- end
-
- tablewriters.maxp = function(fontfile)
- register(fontfile,"maxp")
- local t = fontfile.streams.maximumprofile
- local n = fontfile.nofglyphs
- -- if fontfile.streams.cffinfo then
- -- error
- -- end
- fontfile.maxp = concat {
- tocardinal4(0x00010000),
- tocardinal2(n),
- tocardinal2(t.points),
- tocardinal2(t.contours),
- tocardinal2(t.compositepoints),
- tocardinal2(t.compositecontours),
- tocardinal2(t.zones),
- tocardinal2(t.twilightpoints),
- tocardinal2(t.storage),
- tocardinal2(t.functiondefs),
- tocardinal2(t.instructiondefs),
- tocardinal2(t.stackelements),
- tocardinal2(t.sizeofinstructions),
- tocardinal2(t.componentelements),
- tocardinal2(t.componentdepth),
- }
- end
-
- tablecreators.loca = function(fontfile) return create(fontfile,"loca") end
- tablewriters .loca = function(fontfile) return write (fontfile,"loca") end
-
- tablecreators.glyf = function(fontfile) return create(fontfile,"glyf") end
- tablewriters .glyf = function(fontfile) return write (fontfile,"glyf") end
-
- tablecreators.hmtx = function(fontfile) return create(fontfile,"hmtx") end
- tablewriters .hmtx = function(fontfile) return write (fontfile,"hmtx") end
-
- tablecreators.vmtx = function(fontfile) return create(fontfile,"vmtx") end
- tablewriters .vmtx = function(fontfile) return write (fontfile,"vmtx") end
-
- tableloaders .cmap = function(fontfile) return read (fontfile,"cmap") end
- tablewriters .cmap = function(fontfile) return write (fontfile,"cmap") end
-
- tableloaders .name = function(fontfile) return read (fontfile,"name") end
- tablewriters .name = function(fontfile) return write (fontfile,"name") end
-
- tableloaders .post = function(fontfile) return read (fontfile,"post") end
- tablewriters .post = function(fontfile) return write (fontfile,"post") end
-
- end
-
- mainwriters["truetype"] = function(details)
- --
- local fontfile = openfontfile(details)
- local basefontname = details.basefontname
- local streams = details.streams
- local blobs = streams.streams
- local fontheader = streams.fontheader
- local horizontalheader = streams.horizontalheader
- local verticalheader = streams.verticalheader
- local maximumprofile = streams.maximumprofile
- local names = streams.names
- local descriptions = details.rawdata.descriptions
- local metadata = details.rawdata.metadata
- local indices = details.indices
- local metabbox = { fontheader.xmin, fontheader.ymin, fontheader.xmax, fontheader.ymax }
- local indices,
- minindex,
- maxindex = collectindices(descriptions,indices)
- local glyphstreams = tablecreators.glyf(fontfile)
- local locations = tablecreators.loca(fontfile)
- local horizontals = tablecreators.hmtx(fontfile)
- local verticals = tablecreators.vmtx(fontfile)
- --
- local zero2 = tocardinal2(0)
- local zero4 = tocardinal4(0)
- --
- local horizontal = horizontalheader.nofmetrics > 0
- local vertical = verticalheader.nofmetrics > 0
- --
- local streamoffset = 0
- local lastoffset = zero4
- local g, h, v = 0, 0, 0
- --
- -- todo: locate notdef
- --
- if minindex > 0 then
- local blob = blobs[0]
- if blob and #blob > 0 then
- locations[0] = lastoffset
- g = g + 1 ; glyphstreams[g] = blob
- h = h + 1 ; horizontals [h] = zero4
- if vertical then
- v = v + 1 ; verticals[v] = zero4
- end
- streamoffset = streamoffset + #blob
- lastoffset = tocardinal4(streamoffset)
- else
- report_fonts("missing .notdef in font %a",basefontname)
- end
- -- todo: use a rep for h/v
- for index=1,minindex-1 do
- locations[index] = lastoffset
- h = h + 1 ; horizontals[h] = zero4
- if vertical then
- v = v + 1 ; verticals[v] = zero4
- end
- end
- end
- for index=minindex,maxindex do
- locations[index] = lastoffset
- local data = indices[index]
- if data then
- local blob = blobs[index] -- we assume padding
- if blob and #blob > 0 then
- g = g + 1 ; glyphstreams[g] = blob
- h = h + 1 ; horizontals [h] = tocardinal2(data.width or 0)
- h = h + 1 ; horizontals [h] = tocardinal2(data.boundingbox[1])
- if vertical then
- v = v + 1 ; verticals[v] = tocardinal2(data.height or 0)
- v = v + 1 ; verticals[v] = tocardinal2(data.boundingbox[3])
- end
- streamoffset = streamoffset + #blob
- lastoffset = tocardinal4(streamoffset)
- else
- h = h + 1 ; horizontals[h] = zero4
- if vertical then
- v = v + 1 ; verticals[v] = zero4
- end
- report_fonts("missing blob for index %i in font %a",index,basefontname)
- end
- else
- h = h + 1 ; horizontals[h] = zero4
- if vertical then
- v = v + 1 ; verticals[v] = zero4
- end
- end
- end
- locations[maxindex+1] = lastoffset -- cf spec
- --
- local nofglyphs = maxindex + 1 -- include zero
- --
- fontheader.checksum = 0
- fontheader.indextolocformat = 1
- maximumprofile.nofglyphs = nofglyphs
- --
- fontfile.format = "tff"
- fontfile.basefontname = basefontname
- fontfile.nofglyphs = nofglyphs
- --
- tablewriters.head(fontfile)
- tablewriters.hhea(fontfile)
- if vertical then
- tablewriters.vhea(fontfile)
- end
- tablewriters.maxp(fontfile)
-
- tablewriters.loca(fontfile)
- tablewriters.glyf(fontfile)
-
- tablewriters.hmtx(fontfile)
- if vertical then
- tablewriters.vmtx(fontfile)
- end
- --
- local fontdata = makefontfile(fontfile)
- local fontmeta = makemetadata(fontfile)
- --
- fontfile = closefontfile(fontfile)
- --
- local units = metadata.units
- local basefont = pdfconstant(basefontname)
- local widths = widtharray(details,indices,maxindex,units)
- local object = details.objectnumber
- local tounicode = tounicodedictionary(details,indices,maxindex,basefontname,true)
- local tocidset = tocidsetdictionary(indices,minindex,maxindex)
- local metabbox = metadata.boundingbox or { 0, 0, 0, 0 }
- local fontbbox = pdfarray { unpack(metabbox) }
- local ascender = metadata.ascender
- local descender = metadata.descender
- local capheight = metadata.capheight or fontbbox[4]
- local stemv = metadata.weightclass
- local italicangle = metadata.italicangle
- local xheight = metadata.xheight or fontbbox[4]
- --
- if stemv then
- stemv = (stemv/65)^2 + 50
- end
- --
- local function scale(n)
- if n then
- return round((n) * 10000 / units) / 10
- else
- return 0
- end
- end
- --
- local reserved = pdfreserveobject()
- local child = pdfdictionary {
- Type = pdfconstant("Font"),
- Subtype = pdfconstant("CIDFontType2"),
- BaseFont = basefont,
- FontDescriptor = pdfreference(reserved),
- W = pdfreference(pdfflushobject(widths)),
- CIDToGIDMap = pdfconstant("Identity"),
- CIDSystemInfo = pdfdictionary {
- Registry = pdfstring("Adobe"),
- Ordering = pdfstring("Identity"),
- Supplement = 0,
- }
- }
- local descendants = pdfarray {
- pdfreference(pdfflushobject(child)),
- }
- local descriptor = pdfdictionary {
- Type = pdfconstant("FontDescriptor"),
- FontName = basefont,
- Flags = 4,
- FontBBox = fontbbox,
- Ascent = scale(ascender),
- Descent = scale(descender),
- ItalicAngle = round(italicangle or 0),
- CapHeight = scale(capheight),
- StemV = scale(stemv),
- XHeight = scale(xheight),
- FontFile2 = pdfreference(pdfflushstreamobject(fontdata)),
- CIDSet = tocidset,
- Metadata = fontmeta and pdfreference(pdfflushstreamobject(fontmeta)) or nil,
- }
- local parent = pdfdictionary {
- Type = pdfconstant("Font"),
- Subtype = pdfconstant("Type0"),
- Encoding = pdfconstant(details.properties.writingmode == "vertical" and "Identity-V" or "Identity-H"),
- BaseFont = basefont,
- DescendantFonts = descendants,
- ToUnicode = pdfreference(pdfflushstreamobject(tounicode)),
- }
- pdfflushobject(reserved,descriptor)
- pdfflushobject(object,parent)
- --
- -- if trace_details then
- -- local name = "temp.ttf"
- -- report_fonts("saving %a",name)
- -- io.savedata(name,fontdata)
- -- inspect(fonts.handlers.otf.readers.loadfont(name))
- -- end
- --
- end
-
- do
- -- todo : cff2
-
- local details = {
- details = true,
- platformnames = true,
- platformextras = true,
- }
-
- tablecreators.cff = function(fontfile)
- fontfile.charstrings = { }
- fontfile.charmappings = { }
- fontfile.cffstrings = { }
- fontfile.cffhash = { }
- return fontfile.charstrings , fontfile.charmappings
- end
-
- local todictnumber, todictreal, todictinteger, todictoffset do
-
- local maxnum = 0x7FFFFFFF
- local minnum = - 0x7FFFFFFF - 1
- local epsilon = 1.0e-5
-
- local int2tag = "\28"
- local int4tag = "\29"
- local realtag = "\30"
-
- todictinteger = function(n)
- if not n then
- return char(band(139,0xFF))
- elseif n >= -107 and n <= 107 then
- return char(band(n + 139,0xFF))
- elseif n >= 108 and n <= 1131 then
- n = 0xF700 + n - 108
- return char(band(rshift(n,8),0xFF),band(n,0xFF))
- elseif n >= -1131 and n <= -108 then
- n = 0xFB00 - n - 108
- return char(band(rshift(n,8),0xFF),band(n,0xFF))
- elseif n >= -32768 and n <= 32767 then
- -- return int2tag .. tointeger2(n)
- return char(28,extract(n,8,8),extract(n,0,8))
- else
- -- return int4tag .. tointeger4(n)
- return char(29,extract(n,24,8),extract(n,16,8),extract(n,8,8),extract(n,0,8))
- end
- end
-
- -- -- not called that often
- --
- -- local encoder = readers.cffencoder
- --
- -- todictinteger = function(n)
- -- if not n then
- -- return encoder[0]
- -- elseif n >= -1131 and n <= 1131 then
- -- return encoder[n]
- -- elseif n >= -32768 and n <= 32767 then
- -- -- return int2tag .. tointeger2(n)
- -- return char(28,extract(n,8,8),extract(n,0,8))
- -- else
- -- -- return int4tag .. tointeger4(n)
- -- return char(29,extract(n,24,8),extract(n,16,8),extract(n,8,8),extract(n,0,8))
- -- end
- -- end
-
- todictoffset = function(n)
- return int4tag .. tointeger4(n)
- end
-
- local e = false
- local z = byte("0")
- local dp = 10
- local ep = 11
- local em = 12
- local mn = 14
- local es = 15
-
- local fg = formatters["%g"]
-
- todictreal = function(v)
- local s = fg(v)
- local t = { [0] = realtag }
- local n = 0
- for s in gmatch(s,".") do
- if s == "e" or s == "E" then
- e = true
- elseif s == "+" then
- -- skip
- elseif s == "-" then
- n = n + 1
- if e then
- t[n] = em
- e = false
- else
- t[n] = mn
- end
- else
- if e then
- n = n + 1
- t[n] = ep
- e = false
- end
- n = n + 1
- if s == "." then
- t[n] = dp
- else
- t[n] = byte(s) - z
- end
- end
- end
- n = n + 1
- t[n] = es
- if (n % 2) ~= 0 then
- n = n + 1
- t[n] = es
- end
- local j = 0
- for i=1,n,2 do
- j = j + 1
- t[j] = char(t[i]*0x10+t[i+1])
- end
- t = concat(t,"",0,j)
- return t
- end
-
- todictnumber = function(n)
- if not n or n == 0 then
- return todictinteger(0)
- elseif (n > maxnum or n < minnum or (abs(n - round(n)) > epsilon)) then
- return todictreal(n)
- else
- return todictinteger(n)
- end
- end
-
- end
-
- local todictkey = char
-
- local function todictstring(fontfile,value)
- if not value then
- value = ""
- end
- local s = fontfile.cffstrings
- local h = fontfile.cffhash
- local n = h[value]
- if not n then
- n = #s + 1
- s[n] = value
- h[value] = n
- end
- return todictinteger(390+n)
- end
-
- local function todictboolean(b)
- return todictinteger(b and 1 or 0)
- end
-
- local function todictdeltas(t)
- local r = { }
- for i=1,#t do
- r[i] = todictnumber(t[i]-(t[i-1] or 0))
- end
- return concat(r)
- end
-
- local function todictarray(t)
- local r = { }
- for i=1,#t do
- r[i] = todictnumber(t[i])
- end
- return concat(r)
- end
-
- local function writestring(target,source,offset,what)
- target[#target+1] = source
- -- report_fonts("string : %-11s %06i # %05i",what,offset,#source)
- return offset + #source
- end
-
- local function writetable(target,source,offset,what)
- source = concat(source)
- target[#target+1] = source
- -- report_fonts("table : %-11s %06i # %05i",what,offset,#source)
- return offset + #source
- end
-
- local function writeindex(target,source,offset,what)
- local n = #source
- local t = #target
- t = t + 1 ; target[t] = tocardinal2(n)
- if n > 0 then
- local data = concat(source)
- local size = #data -- assume the worst
- local offsetsize, tocardinal
- if size < 0xFF then
- offsetsize, tocardinal = 1, tocardinal1
- elseif size < 0xFFFF then
- offsetsize, tocardinal = 2, tocardinal2
- elseif size < 0xFFFFFF then
- offsetsize, tocardinal = 3, tocardinal3
- elseif size < 0xFFFFFFFF then
- offsetsize, tocardinal = 4, tocardinal4
- end
- -- report_fonts("index : %-11s %06i # %05i (%i entries with offset size %i)",what,offset,#data,n,offsetsize)
- offset = offset + 2 + 1 + (n + 1) * offsetsize + size
- -- bytes per offset
- t = t + 1 ; target[t] = tocardinal1(offsetsize)
- -- list of offsets (one larger for length calculation)
- local offset = 1 -- mandate
- t = t + 1 ; target[t] = tocardinal(offset)
- for i=1,n do
- offset = offset + #source[i]
- t = t + 1 ; target[t] = tocardinal(offset)
- end
- t = t + 1 ; target[t] = data
- else
- -- report_fonts("index : %-11s %06i # %05i (no entries)",what,offset,0)
- offset = offset + 2
- end
- -- print("offset",offset,#concat(target))
- return offset
- end
-
- tablewriters.cff = function(fontfile)
- --
- local streams = fontfile.streams
- local cffinfo = streams.cffinfo or { }
- local names = streams.names or { }
- local fontheader = streams.fontheader or { }
- local basefontname = fontfile.basefontname
- --
- local offset = 0
- local dictof = 0
- local target = { }
- --
- local charstrings = fontfile.charstrings
- local nofglyphs = #charstrings + 1
- local fontmatrix = { 0.001, 0, 0, 0.001, 0, 0 } -- todo
- local fontbbox = fontfile.fontbbox
- local defaultwidth = cffinfo.defaultwidth or 0
- local nominalwidth = cffinfo.nominalwidth or 0
- local bluevalues = cffinfo.bluevalues
- local otherblues = cffinfo.otherblues
- local familyblues = cffinfo.familyblues
- local familyotherblues = cffinfo.familyotherblues
- local bluescale = cffinfo.bluescale
- local blueshift = cffinfo.blueshift
- local bluefuzz = cffinfo.bluefuzz
- local stdhw = cffinfo.stdhw
- local stdvw = cffinfo.stdvw
- --
- if defaultwidth == 0 then defaultwidth = nil end
- if nomimalwidth == 0 then nominalwidth = nil end
- if bluevalues then bluevalues = todictarray(bluevalues) end
- if otherblues then otherblues = todictarray(otherblues) end
- if familyblues then familyblues = todictarray(familyblues) end
- if familyotherblues then familyotherblues = todictarray(familyotherblues) end
- if bluescale then bluescale = todictnumber(bluescale) end
- if blueshift then blueshift = todictnumber(blueshift) end
- if bluefuzz then bluefuzz = todictnumber(bluefuzz) end
- if stdhw then stdhw = todictdeltas(stdhw) end
- if stdvw then stdvw = todictdeltas(stdvw) end
- --
- local fontversion = todictstring(fontfile,fontheader.fontversion or "uknown version")
- local familyname = todictstring(fontfile,cffinfo.familyname or names.family or basefontname)
- local fullname = todictstring(fontfile,cffinfo.fullname or basefontname)
- local weight = todictstring(fontfile,cffinfo.weight or "Normal")
- local fontbbox = todictarray(fontbbox)
- local strokewidth = todictnumber(cffinfo.strokewidth)
- local monospaced = todictboolean(cffinfo.monospaced)
- local italicangle = todictnumber(cffinfo.italicangle)
- local underlineposition = todictnumber(cffinfo.underlineposition)
- local underlinethickness = todictnumber(cffinfo.underlinethickness)
- local charstringtype = todictnumber(2)
- local fontmatrix = todictarray(fontmatrix)
- local ros = todictstring(fontfile,"Adobe") -- registry
- .. todictstring(fontfile,"Identity") -- identity
- .. todictnumber(0) -- supplement
- local cidcount = todictnumber(fontfile.nofglyphs)
- local fontname = todictstring(fontfile,basefontname)
- local fdarrayoffset = todictoffset(0)
- local fdselectoffset = todictoffset(0)
- local charstringoffset = todictoffset(0)
- local charsetoffset = todictoffset(0)
- local privateoffset = todictoffset(0)
- --
- local defaultwidthx = todictnumber(defaultwidth)
- local nominalwidthx = todictnumber(nominalwidth)
- local private = ""
- .. (defaultwidthx and (defaultwidthx .. todictkey(20)) or "")
- .. (nominalwidthx and (nominalwidthx .. todictkey(21)) or "")
- .. (bluevalues and (bluevalues .. todictkey(6)) or "")
- .. (otherblues and (otherblues .. todictkey(7)) or "")
- .. (familyblues and (familyblues .. todictkey(8)) or "")
- .. (familyotherblues and (familyotherblues .. todictkey(9)) or "")
- .. (bluescale and (bluescale .. todictkey(12,9)) or "")
- .. (blueshift and (blueshift .. todictkey(12,10)) or "")
- .. (bluefuzz and (bluefuzz .. todictkey(12,11)) or "")
- .. (stdhw and (stdhw .. todictkey(12,12)) or "")
- .. (stdvw and (stdvw .. todictkey(12,13)) or "")
- local privatesize = todictnumber(#private)
- local privatespec = privatesize .. privateoffset
- --
- -- header (fixed @ 1)
- --
- local header =
- tocardinal1(1) -- major
- .. tocardinal1(0) -- minor
- .. tocardinal1(4) -- header size
- .. tocardinal1(4) -- offset size
- --
- offset = writestring(target,header,offset,"header")
- --
- -- name index (fixed @ 2) (has to be sorted)
- --
- local names = {
- basefontname,
- }
- --
- offset = writeindex(target,names,offset,"names")
- --
- -- topdict index (fixed @ 3)
- --
- local topvars =
- charstringoffset .. todictkey(17)
- .. charsetoffset .. todictkey(15)
- .. fdarrayoffset .. todictkey(12,36)
- .. fdselectoffset .. todictkey(12,37)
- .. privatespec .. todictkey(18)
- --
- local topdict = {
- ros .. todictkey(12,30) -- first
- .. cidcount .. todictkey(12,34)
- .. familyname .. todictkey( 3)
- .. fullname .. todictkey( 2)
- .. weight .. todictkey( 4)
- .. fontbbox .. todictkey( 5)
- .. monospaced .. todictkey(12, 1)
- .. italicangle .. todictkey(12, 2)
- .. underlineposition .. todictkey(12, 3)
- .. underlinethickness .. todictkey(12, 4)
- .. charstringtype .. todictkey(12, 6)
- .. fontmatrix .. todictkey(12, 7)
- .. strokewidth .. todictkey(12, 8)
- .. topvars
- }
- --
- offset = writeindex(target,topdict,offset,"topdict")
- dictof = #target
- --
- -- string index (fixed @ 4)
- --
- offset = writeindex(target,fontfile.cffstrings,offset,"strings")
- --
- -- global subroutine index (fixed @ 5)
- --
- offset = writeindex(target,{},offset,"globals")
- --
- -- Encoding (cff1)
- --
- -- offset = writeindex(target,{},offset,"encoding")
- --
- -- Charsets
- --
- charsetoffset = todictoffset(offset)
- offset = writetable(target,fontfile.charmappings,offset,"charsets")
- --
- -- fdselect
- --
- local fdselect =
- tocardinal1(3) -- format
- .. tocardinal2(1) -- n of ranges
- -- entry 1
- .. tocardinal2(0) -- first gid
- .. tocardinal1(0) -- fd index
- -- entry 2
--- .. tocardinal2(fontfile.sparsemax-1) -- sentinel
- .. tocardinal2(fontfile.sparsemax) -- sentinel
- --
- fdselectoffset = todictoffset(offset)
- offset = writestring(target,fdselect,offset,"fdselect")
- --
- -- charstrings
- --
- charstringoffset = todictoffset(offset)
- offset = writeindex(target,charstrings,offset,"charstrings")
- --
- -- font dict
- --
- -- offset = writeindex(target,{},offset,"fontdict")
- --
- -- private
- --
- privateoffset = todictoffset(offset)
- privatespec = privatesize .. privateoffset
- offset = writestring(target,private,offset,"private")
- --
- local fdarray = {
- fontname .. todictkey(12,38)
- .. privatespec .. todictkey(18)
- }
- fdarrayoffset = todictoffset(offset)
- offset = writeindex(target,fdarray,offset,"fdarray")
- --
- topdict = target[dictof]
- topdict = sub(topdict,1,#topdict-#topvars)
- topvars =
- charstringoffset .. todictkey(17)
- .. charsetoffset .. todictkey(15)
- .. fdarrayoffset .. todictkey(12,36)
- .. fdselectoffset .. todictkey(12,37)
- .. privatespec .. todictkey(18)
- target[dictof] = topdict .. topvars
- --
- target = concat(target)
- -- if trace_details then
- -- local name = "temp.cff"
- -- report_fonts("saving %a",name)
- -- io.savedata(name,target)
- -- inspect(fonts.handlers.otf.readers.cffcheck(name))
- -- end
- return target
- end
-
- end
-
- -- todo: check widths (missing a decimal)
-
- mainwriters["opentype"] = function(details)
- --
- local fontfile = openfontfile(details)
- local basefontname = details.basefontname
- local streams = details.streams
- local blobs = streams.streams
- local fontheader = streams.fontheader
- local maximumprofile = streams.maximumprofile
- local names = streams.names
- local descriptions = details.rawdata.descriptions
- local metadata = details.rawdata.metadata
- local indices = details.indices
- local metabbox = { fontheader.xmin, fontheader.ymin, fontheader.xmax, fontheader.ymax }
- local correction = 1
-
- -- (*) We share code with type1 and when we have old school tfm with
- -- pfb but without descriptions we're kind of toast.
-
- if not descriptions or not next(descriptions) then
- -- This is good enough, we only need indices and widths.
- descriptions = details.fontdata.characters
- -- This is a hack, we have no basepoints.
- correction = details.fontdata.parameters.size / 1000
- -- And this needs checking.
- correction = correction * bpfactor / ptfactor
- metadata = { }
- end
-
- --
-
- local indices,
- minindex,
- maxindex = collectindices(descriptions,indices)
- local streamoffset = 0
- local glyphstreams,
- charmappings = tablecreators.cff(fontfile)
- --
- local zero2 = tocardinal2(0)
- local zero4 = tocardinal4(0)
- --
- -- we need to locate notdef (or store its unicode someplace)
- --
- local blob = blobs[0] or "\14"
- local sparsemax = 1
- local lastoffset = zero4
- glyphstreams[sparsemax] = blob
- charmappings[sparsemax] = tocardinal1(0) -- format 0
- streamoffset = streamoffset + #blob
- lastoffset = tocardinal4(streamoffset)
- if minindex == 0 then
- minindex = 1
- end
- for index=minindex,maxindex do
- if indices[index] then
- local blob = blobs[index] or "\14"
- sparsemax = sparsemax + 1
- glyphstreams[sparsemax] = blob
- charmappings[sparsemax] = tocardinal2(index)
- streamoffset = streamoffset + #blob
- lastoffset = tocardinal4(streamoffset)
- end
- end
- --
- fontfile.nofglyphs = maxindex + 1
- fontfile.sparsemax = sparsemax
- fontfile.format = "cff"
- fontfile.basefontname = basefontname
- fontfile.fontbbox = metabbox
- --
- local fontdata = tablewriters.cff(fontfile)
- local fontmeta = makemetadata(fontfile)
- --
- fontfile = closefontfile(fontfile)
- --
- local units = fontheader.units or metadata.units
-
- local basefont = pdfconstant(basefontname)
- local widths = widtharray(details,indices,maxindex,units * correction)
- local object = details.objectnumber
- local tounicode = tounicodedictionary(details,indices,maxindex,basefontname,true)
- local tocidset = tocidsetdictionary(indices,minindex,maxindex)
- local fontbbox = pdfarray { unpack(metabbox) }
- local ascender = metadata.ascender or 0
- local descender = metadata.descender or 0
- local capheight = metadata.capheight or fontbbox[4]
- local stemv = metadata.weightclass
- local italicangle = metadata.italicangle
- local xheight = metadata.xheight or fontbbox[4]
- if stemv then
- stemv = (stemv/65)^2 + 50
- else
--- stemv = 2
- end
- --
- local function scale(n)
- if n then
- return round((n) * 10000 / units) / 10
- else
- return 0
- end
- end
- --
- local reserved = pdfreserveobject()
- local child = pdfdictionary {
- Type = pdfconstant("Font"),
- Subtype = pdfconstant("CIDFontType0"),
- BaseFont = basefont,
- FontDescriptor = pdfreference(reserved),
- W = pdfreference(pdfflushobject(widths)),
- CIDSystemInfo = pdfdictionary {
- Registry = pdfstring("Adobe"),
- Ordering = pdfstring("Identity"),
- Supplement = 0,
- }
- }
- local descendants = pdfarray {
- pdfreference(pdfflushobject(child)),
- }
- local fontstream = pdfdictionary {
- Subtype = pdfconstant("CIDFontType0C"),
- }
- local descriptor = pdfdictionary {
- Type = pdfconstant("FontDescriptor"),
- FontName = basefont,
- Flags = 4,
- FontBBox = fontbbox,
- Ascent = scale(ascender),
- Descent = scale(descender),
- ItalicAngle = round(italicangle or 0),
- CapHeight = scale(capheight),
- StemV = scale(stemv),
- XHeight = scale(xheight),
- CIDSet = tocidset,
- FontFile3 = pdfreference(pdfflushstreamobject(fontdata,fontstream())),
- Metadata = fontmeta and pdfreference(pdfflushstreamobject(fontmeta)) or nil,
- }
- local parent = pdfdictionary {
- Type = pdfconstant("Font"),
- Subtype = pdfconstant("Type0"),
- Encoding = pdfconstant(details.properties.writingmode == "vertical" and "Identity-V" or "Identity-H"),
- BaseFont = basefont,
- DescendantFonts = descendants,
- ToUnicode = pdfreference(pdfflushstreamobject(tounicode)),
- }
- pdfflushobject(reserved,descriptor)
- pdfflushobject(object,parent)
- end
-
- mainwriters["type1"] = function(details)
- -- We abuse the cff includer which is ok but for special cases like
- -- tfm -> pfb we don't have the right descriptions and scale so this
- -- is why we cheat elsewhere. Maybe I should just drop that kind of
- -- support and assume afm files to be present. See (*) above.
- local s = details.streams
- local m = details.rawdata.metadata
- if m then
- local h = s.horizontalheader
- local c = s.cffinfo
- local n = s.names
- h.ascender = m.ascender or h.ascender
- h.descender = m.descender or h.descender
- n.copyright = m.copyright or n.copyright
- n.family = m.familyname or n.familyname
- n.fullname = m.fullname or n.fullname
- n.fontname = m.fontname or n.fontname
- n.subfamily = m.subfamilyname or n.subfamilyname
- n.version = m.version or n.version
- setmetatableindex(h,m)
- setmetatableindex(c,m)
- setmetatableindex(n,m)
- end
- mainwriters["opentype"](details)
- end
-
- do
-
- -- The methods might become plugins.
-
- local methods = { }
-
- local pdfimage = lpdf.epdf.image
- local openpdf = pdfimage.open
- local closepdf = pdfimage.close
- local copypage = pdfimage.copy
-
- local embedimage = images.embed
-
- local f_glyph = formatters["G%d"]
- local f_char = formatters["BT /V%d 1 Tf [<%04X>] TJ ET"]
- local f_width = formatters["%.6N 0 d0"]
- local f_index = formatters["I%d"]
- local f_image_xy = formatters["%.6N 0 d0 1 0 0 1 %.6N %.6N cm /%s Do"]
- local f_image_c = formatters["/%s Do"]
- local f_image_c_xy = formatters["%.6N 0 0 %.6N %.6N %.6N cm /%s Do"]
- local f_image_w = formatters["%.6N 0 d0 %s"]
- local f_image_d = formatters["%.6N 0 d0 1 0 0 1 0 %.6N cm /%s Do"]
- local f_stream = formatters["%.6N 0 d0 %s"]
- local f_stream_c = formatters["%.6N 0 0 0 0 0 d1 %s"]
- local f_stream_d = formatters["%.6N 0 d0 1 0 0 1 0 %.6N cm %s"]
-
- -- A type 3 font has at most 256 characters and Acrobat also wants a zero slot
- -- to be filled. We can share a mandate zero slot character. We also need to
- -- make sure that we use bytes as index in the page stream as well as in the
- -- tounicode vector.
-
- local c_notdef = nil
- local r_notdef = nil
- local w_notdef = nil
- local fontbbox = nil
-
- -- pk inclusion (not really tested but not really used either)
-
- function methods.pk(filename)
- local pkfullname = resolvers.findpk(basedfontname,resolution)
- if not pkfullname or pkfullname == "" then
- return
- end
- local readers = fonts.handlers.tfm.readers
- local result = readers.loadpk(pkfullname)
- local convert = readers.pktopdf
- if not result or result.error then
- return
- end
- local resolution = 600
- local widthfactor = resolution / 72
- local scalefactor = 72 / resolution / 10
- local factor = widthfactor / 65536
- local function pktopdf(glyph,data)
- return convert(glyph,data,factor) -- return pdfcode, width
- end
- return result.glyphs, scalefactor, pktopdf, false, false
- end
-
- -- pdf inclusion
-
- local used = setmetatableindex("table")
-
- function methods.pdf(filename,details)
- local properties = details.properties
- local pdfshapes = properties.indexdata[1]
- local pdfdoc = openpdf(pdfshapes.filename)
- local xforms = pdfdictionary()
- local nofglyphs = 0
- if pdfdoc then
- local scale = 10 * details.parameters.size/details.parameters.designsize
- local units = details.parameters.units
- local factor = units * bpfactor / scale
- local fixdepth = pdfshapes.fixdepth
- local useddoc = used[pdfdoc]
- local function pdftopdf(glyph,data)
- local width = (data.width or 0) * factor
- local image = useddoc[glyph]
- local reference = nil
- if not image then
- image = embedimage(copypage(pdfdoc,glyph))
- nofglyphs = nofglyphs + 1
- local name = f_glyph(nofglyphs)
- local stream = nil
- if fixdepth then
- local depth = data.depth or 0
- local height = data.height or 0
- if depth ~= 0 or height ~= 0 then
- local d = data.dropin.descriptions[data.index]
- local b = d.boundingbox
- local l = b[1]
- local r = b[3]
- local w = r - l
- local scale = w / d.width
- local x = l
- local y = - b[4] - b[2] - d.depth
- local scale = w / (image.width * bpfactor)
- stream = f_image_c_xy(scale,scale,x,y,name)
- else
- stream = f_image_c(name)
- end
- else
- stream = f_image_c(name)
- end
- useddoc[glyph] = image
- image.embedded_name = name
- image.embedded_stream = stream
- image.embedded_reference = pdfreference(image.objnum)
- end
- xforms[image.embedded_name] = image.embedded_reference
- return f_image_w(width,image.embedded_stream), width
- end
- local function closepdf()
- -- closepdf(pdfdoc)
- end
- local function getresources()
- return pdfdictionary { XObject = xforms }
- end
- return pdfshapes, 1/units, pdftopdf, closepdf, getresources
- end
- end
-
- -- mps inclusion
-
- local decompress = gzip.decompress
- local metapost = metapost
- local simplemprun = metapost.simple
- local setparameterset = metapost.setparameterset
-
- function methods.mps(filename,details)
- local properties = details.properties
- local parameters = details.parameters
- local mpshapes = properties.indexdata[1] -- indexdata will change
- if mpshapes then
- local scale = 10 * parameters.size/parameters.designsize
- local units = mpshapes.units or parameters.units
- local factor = units * bpfactor / scale
- local fixdepth = mpshapes.fixdepth
- local usecolor = mpshapes.usecolor
- local specification = mpshapes.specification
- local shapedefinitions = mpshapes.shapes
- local instance = mpshapes.instance
- --
- simplemprun(instance,"begingroup;",true,true)
- setparameterset("mpsfont",specification)
- if shapedefinitions then
- local preamble = shapedefinitions.parameters.preamble
- if preamble then
- simplemprun(instance,preamble,true,true)
- end
- end
- --
- local function mpstopdf(mp,data)
- local width = data.width
- if decompress then
- mp = decompress(mp)
- end
- local pdf = simplemprun(instance,mp,true) -- can be sped up, minifun
- local width = width * factor
- if usecolor then
- return f_stream_c(width,pdf), width
- elseif fixdepth then
- local depth = data.depth or 0
- local height = data.height or 0
- if depth ~= 0 or height ~= 0 then
- return f_stream_d(width,(-height-depth)*factor,pdf), width
- end
- end
- return f_stream(width,pdf), width
- end
- --
- local function resetmps()
- setparameterset("mpsfont")
- simplemprun(instance,"endgroup;",true,true)
- end
- --
- local function getresources()
- return lpdf.collectedresources {
- serialize = false,
- }
- end
- --
- return mpshapes, 1/units, mpstopdf, resetmps, getresources
- end
- end
-
- -- png inclusion
-
- -- With d1 the image mask is used when given and obeys color. So it is needed for pure bw
- -- bitmap fonts, so here we really need d0.
- --
- -- Acrobat X pro only seems to see the image mask but other viewers are doing it ok. Acrobat
- -- reader crashes. We really need to add a notdef!
-
- local files = utilities.files
- local openfile = files.open
- local closefile = files.close
- local setposition = files.setposition
- local readstring = files.readstring
-
- function methods.png(filename,details)
- local properties = details.properties
- local pngshapes = properties.indexdata[1]
- if pngshapes then
- local parameters = details.parameters
- local png = properties.png
- local hash = png.hash
- local xforms = pdfdictionary()
- local nofglyphs = 0
- local scale = 10 * parameters.size/parameters.designsize
- local factor = bpfactor / scale
- -- local units = parameters.units -- / 1000
- local units = 1000
- local filehandle = openfile(details.filename,true)
- local function pngtopdf(glyph,data)
- -- local info = graphics.identifiers.png(glyph.data,"string")
- local offset = glyph.o
- local size = glyph.s
- local pdfdata = nil
- if offset and size then
- setposition(filehandle,offset)
- local blob = readstring(filehandle,size)
- local info = graphics.identifiers.png(blob,"string")
- info.enforcecmyk = pngshapes.enforcecmyk
- local image = lpdf.injectors.png(info,"string")
- local width = (data.width or 0) * factor
- if image then
- embedimage(image)
- nofglyphs = nofglyphs + 1
- local xoffset = (glyph.x or 0) / units
- local yoffset = (glyph.y or 0) / units
- local name = f_glyph(nofglyphs)
- xforms[name] = pdfreference(image.objnum)
- pdfdata = f_image_xy(width,xoffset,yoffset,name)
- end
- end
- return pdfdata or f_stream(width), width
- end
- local function closepng()
- if filehandle then
- closefile(filehandle)
- end
- pngshapes = nil
- end
- local function getresources()
- return pdfdictionary { XObject = xforms }
- end
- return pngshapes, 1, pngtopdf, closepng, getresources
- end
- end
-
- function methods.color(filename,details)
- local colrshapes = details.properties.indexdata[1]
- local colrvalues = details.properties.indexdata[2]
- local usedfonts = { }
- local function colrtopdf(description,data)
- -- descriptions by index
- local colorlist = description.colors
- if colorlist then
- local dropdata = data.dropin
- local dropid = dropdata.properties.id
- local dropunits = dropdata.parameters.units -- shared
- local descriptions = dropdata.descriptions
- usedfonts[dropid] = dropid
- local w = description.width or 0
- local s = #colorlist
- local l = nil
- local t = { f_width(w) }
- local n = 1
- local d = colrvalues[#colrvalues]
- for i=1,s do
- local entry = colorlist[i]
- local v = colrvalues[entry.class] or d
- if v and l ~= v then
- n = n + 1 ; t[n] = v
- l = v
- end
- local e = descriptions[entry.slot]
- if e then
- n = n + 1 ; t[n] = f_char(dropid,e.index)
- end
- end
- return concat(t," "), w / dropunits
- end
- end
- local function getresources()
- return lpdf.collectedresources {
- serialize = false,
- fonts = usedfonts,
- fontprefix = "V",
- }
- end
- return colrshapes, 1, colrtopdf, false, getresources
- end
-
- mainwriters["type3"] = function(details)
- local properties = details.properties
- local basefontname = details.basefontname or properties.name
- local askedmethod = properties.method or "pk"
- local method = methods[askedmethod] or methods.pk
- if not method then
- return
- end
- local glyphs, scalefactor, glyphtopdf, reset, getresources = method(basedfontname,details)
- if not glyphs then
- return
- end
- local parameters = details.parameters
- local object = details.objectnumber
- local factor = parameters.factor -- normally 1
- local fontmatrix = pdfarray { scalefactor, 0, 0, scalefactor, 0, 0 }
- local indices,
- minindex,
- maxindex = collectindices(details.fontdata.characters,details.indices)
- local widths = pdfarray()
- local differences = pdfarray()
- local charprocs = pdfdictionary()
- local basefont = pdfconstant(basefontname)
- local d = 0
- local w = 0
- local forcenotdef = minindex > 0
- local lastindex = -0xFF
- if forcenotdef then
- widths[0] = 0
- minindex = 0
- lastindex = 0
- d = 2
- if not c_notdef then
- w_notdef = 0
- c_notdef = pdfconstant(".notdef")
- r_notdef = pdfreference(pdfflushstreamobject("0 0 d0"))
- end
- differences[1] = w_notdef
- differences[2] = c_notdef
- charprocs[".notdef"] = r_notdef
- end
-
- for i=1,maxindex-minindex+1 do
- widths[i] = 0
- end
-
- for index, data in sortedhash(indices) do
- local name = f_index(index)
- local glyph = glyphs[index]
- if glyph then
- local stream, width = glyphtopdf(glyph,data)
- if stream then
- if index - 1 ~= lastindex then
- d = d + 1 differences[d] = index
- end
- lastindex = index
- d = d + 1 differences[d] = pdfconstant(name)
- charprocs[name] = pdfreference(pdfflushstreamobject(stream))
- widths[index-minindex+1] = width
- end
- else
- report_fonts("missing glyph %i in type3 font %a",index,basefontname)
- end
- end
- if not fontbbox then
- -- The specification permits zero values and these are actually also more
- -- robust as then there are no assumptions and no accuracy is needed.
- fontbbox = pdfarray { 0, 0, 0, 0 }
- end
- local encoding = pdfdictionary {
- Type = pdfconstant("Encoding"),
- Differences = differences,
- }
- local tounicode = tounicodedictionary(details,indices,maxindex,basefontname,false)
- local resources = getresources and getresources()
- if not resources or not next(resources) then
- -- resources = lpdf.procset(true)
- resources = nil
- end
- local descriptor = pdfdictionary {
- -- most is optional in type3
- Type = pdfconstant("FontDescriptor"),
- FontName = basefont,
- Flags = 4,
- ItalicAngle = 0,
- }
- local parent = pdfdictionary {
- Type = pdfconstant("Font"),
- Subtype = pdfconstant("Type3"),
- Name = basefont,
- FontBBox = fontbbox,
- FontMatrix = fontmatrix,
- CharProcs = pdfreference(pdfflushobject(charprocs)),
- Encoding = pdfreference(pdfflushobject(encoding)),
- FirstChar = minindex,
- LastChar = maxindex,
- Widths = pdfreference(pdfflushobject(widths)),
- FontDescriptor = pdfreference(pdfflushobject(descriptor)),
- Resources = resources,
- ToUnicode = tounicode and pdfreference(pdfflushstreamobject(tounicode)),
- }
- pdfflushobject(object,parent)
- if reset then
- reset()
- end
- end
-
- end
-
-end
-
--- writingmode
-
-local usedfonts = fonts.hashes.identifiers -- for now
-local noffonts = 0
-
--- The main injector.
-
--- here we need to test for sharing otherwise we reserve too many
--- objects
-
-local getstreamhash = fonts.handlers.otf.getstreamhash
-local loadstreamdata = fonts.handlers.otf.loadstreamdata
-
--- we can actually now number upwards (so not use fontid in /F)
-
-local objects = setmetatableindex(function(t,k)
- local v
- if type(k) == "number" then
- local h = getstreamhash(k)
- v = rawget(t,h)
- if not v then
- v = pdfreserveobject()
- t[h] = v
- end
- if trace_fonts then
- report_fonts("font id %i bound to hash %s and object %i",k,h,v)
- end
- else
- -- no problem as it can be svg only
- -- report_fonts("fatal error, hash %s asked but not used",k,h,v)
- v = pdfreserveobject()
- t[k] = v
- end
- return v
-end)
-
-local n = 0
-
-local names = setmetatableindex(function(t,k)
- local v
- if type(k) == "number" then
- local h = getstreamhash(k)
- v = rawget(t,h)
- if not v then
- n = n + 1
- v = n
- t[h] = v
- end
- if trace_fonts then
- report_fonts("font id %i bound to hash %s and name %i",k,h,n)
- end
- end
- t[k] = v
- return v
-end)
-
-function lpdf.flushfonts()
-
- local mainfonts = { }
-
- statistics.starttiming(objects)
-
- for fontid, used in sortedhash(lpdf.usedcharacters) do
-
- -- for a bitmap we need a different hash unless we stick to a fixed high
- -- resolution which makes much sense
-
- local hash = getstreamhash(fontid)
- if hash then
- local parent = mainfonts[hash]
- if not parent then
- local fontdata = usedfonts[fontid]
- local rawdata = fontdata.shared and fontdata.shared.rawdata
- local resources = fontdata.resources
- local properties = fontdata.properties -- writingmode and type3
- local parameters = fontdata.parameters -- used in type3
- if not rawdata then
- -- we have a virtual font that loaded directly ... at some point i will
- -- sort this out (in readanddefine we need to do a bit more) .. the problem
- -- is that we have a hybrid font then
- for xfontid, xfontdata in next, fonts.hashes.identifiers do
- if fontid ~= xfontid then
- local xhash = getstreamhash(xfontid)
- if hash == xhash then
- rawdata = xfontdata.shared and xfontdata.shared.rawdata
- if rawdata then
- resources = xfontdata.resources
- properties = xfontdata.properties
- parameters = xfontdata.parameters
- break
- end
- end
- end
- end
- end
- if rawdata then
- parent = {
- hash = hash,
- fontdata = fontdata,
- filename = resources.filename or properties.filename or "unset",
- indices = { },
- rawdata = rawdata,
- properties = properties, -- we assume consistency
- parameters = parameters, -- we assume consistency
- streams = { },
- objectnumber = objects[hash],
- basefontname = subsetname(properties.psname or properties.name or "unset"),
- name = names[hash],
- }
- mainfonts[hash] = parent
- noffonts = noffonts + 1
- end
- end
- if parent then
- local indices = parent.indices
- for k in next, used do
- indices[k] = true
- end
- end
- end
- end
-
- -- this is no not yet ok for tfm / type 1 .. we need to move the nested blobs ourside the loop
-
- for hash, details in sortedhash(mainfonts) do
- local filename = details.filename
- if next(details.indices) then
- if trace_fonts then
- report_fonts("embedding %a hashed as %a",filename,hash)
- end
- local properties = details.properties
- local bitmap = properties.usedbitmap
- local method = properties.method -- will be pk | pdf | svg | ...
- if bitmap or method then
- local format = "type3"
- local writer = mainwriters[format]
- if writer then
- if trace_fonts then
- report_fonts("using main writer %a",format)
- end
- writer(details)
- end
- else
- local format = properties.format
- local writer = mainwriters[format]
- if not writer then
- -- This will move to the tpk module where we will also deal
- -- with bitmaps then.
- local encoding, pfbfile, encfile = getmapentry(filename)
- if trace_fonts then
- report_fonts("file %a resolved to encoding %a and file %a",filename,encoding,pfbfile)
- end
- if encoding and pfbfile then
- filename = pfbfile
- format = "type1"
- --
- -- another (temp) hack
- local size = details.fontdata.parameters.size
- local factor = details.fontdata.parameters.factor
- local descriptions = { }
- local characters = details.fontdata.characters
- --
- local names, _, _, metadata = fonts.constructors.handlers.pfb.loadvector(pfbfile)
- local reverse = table.swapped(names)
- local vector = encoding.vector
- local indices = details.indices
- local remapped = { }
- local factor = bpfactor * size / 65536
- for k, v in next, indices do
- local name = vector[k]
- local index = reverse[name] or 0
- local width = factor * (characters[k].width or 0)
- descriptions[k] = {
- width = width,
- index = index,
- name = name,
- }
- remapped[index] = true
- end
- details.indices = remapped
- --
- details.rawdata.descriptions = descriptions
- details.filename = filename
- details.rawdata.metadata = { }
- --
- properties.filename = filename
- properties.format = format
- writer = mainwriters[format]
- end
- end
- if not writer then
- local pfbfile = file.replacesuffix(filename,"pfb")
- if encoding and pfbfile then
- filename = pfbfile
- format = "type1"
- --
- -- another (temp) hack
- local size = details.fontdata.parameters.size
- local factor = details.fontdata.parameters.factor
- local descriptions = { }
- local characters = details.fontdata.characters
- --
- local names, _, _, metadata = fonts.constructors.handlers.pfb.loadvector(pfbfile)
- local reverse = table.swapped(names)
- local vector = encoding.vector
- local indices = details.indices
- local remapped = { }
- local factor = bpfactor * size / 65536
- for k, v in next, indices do
- local name = vector[k]
- local index = reverse[name] or 0
- local width = factor * (characters[k].width or 0)
- descriptions[k] = {
- width = width,
- index = index,
- name = name,
- }
- remapped[index] = true
- end
- details.indices = remapped
- --
- details.rawdata.descriptions = descriptions
- details.filename = filename
- details.rawdata.metadata = { }
- --
- properties.filename = filename
- properties.format = format
- writer = mainwriters[format]
- end
- end
- if writer then
- if trace_fonts then
- report_fonts("using main writer %a",format)
- end
- -- better move this test to the writers .. cleaner
- local streams = loadstreamdata(details.fontdata)
- if streams and streams.fontheader and streams.names then
- details.streams = streams
- writer(details)
- details.streams = { }
- elseif trace_fonts then
- -- can be ok for e.g. emoji
- report_fonts("no streams in %a",filename)
- end
- -- free some memory
- else -- if trace_fonts then
- report_fonts("no %a writer for %a",format,filename)
- end
- end
- else -- not problem for svg ...
- -- report_fonts("no indices for %a",filename)
- end
- if trace_fonts then
- report_fonts("embedded indices: % t",table.sortedkeys(details.indices))
- end
- mainfonts[details.hash] = false -- done
- end
-
- statistics.stoptiming(objects)
-
-end
-
-statistics.register("font embedding time",function()
- if noffonts > 0 then
- return format("%s seconds, %s fonts", statistics.elapsedtime(objects),noffonts)
- end
-end)
-
--- this is temporary
-
-local done = false
-
-updaters.register("backend.update.pdf",function()
- if not done then
- function pdf.getfontobjnum (k) return objects[k] end
- function pdf.getfontname (k) return names [k] end
- function pdf.includechar () end -- maybe, when we need it
- function pdf.includefont () end -- maybe, when we need it
- function pdf.includecharlist () end -- maybe, when we need it
- function pdf.setomitcidset (v) includecidset = not toboolean(v) end
- function pdf.setomitcharset () end -- we don't need that in lmtx
- function pdf.setsuppressoptionalinfo() end -- we don't need that in lmtx
- function pdf.mapfile (n) loadmapfile(n) end
- function pdf.mapline (n) loadmapline(n) end
- -- this will change
- lpdf.registerdocumentfinalizer(lpdf.flushfonts,1,"wrapping up fonts")
- done = true
- end
-end)