summaryrefslogtreecommitdiff
path: root/tex/context/base/mkiv/lpdf-emb.lua
diff options
context:
space:
mode:
Diffstat (limited to 'tex/context/base/mkiv/lpdf-emb.lua')
-rw-r--r--tex/context/base/mkiv/lpdf-emb.lua397
1 files changed, 293 insertions, 104 deletions
diff --git a/tex/context/base/mkiv/lpdf-emb.lua b/tex/context/base/mkiv/lpdf-emb.lua
index dbf6fd4d3..1cdcac1ae 100644
--- a/tex/context/base/mkiv/lpdf-emb.lua
+++ b/tex/context/base/mkiv/lpdf-emb.lua
@@ -45,7 +45,9 @@ local report_fonts = logs.reporter("backend","fonts")
local trace_fonts = false
local trace_details = false
-local dimenfactors = number.dimenfactors
+local dimenfactors = number.dimenfactors
+local bpfactor = dimenfactors.bp
+local ptfactor = dimenfactors.pt
trackers.register("backend.pdf.fonts",function(v) trace_fonts = v end)
@@ -1360,7 +1362,7 @@ do
-- This is a hack, we have no basepoints.
correction = details.fontdata.parameters.size / 1000
-- And this needs checking.
- correction = correction * dimenfactors.bp / dimenfactors.pt
+ correction = correction * bpfactor / ptfactor
metadata = { }
end
@@ -1510,114 +1512,296 @@ do
mainwriters["opentype"](details)
end
- -- todo: map pdf glyphs onto companion type 3 font .. can be set of small
- -- ones. maybe only private codes with proper tounicode
+ 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 = formatters["%.6N 0 d0 /%s Do"]
+ local f_stream = formatters["%.6N 0 d0 %s"]
- local methods = { }
+ -- 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.
- function methods.pk(filename)
- local resolution = 600
- local widthfactor = resolution / 72
- local scalefactor = 72 / resolution / 10
- local pkfullname = resolvers.findpk(basedfontname,resolution)
- if not pkfullname or pkfullname == "" then
- return
+ 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 resolution = 600
+ local widthfactor = resolution / 72
+ local scalefactor = 72 / resolution / 10
+ 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)
+ if not result or result.error then
+ return
+ end
+ return result.glyphs, widthfactor / 65536, scalefactor, readers.pktopdf
end
- local readers = fonts.handlers.tfm.readers
- local result = readers.loadpk(pkfullname)
- if not result or result.error then
- return
+
+ -- pdf inclusion
+
+ 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 function pdftopdf(glyph,width,data)
+ local image = copypage(pdfdoc,glyph)
+ width = 100 * width * bpfactor
+ embedimage(image)
+ nofglyphs = nofglyphs + 1
+ local name = f_glyph(nofglyphs)
+ xforms[name] = pdfreference(image.objnum)
+ local pdf = f_image(width,name)
+ return pdf, width
+ end
+ local function closepdf()
+ -- closepdf(pdfdoc)
+ end
+ local function getresources()
+ return pdfdictionary { XObject = xforms }
+ end
+ return pdfshapes, 1, 0.001, pdftopdf, closepdf, getresources
+ end
end
- return result.glyphs, widthfactor / 65536, scalefactor, readers.pktopdf
- end
- mainwriters["type3"] = function(details)
- local properties = details.properties
- local basefontname = details.basefontname or properties.name
- local askedmethod = "pk"
- local method = methods[askedmethod]
- if not method then
- return
+ -- mps inclusion
+
+ function methods.mps(filename,details)
+ local properties = details.properties
+ local mpshapes = properties.indexdata[1]
+ local function mpstopdf(mp,width,data)
+ local pdf = metapost.simple("metafun",mp,true) -- can be sped up, minifun
+ local width = 100 * width * bpfactor
+ local stream = f_stream(width,pdf)
+ return stream, width
+ end
+ local function getresources()
+ return lpdf.collectedresources {
+ serialize = false,
+ }
+ end
+ return mpshapes, 1, 0.001, mpstopdf, nil, getresources
end
- local glyphs, widthfactor, scalefactor, glyphtopdf = method(basedfontname)
- if not glyphs then
- return
+
+ -- 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!
+
+ function methods.png(filename,details)
+ local properties = details.properties
+ local png = properties.png
+ local hash = png.hash
+ local pngshapes = properties.indexdata[1]
+ local xforms = pdfdictionary()
+ local nofglyphs = 0
+ if pngshapes then
+ local function pngtopdf(glyph,width,data)
+ local info = graphics.identifiers.png(glyph.data,"string")
+ local image = lpdf.injectors.png(info,"string")
+ local bbox = image.bbox
+ local llx = bbox[1] * bpfactor
+ local lly = bbox[2] * bpfactor
+ local urx = bbox[3] * bpfactor
+ local ury = bbox[4] * bpfactor
+ width = width * bpfactor / 10
+ embedimage(image)
+ nofglyphs = nofglyphs + 1
+ local name = f_glyph(nofglyphs)
+ xforms[name] = pdfreference(image.objnum)
+ local pdf = f_image(width,name)
+ return pdf, width
+ end
+ local function closepng()
+ pngshapes = nil
+ end
+ local function getresources()
+ return pdfdictionary { XObject = xforms }
+ end
+ return pngshapes, 1, 1, pngtopdf, closepng, getresources
+ end
end
- local parameters = details.parameters
- local object = details.objectnumber
- local factor = parameters.factor -- normally 1
- local f_name = formatters["I%05i"]
- 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 llx, lly, urx, ury = 0, 0, 0, 0
- for i=1,maxindex-minindex+1 do
- widths[i] = 0
+
+ function methods.color(filename,details)
+ local colrshapes = details.properties.indexdata[1]
+ local colrvalues = details.properties.indexdata[2]
+ local usedfonts = { }
+ local dd = details.fontdata.descriptions -- temp hack
+ local function colrtopdf(description,wd,data) -- todo: chardata instead of descriptions
+ -- descriptions by index
+ local colorlist = description.colors
+ if colorlist then
+ local dropdata = data.dropin
+ local dropid = dropdata.properties.id
+ local dropunits = dropdata.parameters.units
+ 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 = dd[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, 1, colrtopdf, false, getresources
end
- local d = 0
- local lastindex = -0xFFFF
- for index, data in sortedhash(indices) do
- local name = f_name(index)
- local glyph = glyphs[index]
- if glyph then
- local width = widthfactor * data.width
- local stream, lx, ly, ux, uy = glyphtopdf(glyph,width)
- if stream then
- if index - 1 ~= lastindex then
- d = d + 1 ; differences[d] = index
+
+ 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, widthfactor, 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 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 width = widthfactor * data.width
+ local stream, wd = glyphtopdf(glyph,width,data)
+ if stream then
+ if wd then
+ width = wd
+ end
+ 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
- lastindex = index
- d = d + 1 ; differences[d] = pdfconstant(name)
- charprocs[name] = pdfreference(pdfflushstreamobject(stream))
- widths[index-minindex+1] = width
- if lx < llx then llx = lx end
- if ux > urx then urx = ux end
- if ly < lly then lly = ly end
- if uy > ury then ury = uy 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)
+ local resources = getresources and getresources() or lpdf.procset(true)
+ 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 = pdfreference(pdfflushstreamobject(tounicode)),
+ }
+ pdfflushobject(object,parent)
+ if reset then
+ reset()
+ end
end
- local fontbbox = pdfarray { llx, lly, urx, ury }
- local encoding = pdfdictionary {
- Type = pdfconstant("Encoding"),
- Differences = differences,
- }
- local tounicode = tounicodedictionary(details,indices,maxindex,basefontname)
- 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),
- -- Metadata = fontmeta and pdfreference(pdfflushstreamobject(fontmeta)) or nil,
- }
- 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 = lpdf.procset(true),
- ToUnicode = pdfreference(pdfflushstreamobject(tounicode)),
- }
- pdfflushobject(reserved,descriptor)
- pdfflushobject(object,parent)
+
end
end
@@ -1650,7 +1834,8 @@ local objects = setmetatableindex(function(t,k)
report_fonts("font id %i bound to hash %s and object %i",k,h,v)
end
else
- report_fonts("fatal error, hash %s asked but not used",k,h,v)
+ -- 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
@@ -1752,7 +1937,8 @@ function lpdf.flushfonts()
end
local properties = details.properties
local bitmap = properties.usedbitmap
- if bitmap then
+ local method = properties.method -- will be pk | pdf | svg | ...
+ if bitmap or method then
local format = "type3"
local writer = mainwriters[format]
if writer then
@@ -1786,7 +1972,7 @@ function lpdf.flushfonts()
local vector = encoding.vector
local indices = details.indices
local remapped = { }
- local factor = dimenfactors.bp * size / 65536
+ local factor = bpfactor * size / 65536
for k, v in next, indices do
local name = vector[k]
local index = reverse[name] or 0
@@ -1826,7 +2012,7 @@ function lpdf.flushfonts()
local vector = encoding.vector
local indices = details.indices
local remapped = { }
- local factor = dimenfactors.bp * size / 65536
+ local factor = bpfactor * size / 65536
for k, v in next, indices do
local name = vector[k]
local index = reverse[name] or 0
@@ -1868,8 +2054,11 @@ function lpdf.flushfonts()
report_fonts("no %a writer for %a",format,filename)
end
end
- else
- report_fonts("no indices for %a",filename)
+ 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