77 lines
3.1 KiB
Lua
77 lines
3.1 KiB
Lua
-- Copyright (c) 2026 Marc Wäckerlin. SPDX-License-Identifier: MIT
|
|||
|
|
-- A picture that stands alone in its paragraph gets its caption under it.
|
||
|
|
--
|
||
|
|
-- `` carries a text between the brackets, and
|
||
|
|
-- every reader who writes it means it as the caption. GFM has no such rule: the
|
||
|
|
-- text becomes the alternative text of the picture, which reaches a screen
|
||
|
|
-- reader and nobody else, and the PDF shows a picture with nothing under it.
|
||
|
|
--
|
||
|
|
-- pandoc has the rule under the name implicit_figures, and its GFM reader does
|
||
|
|
-- not carry it, so the paragraph is turned into a figure here. What the figure
|
||
|
|
-- then looks like — where it stands, how the caption is set — comes from the
|
||
|
|
-- package like everything else.
|
||
|
|
|
||
|
|
-- A picture that stands directly under a heading does not float away from it.
|
||
|
|
--
|
||
|
|
-- A floating picture leaves the page it was written on, and where the heading
|
||
|
|
-- announced nothing but that picture, the heading stays behind alone at the
|
||
|
|
-- foot of the page. Measured over one report: a page ended with its heading,
|
||
|
|
-- 430 points of it still empty, and its picture began on the next. The
|
||
|
|
-- reservation a heading takes cannot answer this — the room was there, and what
|
||
|
|
-- should have filled it had left the page.
|
||
|
|
--
|
||
|
|
-- So this one picture is placed where it stands. It then either fits under its
|
||
|
|
-- heading, or the page breaks BEFORE the heading and the two travel together,
|
||
|
|
-- which is what a reader expects of a heading.
|
||
|
|
local function nailed(figure)
|
||
|
|
return {
|
||
|
|
pandoc.RawBlock('latex', '\\begingroup\\floatplacement{figure}{H}'),
|
||
|
|
figure,
|
||
|
|
pandoc.RawBlock('latex', '\\endgroup'),
|
||
|
|
}
|
||
|
|
end
|
||
|
|
|
||
|
|
function Blocks(blocks)
|
||
|
|
if not FORMAT:match('latex') then return nil end
|
||
|
|
local kept, previous = pandoc.Blocks{}, nil
|
||
|
|
for _, block in ipairs(blocks) do
|
||
|
|
if block.t == 'Figure' and previous and previous.t == 'Header' then
|
||
|
|
for _, piece in ipairs(nailed(block)) do
|
||
|
|
kept:insert(piece)
|
||
|
|
end
|
||
|
|
else
|
||
|
|
kept:insert(block)
|
||
|
|
end
|
||
|
|
previous = block
|
||
|
|
end
|
||
|
|
return kept
|
||
|
|
end
|
||
|
|
|
||
|
|
-- A picture of pixels says so, because only this side knows the file name.
|
||
|
|
--
|
||
|
|
-- How large a picture may be drawn is the package's rule, and it is not the
|
||
|
|
-- same rule for a drawing and for a photograph: a drawing is drawn again at
|
||
|
|
-- every size, a raster of pixels gets coarse. What the package sees is the
|
||
|
|
-- finished box, which carries no file name any more, so the mark is set here
|
||
|
|
-- and read by the picture that follows it.
|
||
|
|
local RASTER = {
|
||
|
|
png = true, jpg = true, jpeg = true, bmp = true,
|
||
|
|
gif = true, tif = true, tiff = true,
|
||
|
|
}
|
||
|
|
|
||
|
|
function Image(image)
|
||
|
|
if not FORMAT:match('latex') then return nil end
|
||
|
|
local extension = image.src:match('%.([%a%d]+)$')
|
||
|
|
if extension and RASTER[extension:lower()] then
|
||
|
|
return { pandoc.RawInline('latex', '\\businessbitmap'), image }
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function Para(para)
|
||
|
|
if #para.content ~= 1 then return nil end
|
||
|
|
local image = para.content[1]
|
||
|
|
if image.t ~= 'Image' or #image.caption == 0 then return nil end
|
||
|
|
return pandoc.Figure(
|
||
|
|
pandoc.Blocks{ pandoc.Plain{ image } },
|
||
|
|
{ long = pandoc.Blocks{ pandoc.Plain(image.caption) } })
|
||
|
|
end
|