admin管理员组

文章数量:1186088

I'm developing a Lua script to generate a multi-page PDF file containing tabular data. My script generates the PDF correctly and the logs show that all the pages are created correctly. However, when I open the PDF file, one of the pages (page 2) is not displayed. The content goes directly from page 1 to page 3.

What I tried: Checked the script logs: everything indicates that page 2 has been generated and included. Validated the structure of PDF objects in the file: all objects (pages, references, xref) seem to be in place. Checked cross-reference structure (xref) and /Kids entries for page tree.

local function creerPDF(fichierPDF, data)

local fichier = assert(io.open(fichierPDF, "w"))
fichier:write("%PDF-1.4\n")

local pages = {}
local kids = {}
local positionY = 700
local lignesParPage = 30


local function nouvellePage()
    local pageObj = #pages * 2 + 3
    local contentObj = pageObj + 1
    table.insert(kids, pageObj .. " 0 R")
    table.insert(pages, {page = pageObj, content = contentObj, stream = ""})
    positionY = 700
end


local function ajouterEnTete(page)
    local header = "q\n"
    header = header .. "BT /F1 12 Tf 20 820 Td (Programmeur : LumiArt-Studio) Tj ET\n"
    header = header .. "BT /F1 12 Tf 20 800 Td (Date : " .. os.date("%d/%m/%Y %H:%M") .. ") Tj ET\n"
    header = header .. "BT /F1 12 Tf 20 780 Td (Machine : TestMachine) Tj ET\n"
    header = header .. "BT /F1 12 Tf 50 720 Td (ID) Tj ET\n"
    header = header .. "BT /F1 12 Tf 150 720 Td (Type) Tj ET\n"
    header = header .. "BT /F1 12 Tf 250 720 Td (Name) Tj ET\n"
    header = header .. "BT /F1 12 Tf 350 720 Td (FixtureType) Tj ET\n"
    header = header .. "BT /F1 12 Tf 450 720 Td (U.Addrs) Tj ET\n"
    header = header .. "Q\n"
    page.stream = page.stream .. header
end


