I wrote this piece of code to generate a pdf without external modules like (LUApdf).
I have a problem because in my table I have three elements, yet at the time of creating my pdf when I open it I have only the first lines of my table that was encoded in the PDF.
If you have a solution or an idea,
Thanks
local patch = {
{FID = 17, IDType = "nothing", CID = "nothing", Name = "Mac Aura PXL 1", Mode = "Standard", Uaddrs = 1.301},
{FID = 18, IDType = "something", CID = "controller", Name = "Spot Moving Head", Mode = "Advanced", Uaddrs = 2.101},
{FID = 19, IDType = "example", CID = "another", Name = "Wash Light", Mode = "Standard", Uaddrs = 3.401}
}
local function escape_pdf_string(str)
return str:gsub("\\", "\\\\"):gsub("%(", "\\("):gsub("%)", "\\)")
end
local file = io.open("patch_corrected.pdf", "wb")
file:write("%PDF-1.4\n")
file:write("1 0 obj\n<< /Type /Catalog /Pages 2 0 R >>\nendobj\n")
file:write("2 0 obj\n<< /Type /Pages /Count 1 /Kids [3 0 R] >>\nendobj\n")
file:write("3 0 obj\n<< /Type /Page /Parent 2 0 R /MediaBox [0 0 595 842] /Contents 4 0 R /Resources << /Font << /F1 5 0 R >> >> >>\nendobj\n")
local content_stream = "BT\n/F1 12 Tf\n"
local y_position = 800
for index, element in ipairs(patch) do
local line = string.format("FID: %d, IDType: %s, CID: %s, Name: %s, Mode: %s, Uaddrs: %.3f",
element.FID, element.IDType, element.CID, element.Name, element.Mode, element.Uaddrs)
line = escape_pdf_string(line)
content_stream = content_stream .. string.format("50 %d Td\n(%s) Tj\n", y_position, line)
y_position = y_position - 40
content_stream = content_stream .. "ET\n"
print("Contenu complet de content_stream :\n" .. content_stream)
file:write(string.format("4 0 obj\n<< /Length %d >>\nstream\n%s\nendstream\nendobj\n", #content_stream, content_stream))
file:write("5 0 obj\n<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>\nendobj\n")
file:write("xref\n")
file:write("0 6\n")
file:write("0000000000 65535 f \n")
file:write("0000000009 00000 n \n")
file:write("0000000056 00000 n \n")
file:write("0000000104 00000 n \n")
file:write(string.format("%010d 00000 n \n", 247))
file:write(string.format("%010d 00000 n \n", 317))
file:write("trailer\n")
file:write("<< /Root 1 0 R /Size 6 >>\n")
file:write("startxref\n394\n")
file:write("%%EOF\n")
file:close()
print("Le fichier PDF corrigé a été généré : patch_corrected.pdf")
In general your approach is good but the execution method for writing PDF as text in a programmable stream, needs to hold as few variables as is possible.
Currently your limited to about one lines worth of text.
All those variables need to tally in the trailer. Thus I suggest you adapt slightly to use this basic structure.
%PDF-1.4
1 0 obj <</Type/Catalog/Pages 2 0 R>> endobj
2 0 obj <</Type/Pages/Count 1/Kids [3 0 R]>> endobj
3 0 obj <</Type/Page/Parent 2 0 R/MediaBox [0 0 595 842]/Resources<</Font<</F1 4 0 R>>>>/Contents 5 0 R>> endobj
4 0 obj <</Type/Font/Subtype/Type1/BaseFont/Helvetica>> endobj
5 0 obj <</Length 6 0 R>>
stream
BT /F1 20 Tf 100 700 Td (Something or nothing line 1) Tj ET
...line 2...
...line 3...
etc. ... etc.
endstream
endobj
6 0 obj ..$Length.. endobj
xref
0 7
0000000000 65535 f
0000000009 00000 n
0000000054 00000 n
0000000106 00000 n
0000000219 00000 n
0000000282 00000 n
0.$length+ 00000 n
trailer
<</Root 1 0 R/Size 7>>
startxref
.$Above+20.
%%EOF
So to add lines it is simpler, beware this is NOT correct syntax just proves the method works!
The difference is we add the stream length after the data stream and only alter the fewest variable entries.
5 0 obj <</Length 6 0 R>>
stream
q 0 g
BT /F1 12 Tf 1 0 0 1 50 700 Tm (FID: 17, IDType: nothing, CID: nothing, Name: Mac Aura PXL 1, Mode: Standard, Uaddrs: 1.301) Tj ET
BT 1 0 0 1 50 660 Tm (FID: 18, IDType: something, CID: controller, Name: Spot Moving Head, Mode: Advanced, Uaddrs: 2.101) Tj ET
BT 1 0 0 1 50 620 Tm (FID: 19, IDType: example, CID: another, Name: Wash Light, Mode: Standard, Uaddrs: 3.401) Tj ET
BT 1 0 0 1 50 580 Tm (FID: 20, IDType: , CID: , Name: , Mode: , Uaddrs: 0.000) Tj ET
Q
endstream
endobj
6 0 obj 0469 endobj
xref
0 7
0000000000 65536 f
0000000009 00000 n
0000000054 00000 n
0000000105 00000 n
0000000217 00000 n
0000000280 00000 n
0000000800 00000 n
trailer
<</Size 7/Root 1 0 R>>
startxref
820
%%EOF
Adobe Acrobat Reader will accept some errors and correct others, however a stream LENGTH must be close to correct.
When adding lines of text as variable page contents you cannot count until all done. So the stream length shown here as 469
needs to be added after the stream (or carried back to stream header).
Any variable length needs to adjust for itself in the trailer. The decimal distance addresses can be static up to the last which impacts the startXREF (A known fixed amount later)
The maths is then simplified to if 6 0 obj 0469 endobj
value is 4 digits 0469
starting at 0800
then startxref is 20 more @ 820
There is usually no problem using leading zer0's to ensure fixed character string lengths.
Thus heading objects 0-5 up to stream have a static value of 313 (800-469+ approx. 18 for EOS&EOO)
Thus stream = 0+ and the later index value is (313 + 18) + stream length
and startxref
a fixed +20
after that.