Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
mets-combined.pdf
mets-tmp/
36 changes: 0 additions & 36 deletions chapter-builder.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,42 +36,6 @@
./METS-guide/mets_howtos.md
```

``` {.include shift-heading-level-by=1}
./METS-guide/howto/altRecordID.md
./METS-guide/howto/agent.md
./METS-guide/howto/md.md
./METS-guide/howto/binary_marc.md
./METS-guide/howto/premis.md
./METS-guide/howto/source_metadata.md
./METS-guide/howto/mods.md
./METS-guide/howto/mets_rights.md
./METS-guide/howto/technical_metadata.md
./METS-guide/howto/mdRef.md
./METS-guide/howto/mdWrap.md
./METS-guide/howto/file.md
./METS-guide/howto/fileSec_example_audio.md
./METS-guide/howto/fileSec_example_images.md
./METS-guide/howto/fileGrp.md
./METS-guide/howto/transformFile.md
./METS-guide/howto/stream.md
./METS-guide/howto/FLocat.md
./METS-guide/howto/FContent.md
./METS-guide/howto/xmlData.md
./METS-guide/howto/binData.md
./METS-guide/howto/mptr.md
./METS-guide/howto/fptr.md
./METS-guide/howto/idref_namespaces.md
./METS-guide/howto/area.md
./METS-guide/howto/locref.md
./METS-guide/howto/seq.md
./METS-guide/howto/par.md
./METS-guide/howto/external_idref.md
./METS-guide/howto/idrefs_within_mets.md
./METS-guide/howto/begin_end_betype.md
./METS-guide/howto/id_idrefs.md
./METS-guide/howto/div.md
```

``` {.include}
./METS-guide/external_schema_vocabulary.md
./METS-guide/mets_profiles.md
Expand Down
30 changes: 30 additions & 0 deletions dedupe-header-ids.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
-- Ensure header identifiers are unique across the merged document.
-- Keep the original identifier if available, otherwise append a numeric suffix.

local used = {}

local function next_unique_id(base)
if not used[base] then
return base
end

local n = 2
local candidate = base .. '-' .. tostring(n)
while used[candidate] do
n = n + 1
candidate = base .. '-' .. tostring(n)
end
return candidate
end

function Header(h)
local id = h.identifier
if not id or id == '' then
return h
end

local unique = next_unique_id(id)
used[unique] = true
h.identifier = unique
return h
end
140 changes: 140 additions & 0 deletions expand-howtos.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
-- Expand linked how-to pages under each H2 in the METS How-Tos chapter.

local stringify = (require 'pandoc.utils').stringify
local List = require 'pandoc.List'

local function slugify(text)
local s = text:lower()
s = s:gsub('[^%w%s-]', '')
s = s:gsub('%s+', '-')
s = s:gsub('%-+', '-')
s = s:gsub('^%-', ''):gsub('%-$', '')
return s
end

local function build_h2_index(index_items)
local list_items = List:new()
for _, item in ipairs(index_items) do
local link = pandoc.Link(item.title, '#' .. item.id)
list_items:insert({ pandoc.Plain({ link }) })
end

return List:new({
pandoc.Para({ pandoc.Strong({ pandoc.Str('Index') }) }),
pandoc.BulletList(list_items)
})
end

local function shift_headings(blocks, shift_by)
local filter = {
Header = function(h)
h.level = h.level + shift_by
return h
end
}
return pandoc.walk_block(pandoc.Div(blocks), filter).content
end

local function collect_links_from_bullet_list(bullet_list)
local links = List:new()

for _, item in ipairs(bullet_list.content) do
for _, blk in ipairs(item) do
if blk.t == 'Plain' or blk.t == 'Para' then
for _, inline in ipairs(blk.content) do
if inline.t == 'Link' and inline.target and inline.target:match('%.md$') then
links:insert(inline.target)
end
end
end
end
end

return links
end

local function read_markdown_file(path)
local fh = io.open(path)
if not fh then
io.stderr:write('Cannot open howto file ' .. path .. '\n')
return {}
end