local function ajouterDonnee(page, ligne)
    local columnPositions = {50, 150, 250, 350, 450}
    local values = {ligne.id, ligne.idtype, ligne.name, ligne.fixturetype, ligne.uaddrs}
    for i, value in ipairs(values) do
        page.stream = page.stream .. string.format("BT /F1 10 Tf %d %d Td (%s) Tj ET\n", columnPositions[i], positionY, tostring(value))
    end
    positionY = positionY - 20
    if positionY < 100 then
        nouvellePage()
        ajouterEnTete(pages[#pages])
    end
end


local function ajouterPiedDePage(page, index, total)
    local footer = string.format("BT /F1 10 Tf 500 20 Td (Page %d sur %d) Tj ET\n", index, total)
    page.stream = page.stream .. footer
end


nouvellePage()
ajouterEnTete(pages[#pages])
for _, ligne in ipairs(data) do
    ajouterDonnee(pages[#pages], ligne)
end


local totalPages = #pages
for i, page in ipairs(pages) do
    ajouterPiedDePage(page, i, totalPages)
end


for _, page in ipairs(pages) do
    fichier:write(string.format("%d 0 obj\n<< /Type /Page /Parent 2 0 R /MediaBox [0 0 595 842] /Resources << /Font << /F1 5 0 R >> >> /Contents %d 0 R >>\nendobj\n", page.page, page.content))
    fichier:write(string.format("%d 0 obj\n<< /Length %d >>\nstream\n%s\nendstream\nendobj\n", page.content, #page.stream, page.stream))
end


fichier:write("2 0 obj\n<< /Type /Pages /Kids [")
for _, kid in ipairs(kids) do
    fichier:write(kid .. " ")
end
fichier:write(string.format("] /Count %d >>\nendobj\n", #kids))


fichier:write("1 0 obj\n<< /Type /Catalog /Pages 2 0 R >>\nendobj\n")


fichier:write("5 0 obj\n<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>\nendobj\n")


local xrefStart = fichier:seek()
fichier:write("xref\n0 " .. (#kids * 2 + 6) .. "\n0000000000 65535 f \n")
local offset = 9
for i = 1, (#kids * 2 + 4) do
    fichier:write(string.format("%010d 00000 n \n", offset))
    offset = fichier:seek()
end


fichier:write(string.format("trailer\n<< /Size %d /Root 1 0 R >>\nstartxref\n%d\n%%EOF\n", (#kids * 2 + 6), xrefStart))

fichier:close()

end

local data = {} for i = 1, 200 do table.insert(data, {id = i, idtype = "Fixture", name = "Fixture " .. i, fixturetype = "Type " .. i, uaddrs = tostring(i * 100)}) end

creerPDF("output_debug_fixed.pdf", data)

I'm developing a Lua script to generate a multi-page PDF file containing tabular data. My script generates the PDF correctly and the logs show that all the pages are created correctly. However, when I open the PDF file, one of the pages (page 2) is not displayed. The content goes directly from page 1 to page 3.

What I tried: Checked the script logs: everything indicates that page 2 has been generated and included. Validated the structure of PDF objects in the file: all objects (pages, references, xref) seem to be in place. Checked cross-reference structure (xref) and /Kids entries for page tree.

local function creerPDF(fichierPDF, data)

local fichier = assert(io.open(fichierPDF, "w"))
fichier:write("%PDF-1.4\n")

local pages = {}
local kids = {}
local positionY = 700
local lignesParPage = 30


local function nouvellePage()
    local pageObj = #pages * 2 + 3
    local contentObj = pageObj + 1
    table.insert(kids, pageObj .. " 0 R")
    table.insert(pages, {page = pageObj, content = contentObj, stream = ""})
    positionY = 700
end


local function ajouterEnTete(page)
    local header = "q\n"
    header = header .. "BT /F1 12 Tf 20 820 Td (Programmeur : LumiArt-Studio) Tj ET\n"
    header = header .. "BT /F1 12 Tf 20 800 Td (Date : " .. os.date("%d/%m/%Y %H:%M") .. ") Tj ET\n"
    header = header .. "BT /F1 12 Tf 20 780 Td (Machine : TestMachine) Tj ET\n"
    header = header .. "BT /F1 12 Tf 50 720 Td (ID) Tj ET\n"
    header = header .. "BT /F1 12 Tf 150 720 Td (Type) Tj ET\n"
    header = header .. "BT /F1 12 Tf 250 720 Td (Name) Tj ET\n"
    header = header .. "BT /F1 12 Tf 350 720 Td (FixtureType) Tj ET\n"
    header = header .. "BT /F1 12 Tf 450 720 Td (U.Addrs) Tj ET\n"
    header = header .. "Q\n"
    page.stream = page.stream .. header
end


local function ajouterDonnee(page, ligne)
    local columnPositions = {50, 150, 250, 350, 450}
    local values = {ligne.id, ligne.idtype, ligne.name, ligne.fixturetype, ligne.uaddrs}
    for i, value in ipairs(values) do
        page.stream = page.stream .. string.format("BT /F1 10 Tf %d %d Td (%s) Tj ET\n", columnPositions[i], positionY, tostring(value))
    end
    positionY = positionY - 20
    if positionY < 100 then
        nouvellePage()
        ajouterEnTete(pages[#pages])
    end
end


local function ajouterPiedDePage(page, index, total)
    local footer = string.format("BT /F1 10 Tf 500 20 Td (Page %d sur %d) Tj ET\n", index, total)
    page.stream = page.stream .. footer
end


nouvellePage()
ajouterEnTete(pages[#pages])
for _, ligne in ipairs(data) do
    ajouterDonnee(pages[#pages], ligne)
end


local totalPages = #pages
for i, page in ipairs(pages) do
    ajouterPiedDePage(page, i, totalPages)
end


for _, page in ipairs(pages) do
    fichier:write(string.format("%d 0 obj\n<< /Type /Page /Parent 2 0 R /MediaBox [0 0 595 842] /Resources << /Font << /F1 5 0 R >> >> /Contents %d 0 R >>\nendobj\n", page.page, page.content))
    fichier:write(string.format("%d 0 obj\n<< /Length %d >>\nstream\n%s\nendstream\nendobj\n", page.content, #page.stream, page.stream))
end


fichier:write("2 0 obj\n<< /Type /Pages /Kids [")
for _, kid in ipairs(kids) do
    fichier:write(kid .. " ")
end
fichier:write(string.format("] /Count %d >>\nendobj\n", #kids))


fichier:write("1 0 obj\n<< /Type /Catalog /Pages 2 0 R >>\nendobj\n")


fichier:write("5 0 obj\n<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>\nendobj\n")


local xrefStart = fichier:seek()
fichier:write("xref\n0 " .. (#kids * 2 + 6) .. "\n0000000000 65535 f \n")
local offset = 9
for i = 1, (#kids * 2 + 4) do
    fichier:write(string.format("%010d 00000 n \n", offset))
    offset = fichier:seek()
end


fichier:write(string.format("trailer\n<< /Size %d /Root 1 0 R >>\nstartxref\n%d\n%%EOF\n", (#kids * 2 + 6), xrefStart))

fichier:close()

end

local data = {} for i = 1, 200 do table.insert(data, {id = i, idtype = "Fixture", name = "Fixture " .. i, fixturetype = "Type " .. i, uaddrs = tostring(i * 100)}) end

creerPDF("output_debug_fixed.pdf", data)

Share Improve this question asked Jan 25 at 17:42 JoshJosh 611 silver badge4 bronze badges New contributor Josh is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct. 1
  • 1 Yet again without your example it is almost impossible to guess which byte is wrong (only need one binary bit in the wrong position to corrupt a PDF). Try this one as text it has 3 pages all the same. easyupload.io/40jh21 – K J Commented Jan 25 at 21:16
Add a comment  | 

1 Answer 1

Reset to default 1

The Problem with thinking in terms of content flow is that a PDF cannot be written like one HTML page.

A PDF is defined as a number of page medias either before or after first parse and thus before any second pass needed to use those numbers.

We can decide that if there are 30 lines per page and we know we need to write 75 entries, then we will need to prepare 3 pages. It does not matter if blank or the same content or different content we MUST either know OR replace any estimate later (by total rewrite the catalogue of objects).

Let use presume 3 the same from one page header style. Then we know at the start we only need to count for page 0, 1 & 2 (ALL PDF writers internally start with Page 0 which a PDF Viewer will later display as Page 1).

%PDF-1.4
1 0 obj <</Type/Catalog/Pages 2 0 R>> endobj
2 0 obj <</Type/Pages/Count 3/Kids[4 0 R 5 0 R 6 0 R ]>> endobj
3 0 obj <</Type/Font/Subtype/Type1/BaseFont/Helvetica>> endobj
4 0 obj <</Type/Page/Parent 2 0 R/MediaBox[0 0 595 842]/Resources<</Font<</F0 3 0 R>>>>/Contents 7 0 R>> endobj
5 0 obj <</Type/Page/Parent 2 0 R/MediaBox[0 0 595 842]/Resources<</Font<</F0 3 0 R>>>>/Contents 7 0 R>> endobj
6 0 obj <</Type/Page/Parent 2 0 R/MediaBox[0 0 595 842]/Resources<</Font<</F0 3 0 R>>>>/Contents 7 0 R>> endobj
7 0 obj <</Length 8 0 R>>
stream
q 0 g
BT /F0 12 Tf 1 0 0 1 20 820 Tm (Programmeur : LumiArt-Studio) Tj ET
1 0 0 1 20 800 cm BT (Date : " .. os.date \("%d/%m/%Y %H:%M"\) .. ") Tj ET
1 0 0 1 0 -20 cm BT (Machine : Test Machine) Tj ET
1 0 0 1 30 -100 cm BT (ID :) Tj ET
1 0 0 1 100 0 cm BT (Type :) Tj ET
1 0 0 1 100 0 cm BT (Name :) Tj ET
1 0 0 1 100 0 cm BT (Fixture Type :) Tj ET
1 0 0 1 100 0 cm BT (U.Address :) Tj ET
Q
endstream
endobj
8 0 obj 0548 endobj
xref
0 9
0000000000 65536 f 
0000000009 00000 n 
0000000054 00000 n 
0000000118 00000 n 
0000000181 00000 n 
0000000293 00000 n 
0000000405 00000 n 
0000000517 00000 n 
0000000957 00000 n 
trailer
<</Size 9/Root 1 0 R>>
startxref
977
%%EOF

If you need each page to show different entries like a page number of different font text and or images. Then you need a fresh object for each page and possibly their resources if different.

If you need to write the pages last we can append a replacement line for the first estimated one. This is usually done either as an incremental addition or allowed for in build by index the "only" entry with a higher address in the Xref index.

thus a late object 2 entry could be like this

2 0 obj <</Type/Pages/Count 4/Kids[4 0 R 5 0 R 6 0 R 13 0 R]>> endobj
xref
0 15
0000000000 65536 f 
0000000009 00000 n 
0000025168 00000 n 
0000000054 00000 n 

本文标签: Problem with a missing page when generating a PDF in LuaStack Overflow