Skip to content

Commit a71a570

Browse files
author
Olivier Bonnaure
committed
fix: improve pdf sample and add charts
1 parent 1650f8d commit a71a570

File tree

3 files changed

+193
-47
lines changed

3 files changed

+193
-47
lines changed

.lua/pdfgenerator.lua

Lines changed: 51 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -449,57 +449,57 @@ end
449449

450450
-- Add text to current page
451451
function PDFGenerator:addText(text, fontSize, color, alignment, width)
452-
width = width or self.page_width
453-
fontSize = fontSize or 12
454-
color = color or "000" -- default black
455-
alignment = alignment or "justify" -- default left alignment
456-
color = PDFGenerator:hexToRGB(color)
452+
width = width or self.page_width
453+
fontSize = fontSize or 12
454+
color = color or "000" -- default black
455+
alignment = alignment or "justify" -- default left alignment
456+
color = PDFGenerator:hexToRGB(color)
457457

458-
local content = self.contents[self.current_page_obj]
459-
local fontName = self.last_font.fontWeight == "bold" and "F1" or "F2"
458+
local content = self.contents[self.current_page_obj]
459+
local fontName = self.last_font.fontWeight == "bold" and "F1" or "F2"
460460

461-
if self.current_font then
462-
fontName = self.current_font .. "-" .. self.last_font.fontWeight
463-
end
461+
if self.current_font then
462+
fontName = self.current_font .. "-" .. self.last_font.fontWeight
463+
end
464464

465-
-- Calculate x position based on alignment
466-
local x_pos = self.margin_x[1] + self.current_x
467-
local text_width = self:getTextWidth(text, fontSize, self.last_font.fontWeight)
465+
-- Calculate x position based on alignment
466+
local x_pos = self.margin_x[1] + self.current_x
467+
local text_width = self:getTextWidth(text, fontSize, self.last_font.fontWeight)
468468

469-
if alignment == "center" then
470-
x_pos = x_pos + (width - text_width) / 2
471-
elseif alignment == "right" then
472-
x_pos = x_pos + (width - text_width)
473-
end
469+
if alignment == "center" then
470+
x_pos = x_pos + (width - text_width) / 2
471+
elseif alignment == "right" then
472+
x_pos = x_pos + (width - text_width)
473+
end
474474

475-
-- For justified text, we need to calculate word spacing
476-
if alignment == "justify" then
477-
local spaces = text:gsub("[^ ]", ""):len() -- Count spaces
478-
local words = select(2, text:gsub("%S+", "")) + 1 -- Count words
479-
local available_width = self.page_width - self.margin_x[1] - self.margin_x[2]
480-
local extra_space = available_width - text_width
481-
local word_spacing = extra_space / spaces
482-
if word_spacing > 30 then
483-
word_spacing = 1
484-
end
485-
content.stream = content.stream .. string.format(
486-
"BT\n/%s %s Tf\n%s %s %s rg\n%s Tw\n%s %s Td\n(%s) Tj\nET\n",
487-
fontName,
488-
numberToString(fontSize),
489-
numberToString(color[1]),
490-
numberToString(color[2]),
491-
numberToString(color[3]),
492-
numberToString(word_spacing), -- Set word spacing using Tw operator
493-
numberToString(x_pos),
494-
numberToString(self.currentYPos(self)),
495-
self:escapePdfText(text)
496-
)
497-
return self
498-
end
475+
-- For justified text, we need to calculate word spacing
476+
if alignment == "justify" then
477+
local spaces = text:gsub("[^ ]", ""):len() -- Count spaces
478+
local words = select(2, text:gsub("%S+", "")) + 1 -- Count words
479+
local available_width = self.page_width - self.margin_x[1] - self.margin_x[2]
480+
local extra_space = available_width - text_width
481+
local word_spacing = extra_space / spaces
482+
if word_spacing > 30 then
483+
word_spacing = 1
484+
end
485+
content.stream = content.stream .. string.format(
486+
"BT\n/%s %s Tf\n%s %s %s rg\n%s Tw\n%s %s Td\n(%s) Tj\nET\n",
487+
fontName,
488+
numberToString(fontSize),
489+
numberToString(color[1]),
490+
numberToString(color[2]),
491+
numberToString(color[3]),
492+
numberToString(word_spacing), -- Set word spacing using Tw operator
493+
numberToString(x_pos),
494+
numberToString(self.currentYPos(self)),
495+
self:escapePdfText(text)
496+
)
497+
return self
498+
end
499499