local src = fh:read('*a')
fh:close()

local parsed = pandoc.read(src, 'markdown', PANDOC_READER_OPTIONS)
return parsed.blocks
end

function Pandoc(doc)
local blocks = doc.blocks
local out = List:new()

local howto_h2_index = List:new()
local collecting = false
for _, b in ipairs(blocks) do
if b.t == 'Header' and b.level == 1 then
collecting = (stringify(b.content) == 'METS How-Tos')
elseif collecting and b.t == 'Header' and b.level == 2 then
local title = stringify(b.content)
local id = b.identifier
if not id or id == '' then
id = slugify(title)
end
howto_h2_index:insert({ title = title, id = id })
elseif collecting and b.t == 'Header' and b.level == 1 then
collecting = false
end
end

local in_howtos = false
local index_inserted = false
local i = 1

while i <= #blocks do
local b = blocks[i]

if b.t == 'Header' and b.level == 1 then
in_howtos = (stringify(b.content) == 'METS How-Tos')
out:insert(b)
i = i + 1
elseif in_howtos and b.t == 'Header' and b.level == 2 then
if not index_inserted and #howto_h2_index > 0 then
out:extend(build_h2_index(howto_h2_index))
index_inserted = true
end

out:insert(b)

local j = i + 1
local section_links = List:new()

while j <= #blocks do
local nxt = blocks[j]
if nxt.t == 'Header' and nxt.level <= 2 then
break
end

out:insert(nxt)
if nxt.t == 'BulletList' then
section_links:extend(collect_links_from_bullet_list(nxt))
end
j = j + 1
end

for _, link_target in ipairs(section_links) do
local included = read_markdown_file(link_target)
out:extend(shift_headings(included, 2))
end

i = j
else
out:insert(b)
i = i + 1
end
end

doc.blocks = out
return doc
end
20 changes: 13 additions & 7 deletions fix-links.lua
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
--- Created with using Google Gemini AI

function Link(el)
if el.target:match("%.md$") or el.target:match("%.md#") then
local base = el.target:gsub("%.md", "")
if not base:match("^#") then
el.target = "#" .. base:gsub(".*/", "")
else
el.target = base
end
local path, fragment = el.target:match("^([^#]+)%.md#(.+)$")
if path and fragment then
-- Keep explicit section fragments from cross-file links.
el.target = "#" .. fragment
return el
end

local only_path = el.target:match("^([^#]+)%.md$")
if only_path then
-- Resolve file links to the first-header anchor injected by add-anchor.lua.
el.target = "#" .. only_path:gsub(".*/", "")
return el
end

return el
end

9 changes: 5 additions & 4 deletions md2pdf.sh
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
#!/bin/bash
SOFFICE_PATH='/Applications/LibreOffice.app/Contents/MacOS/soffice'

mkdir ./mets-tmp
mkdir -p ./mets-tmp
find ./METS-guide -iname "*.md" -type f -exec sh -c 'pandoc --lua-filter=add-anchor.lua -f gfm+yaml_metadata_block -t markdown "${0}" -o "./mets-tmp/${0%}"' {} \;
pandoc --lua-filter=include-files.lua --lua-filter=fix-links.lua -f markdown -t markdown chapter-builder.md > ./mets-tmp/mets-combined.md
# Build from the preprocessed copies so inserted file anchors are present.
sed 's#\./METS-guide/#./mets-tmp/./METS-guide/#g' chapter-builder.md | \
pandoc --lua-filter=include-files.lua --lua-filter=expand-howtos.lua --lua-filter=dedupe-header-ids.lua --lua-filter=fix-links.lua -f markdown -t markdown > ./mets-tmp/mets-combined.md
pandoc --number-sections --reference-doc=template.docx -f markdown -t docx ./mets-tmp/mets-combined.md > ./mets-tmp/mets-combined.docx
$SOFFICE_PATH --headless --convert-to pdf ./mets-tmp/mets-combined.docx
rm -rf ./mets-tmp

#rm -rf ./mets-tmp