@@ -19,6 +19,75 @@ local function getNewObjNum()
1919 return num
2020end
2121
22+
23+ -- Create new PDF document
24+ function PDFGenerator .new (options )
25+ objCounter = 1
26+ local self = {
27+ objects = {},
28+ current_page = 0 ,
29+ current_page_obj = nil ,
30+ page_list = {}, -- Array to store page objects
31+ pages_obj = nil , -- Object number for pages tree
32+ images = {},
33+ contents = {},
34+ catalog = nil ,
35+ info = nil ,
36+ root = nil ,
37+ page_width = 595 ,
38+ page_height = 842 ,
39+ header_height = 0 ,
40+ margin_x = { 50 , 50 },
41+ margin_y = { 50 , 80 },
42+ current_x = 0 ,
43+ current_y = 0 ,
44+ resources = {},
45+ font_metrics = {},
46+ fonts = {},
47+ rgb_colors = {},
48+ last_font = { fontFamily = " helvetica" , fontWeight = " normal" },
49+ current_table = {
50+ current_row = {
51+ height = nil ,
52+ },
53+ padding_x = 5 ,
54+ padding_y = 5 ,
55+ header_columns = nil ,
56+ data_columns = nil ,
57+ header_options = nil ,
58+ data_options = nil ,
59+ },
60+ out_of_page = false ,
61+ }
62+
63+ self = table .merge (self , options or {})
64+
65+ self .header = function (pageId ) end
66+ self .footer = function (pageId )
67+ self .moveY (self , 5 )
68+ self :addParagraph (" Page %s of %s" % { pageId , # self .page_list }, { fontSize = 8 , alignment = " right" })
69+ end
70+
71+ -- Initialize document
72+ self .info = getNewObjNum ()
73+ self .root = getNewObjNum ()
74+ self .pages_obj = getNewObjNum ()
75+ self .basic_font_obj = getNewObjNum ()
76+ self .basic_bold_font_obj = getNewObjNum ()
77+
78+ -- Initialize resources
79+ self .resources = { fonts = {}, images = {} }
80+
81+ -- Add required PDF objects
82+ self .objects [self .info ] = string.format (
83+ " %d 0 obj\n << /Producer (Lua PDF Generator 1.0) /CreationDate (D:%s) >>\n endobj\n " ,
84+ self .info ,
85+ os.date (" !%Y%m%d%H%M%S" )
86+ )
87+
88+ return setmetatable (self , { __index = PDFGenerator })
89+ end
90+
2291function PDFGenerator :getHashValues (hash )
2392 local values = {}
2493 for _ , value in pairs (hash ) do
@@ -96,6 +165,11 @@ function PDFGenerator:addBasicFont()
96165 " %d 0 obj\n << /Type /Font /Subtype /Type1 /BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding >>\n endobj\n " ,
97166 self .basic_bold_font_obj
98167 )
168+
169+ self .custom_fonts = {
170+ [' helvetica-normal' ] = self .basic_bold_font_obj ,
171+ [' helvetica-bold' ] = self .basic_bold_font_obj
172+ }
99173end
100174
101175-- Add custom font (TrueType)
@@ -296,6 +370,7 @@ function PDFGenerator:getTextWidth(text, fontSize, fontWeight)
296370 self .font_metrics = self .font_metrics or {}
297371 local fontMetrics = self .font_metrics [self .last_font .fontFamily .. " -" .. fontWeight ]
298372
373+ if (fontMetrics == nil ) then return end
299374 local width = 0
300375 for i = 1 , # text do
301376 local charCode = string.byte (text , i )
@@ -1050,8 +1125,53 @@ function PDFGenerator:drawStar(outerRadius, branches, borderWidth, borderStyle,
10501125 return self
10511126end
10521127
1128+
1129+ local function get_jpeg_dimensions (data )
1130+ -- Check for JPEG SOI marker (0xFFD8)
1131+ if data :byte (1 ) ~= 0xFF or data :byte (2 ) ~= 0xD8 then
1132+ return nil , nil , " Not a valid JPEG file"
1133+ end
1134+
1135+ -- Start parsing from byte 3
1136+ local pos = 3
1137+ local length = # data
1138+
1139+ -- Loop through the JPEG markers
1140+ while pos < length do
1141+ -- Look for marker (starts with 0xFF)
1142+ if data :byte (pos ) ~= 0xFF then
1143+ return nil , nil , " Invalid JPEG marker"
1144+ end
1145+
1146+ -- Get marker type
1147+ local marker = data :byte (pos + 1 )
1148+ pos = pos + 2
1149+
1150+ -- Check for SOF markers (0xC0 to 0xCF, excluding 0xC4, 0xC8, 0xCC)
1151+ if marker >= 0xC0 and marker <= 0xCF and marker ~= 0xC4 and marker ~= 0xC8 and marker ~= 0xCC then
1152+ -- Skip 2 bytes (length of block) and 1 byte (sample precision)
1153+ pos = pos + 3
1154+ -- Read height (2 bytes)
1155+ local height = data :byte (pos ) * 256 + data :byte (pos + 1 )
1156+ -- Read width (2 bytes)
1157+ local width = data :byte (pos + 2 ) * 256 + data :byte (pos + 3 )
1158+ return width , height
1159+ else
1160+ -- Read the length of the segment (2 bytes)
1161+ if pos + 1 > length then
1162+ return nil , nil , " Unexpected end of file"
1163+ end
1164+ local segment_length = data :byte (pos ) * 256 + data :byte (pos + 1 )
1165+ -- Skip the segment
1166+ pos = pos + segment_length
1167+ end
1168+ end
1169+
1170+ return nil , nil , " No SOF marker found"
1171+ end
1172+
10531173-- Add image to PDF (imgData should be binary data)
1054- function PDFGenerator :addImage (imgData , width , height , format )
1174+ function PDFGenerator :addImage (imgData , format )
10551175 format = format :lower ()
10561176 if format ~= " jpeg" then
10571177 error (" Unsupported image format: " .. format )
@@ -1061,6 +1181,8 @@ function PDFGenerator:addImage(imgData, width, height, format)
10611181 local imageObj = getNewObjNum ()
10621182 local imgName = string.format (" Im%d" , # self .resources .images + 1 )
10631183
1184+ local width , height , err = get_jpeg_dimensions (imgData )
1185+
10641186 -- Store image information
10651187 self .resources .images [imgName ] = {
10661188 obj = imageObj ,
@@ -1851,74 +1973,6 @@ function PDFGenerator:BarChart(barData, labels)
18511973end
18521974-- /ChartBar
18531975
1854- -- Create new PDF document
1855- function PDFGenerator .new (options )
1856- objCounter = 1
1857- local self = {
1858- objects = {},
1859- current_page = 0 ,
1860- current_page_obj = nil ,
1861- page_list = {}, -- Array to store page objects
1862- pages_obj = nil , -- Object number for pages tree
1863- images = {},
1864- contents = {},
1865- catalog = nil ,
1866- info = nil ,
1867- root = nil ,
1868- page_width = 595 ,
1869- page_height = 842 ,
1870- header_height = 0 ,
1871- margin_x = { 50 , 50 },
1872- margin_y = { 50 , 80 },
1873- current_x = 0 ,
1874- current_y = 0 ,
1875- resources = {},
1876- font_metrics = {},
1877- fonts = {},
1878- rgb_colors = {},
1879- last_font = { fontFamily = " Helvetica" , fontWeight = " normal" },
1880- current_table = {
1881- current_row = {
1882- height = nil ,
1883- },
1884- padding_x = 5 ,
1885- padding_y = 5 ,
1886- header_columns = nil ,
1887- data_columns = nil ,
1888- header_options = nil ,
1889- data_options = nil ,
1890- },
1891- out_of_page = false ,
1892- }
1893-
1894- self = table .merge (self , options or {})
1895-
1896- self .header = function (pageId ) end
1897- self .footer = function (pageId )
1898- self .moveY (self , 5 )
1899- self :addParagraph (" Page %s of %s" % { pageId , # self .page_list }, { fontSize = 8 , alignment = " right" })
1900- end
1901-
1902- -- Initialize document
1903- self .info = getNewObjNum ()
1904- self .root = getNewObjNum ()
1905- self .pages_obj = getNewObjNum ()
1906- self .basic_font_obj = getNewObjNum ()
1907- self .basic_bold_font_obj = getNewObjNum ()
1908-
1909- -- Initialize resources
1910- self .resources = { fonts = {}, images = {} }
1911-
1912- -- Add required PDF objects
1913- self .objects [self .info ] = string.format (
1914- " %d 0 obj\n << /Producer (Lua PDF Generator 1.0) /CreationDate (D:%s) >>\n endobj\n " ,
1915- self .info ,
1916- os.date (" !%Y%m%d%H%M%S" )
1917- )
1918-
1919- return setmetatable (self , { __index = PDFGenerator })
1920- end
1921-
19221976-- Generate PDF and return as string
19231977function PDFGenerator :generate ()
19241978 local output = {}
0 commit comments