500-
-- For left, center, and right alignment
501-
-- Check if text width exceeds available space for left/right alignment
502-
content.stream = content.stream .. string.format(
500+
-- For left, center, and right alignment
501+
-- Check if text width exceeds available space for left/right alignment
502+
content.stream = content.stream .. string.format(
503503
"BT\n/%s %s Tf\n%s %s %s rg\n0 Tw\n%s %s Td\n(%s) Tj\nET\n",
504504
fontName,
505505
numberToString(fontSize),
@@ -830,6 +830,11 @@ end
830830
-- Draw rectangle on current page
831831
function PDFGenerator:drawRectangle(options)
832832
options = options or {}
833+
834+
if self.out_of_page == false and self.page_height - self.current_y - options.height - self.header_height < self.margin_y[1] + self.margin_y[2] then
835+
self:addPage()
836+
end
837+
833838
options.borderWidth = options.borderWidth or 1
834839
options.borderStyle = options.borderStyle or "solid"
835840
options.borderColor = options.borderColor or "000000" -- default gray
@@ -845,6 +850,7 @@ function PDFGenerator:drawRectangle(options)
845850

846851
local content = self.contents[self.current_page_obj]
847852

853+
848854
-- Save graphics state
849855
content.stream = content.stream .. "q\n"
850856

app/controllers/welcome_controller.lua

Lines changed: 136 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@ local app = {
99
PDFGenerator = require("pdfgenerator")
1010

1111
local pdf = PDFGenerator.new({header_height = 50})
12+
13+
pdf:setHeader(function(pageId)
14+
pdf:addParagraph("Header - redbean.com PDF Generator - %s on %d" % { pageId, pdf:totalPage(self) }, { fontSize = 16, alignment = "left", newPage = false })
15+
pdf:drawLine(50, 842-50, 595 - 50, 842-50, 1)
16+
end)
17+
1218
-- Add a page (default A4 size)
1319
pdf:addPage()
1420
--pdf:addCustomFont("fonts/Helvetica.ttf", "helvetica", "normal")
@@ -17,6 +23,40 @@ local app = {
1723
pdf:addCustomFont("fonts/TitilliumWeb-Bold.ttf", "titillium", "bold")
1824

1925

26+
local imgName = pdf:addImage(LoadAsset("greece.jpg"), 1600, 492, "jpeg")
27+
pdf:drawImage(imgName)
28+
29+
-- Add some text
30+
pdf:addParagraph([[
31+
Morbi ultrices pharetra risus sed pellentesque. Integer id semper erat. Duis lobortis mollis erat, id commodo orci lobortis ut. Sed laoreet libero sed lorem sagittis, et lacinia arcu efficitur. Curabitur eu scelerisque elit. Aenean enim turpis, congue nec ipsum non, dapibus laoreet ex. Cras viverra congue tortor vitae rutrum.
32+
]], { fontSize = 15, alignment = "justify", fontWeight = "bold" })
33+
34+
pdf:moveY(10)
35+
36+
pdf:addParagraph([[
37+
Morbi ultrices pharetra risus sed pellentesque. Integer id semper erat. Duis lobortis mollis erat, id commodo orci lobortis ut. Sed laoreet libero sed lorem sagittis, et lacinia arcu efficitur. Curabitur eu scelerisque elit. Aenean enim turpis, congue nec ipsum non, dapibus laoreet ex. Cras viverra congue tortor vitae rutrum.
38+
]], { fontSize = 15, alignment = "justify", fontWeight = "normal" })
39+
40+
pdf:moveY(10)
41+
42+
pdf:addParagraph([[
43+
Morbi ultrices pharetra risus sed pellentesque. Integer id semper erat. Duis lobortis mollis erat, id commodo orci lobortis ut. Sed laoreet libero sed lorem sagittis, et lacinia arcu efficitur. Curabitur eu scelerisque elit. Aenean enim turpis, congue nec ipsum non, dapibus laoreet ex. Cras viverra congue tortor vitae rutrum.
44+
]], { fontSize = 10, alignment = "right" })
45+
46+
pdf:moveY(10)
47+
48+
pdf:addParagraph([[
49+
Morbi ultrices pharetra risus sed pellentesque. Integer id semper erat. Duis lobortis mollis erat, id commodo orci lobortis ut. Sed laoreet libero sed lorem sagittis, et lacinia arcu efficitur. Curabitur eu scelerisque elit. Aenean enim turpis, congue nec ipsum non, dapibus laoreet ex. Cras viverra congue tortor vitae rutrum.
50+
]], { fontSize = 10, alignment = "center" })
51+
52+
pdf:moveY(10)
53+
54+
pdf:addParagraph([[
55+
Morbi ultrices pharetra risus sed pellentesque. Integer id semper erat. Duis lobortis mollis erat, id commodo orci lobortis ut. Sed laoreet libero sed lorem sagittis, et lacinia arcu efficitur. Curabitur eu scelerisque elit. Aenean enim turpis, congue nec ipsum non, dapibus laoreet ex. Cras viverra congue tortor vitae rutrum.
56+
]], { fontSize = 10, alignment = "left" })
57+
58+
pdf:moveY(10)
59+
2060
local headerColumns = {
2161
{ text = "Label", width = 305, fontSize = 10, alignment = "left", borderSides = { right = "false" } },
2262
{ text = "UnitPrice", width = 70, fontSize = 10, alignment = "right", borderSides = { left = "false", right = "false" } },
@@ -25,7 +65,7 @@ local app = {
2565
}
2666

2767
local dataColumns = {}
28-
for i = 1, 100 do
68+
for i = 1, 3 do
2969
table.insert(dataColumns, {
3070
{ text = "Mac Mini M4 pro " .. i, width = 305, fontSize = 10, alignment = "left", borderSides = { right = "false" } },
3171
{ text = "$700", width = 70, fontSize = 10, alignment = "right", borderSides = { left = "false", right = "false" } },
@@ -41,6 +81,101 @@ local app = {
4181
data_options = { fillColor = "fff", borderColor = "eee" }
4282
}, { padding_x = 5, padding_y = 5 })
4383

84+
pdf:moveY(10)
85+
86+
local dataColumns = {}
87+
for i = 1, 10 do
88+
table.insert(dataColumns, {
89+
{ text = "Mac Mini M4 pro Morbi ultrices pharetra risus sed pellentesque. Integer id semper erat. Duis l" .. i, width = 305, fontSize = 10, alignment = "left" },
90+
{ text = "$700", width = 70, fontSize = 10, alignment = "right" },
91+
{ text = "2", width = 50, fontSize = 10, alignment = "center" },
92+
{ text = "$1400", width = 70, fontSize = 10, alignment = "right" }
93+
})
94+
end
95+
96+
pdf:drawTable({
97+
header_columns = headerColumns,
98+
data_columns = dataColumns,
99+
header_options = { fillColor = "000", borderColor = "000", textColor = "fff" },
100+
data_options = { fillColor = "fff", borderColor = "eee", oddFillColor = "fafafa", evenFillColor = "fff" }
101+
}, { padding_x = 5, padding_y = 2 })
102+
103+
for i = 1, 1 do
104+
pdf:moveY(10)
105+
pdf:addParagraph([[
106+
Morbi ultrices pharetra risus sed pellentesque. Integer id semper erat. Duis lobortis mollis erat, id commodo orci lobortis ut. Sed laoreet libero sed lorem sagittis, et lacinia arcu efficitur. Curabitur eu scelerisque elit. Aenean enim turpis, congue nec ipsum non, dapibus laoreet ex. Cras viverra congue tortor vitae rutrum.
107+
108+
Morbi ultrices pharetra risus sed pellentesque. Integer id semper erat. Duis lobortis mollis erat, id commodo orci lobortis ut. Sed laoreet libero sed lorem sagittis, et lacinia arcu efficitur. Curabitur eu scelerisque elit. Aenean enim turpis, congue nec ipsum non, dapibus laoreet ex. Cras viverra congue tortor vitae rutrum.
109+
]], { fontSize = 10, alignment = "justify", fontWeight = "bold", color = "FF0000" })
110+
end
111+
112+
113+
pdf:addPage()
114+
-- Chart title
115+
pdf:addParagraph("PDF Chart Examples", { fontSize = 20, alignment = "center", fontWeight = "bold" })
116+
pdf:moveY(20)
117+
118+
-- 1. Bar Chart
119+
pdf:addParagraph("1. Bar Chart - Monthly Sales", { fontSize = 16, alignment = "left", fontWeight = "bold" })
120+
pdf:moveY(10)
121+
122+
for i = 1, 5 do
123+
124+
local barData = {Rand64() % 100, Rand64() % 100, Rand64() % 100, Rand64() % 100, Rand64() % 100, Rand64() % 100, Rand64() % 100, Rand64() % 100, Rand64() % 100, Rand64() % 100, Rand64() % 100, Rand64() % 100}
125+
local months = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}
126+
local chartWidth = 555 - 100
127+
local chartHeight = 60
128+
local barWidth = chartWidth / #barData - 5
129+
local maxValue = 100
130+
local startX = 25
131+
132+
133+
-- Draw chart background
134+
pdf:drawRectangle({
135+
width = chartWidth + 40,
136+
height = chartHeight + 40 + 10,
137+
borderWidth = 1,
138+
borderColor = "cccccc",
139+
fillColor = "f8f9fa"
140+
})
141+
142+
local startY = pdf.current_y + 20
143+
144+
-- Draw bars
145+
for i, value in ipairs(barData) do
146+
local barHeight = (value / maxValue) * chartHeight
147+
local x = startX + (i-1) * (barWidth + 5) - 2
148+
local y = startY + chartHeight - barHeight
149+
150+
-- Bar color based on value
151+
local color = value > 80 and "4CAF50" or (value > 60 and "FF9800" or "F44336")
152+
153+
pdf:setX(x)
154+
pdf:setY(startY + chartHeight - barHeight)
155+
pdf:drawRectangle({
156+
width = barWidth,
157+
height = barHeight,
158+
borderWidth = 0.3,
159+
borderColor = "333333",
160+
fillColor = color,
161+
})
162+
163+
-- Value label on top of bar
164+
pdf:setX(x)
165+
pdf:setY(startY + chartHeight - barHeight - 5)
166+
pdf:addText(tostring(value) .. " ", 8, "000000", "center", barWidth)
167+
168+
-- Month label below bar
169+
pdf:setX(x)
170+
pdf:setY(startY + chartHeight + 10)
171+
pdf:addText(months[i] .. " 2025", 6, "000000", "center", barWidth)
172+
pdf:moveY(20)
173+
end
174+
175+
pdf:setX(0)
176+
pdf:moveY(5)
177+
end
178+
44179
SetHeader("Content-Type", "application/pdf")
45180
Write(pdf:generate())
46181
end,

config/routes.lua

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
Routes = { ["GET"] = { [""] = "welcome#index", ["pdf"] = "welcome#pdf" } } --define root
1+
Routes = { ["GET"] = {
2+
[""] = "welcome#index",
3+
["pdf"] = "welcome#pdf",
4+
["chart"] = "welcome#chart"
5+
}
6+
}
27

38
CustomRoute("POST", "/upload/:collection/:key/:field", "uploads#upload")
49
CustomRoute("GET", "/o/:uuid/:format", "uploads#original_image")

0 commit comments

Comments
 (0)