LaTeX format for LuaMetaTeX
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

106 lines
3.9 KiB

  1. local writer -- = require'luametalatex-nodewriter' -- This would introduce some cyclic dependency
  2. local pdfvariable = pdf.variable
  3. -- XForms currently have the form {width, height, depth, objnum, attributes, list, margin}
  4. local xforms = {}
  5. local utils = require'luametalatex-pdf-utils'
  6. local strip_floats = utils.strip_floats
  7. local to_bp = utils.to_bp
  8. local function shipout(pfile, xform, fontdirs, usedglyphs)
  9. local list, margin = xform.list, xform.margin
  10. if not list then return xform.objnum end -- Already shipped out
  11. local last_page = cur_page cur_page = nil
  12. local out, resources, annots = writer(pfile, list, fontdirs, usedglyphs)
  13. cur_page = last_page
  14. assert(annots == '')
  15. if pdfvariable.xformattr ~= '' or pdfvariable.xformresources ~= '' then
  16. texio.write_nl('term and log', 'WARNING (savedboxresource shipout): Ignoring unsupported PDF variables xformattr and xformresources. Specify resources and attributes for specific XForms instead.')
  17. end
  18. local bbox = strip_floats(string.format('/BBox[%f %f %f %f]', -to_bp(margin), -to_bp(list.depth+margin), to_bp(list.width+margin), to_bp(list.height+margin)))
  19. local dict = string.format('/Subtype/Form%s/Resources%s%s', bbox, resources(xform.resources), xform.attributes or '')
  20. node.flush_list(list)
  21. xform.list = nil
  22. local objnum = pfile:stream(xform.objnum, dict, out)
  23. xform.objnum = objnum
  24. return objnum
  25. end
  26. local function save(pfile, n, attr, resources, immediate, type, margin, fontdirs, usedglyphs)
  27. local index = #xforms+1
  28. local xform = {
  29. list = assert(n, 'List required for saveboxresource'),
  30. width = n.width,
  31. height = n.height,
  32. depth = n.depth,
  33. attributes = attr,
  34. resources = resources,
  35. margin = margin,
  36. -- type = type, -- TODO: Not yet used. Do we need this at all?
  37. }
  38. xforms[index] = xform
  39. if immediate then
  40. shipout(pfile, xform, fontdirs, usedglyphs)
  41. end
  42. return index
  43. end
  44. local function adjust_sizes(width, height, depth, real_width, real_height, real_depth)
  45. if not depth then
  46. if height then
  47. local scale = height/real_height
  48. depth = (real_depth*scale + .5)//1
  49. width = width or (real_width*scale + .5)//1
  50. elseif width then
  51. local scale = width/real_width
  52. depth = (real_depth*scale + .5)//1
  53. height = (real_height*scale + .5)//1
  54. else
  55. width, height, depth = real_width, real_height, real_depth
  56. end
  57. elseif height then
  58. width = width or (real_width*(height+depth)/(real_height+real_depth) + .5)//1
  59. else
  60. width = width or real_width
  61. local scale = width/real_width
  62. height = ((real_depth+real_height)*scale + .5)//1 - depth
  63. end
  64. return width, height, depth
  65. end
  66. local ruleid = node.id'rule'
  67. local ruletypes = node.subtypes'rule'
  68. local boxrule
  69. for n, name in next, ruletypes do
  70. if name == 'box' then boxrule = n break end
  71. end
  72. local function use(index, width, height, depth)
  73. local xform = xforms[index]
  74. if not xform then return nil, nil, nil, nil end
  75. width, height, depth = adjust_sizes(width, height, depth, xform.width, xform.height, xform.depth)
  76. local n = node.direct.new(ruleid, boxrule)
  77. node.direct.setdata(n, index)
  78. node.direct.setwhd(n, width, height, depth)
  79. return node.direct.tonode(n), width, height, depth
  80. end
  81. local function do_box(data, p, n, x, y)
  82. local xform = assert(xforms[data], 'Invalid XForm')
  83. local objnum = shipout(p.file, xform, p.fontdirs, p.usedglyphs)
  84. local width, height, depth = node.direct.getwhd(n)
  85. local xscale, yscale = width / xform.width, (height+depth) / (xform.height+xform.depth)
  86. p.resources.XObject['Fm' .. tostring(data)] = objnum
  87. pdf.write('page', strip_floats(string.format('q %f 0 0 %f %f %f cm /Fm%i Do Q',
  88. xscale, yscale,
  89. to_bp(x), to_bp(y-depth+yscale*xform.depth),
  90. data)), nil, nil, p)
  91. end
  92. return {
  93. save = save,
  94. use = use,
  95. ship = do_box,
  96. init_nodewriter = function(t, nodewriter) writer, t.init_nodewriter = nodewriter, nil end,
  97. }