├── doc └── us │ ├── doc.bat │ ├── doc.css │ ├── index.html │ ├── index.md │ ├── license.html │ ├── license.md │ └── makedoc.lua ├── readme.md ├── tests └── printxlsx.lua ├── xlsx.jam └── xlsx.lua /doc/us/doc.bat: -------------------------------------------------------------------------------- 1 | cd %~dp0 2 | lua %~dp0makedoc.lua 3 | start %~dp0index.html 4 | 5 | -------------------------------------------------------------------------------- /doc/us/doc.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin-left: 1em; 3 | margin-right: 1em; 4 | font-family: arial, helvetica, geneva, sans-serif; 5 | background-color:#ffffff; margin:0px; 6 | } 7 | 8 | code { 9 | font-family: "Andale Mono", monospace; 10 | } 11 | 12 | tt { 13 | font-family: "Andale Mono", monospace; 14 | } 15 | 16 | body, td, th { font-size: 11pt; } 17 | 18 | h1, h2, h3, h4 { margin-left: 0em; } 19 | 20 | textarea, pre, tt { font-size:10pt; } 21 | body, td, th { color:#000000; } 22 | small { font-size:0.85em; } 23 | h1 { font-size:1.5em; } 24 | h2 { font-size:1.25em; } 25 | h3 { font-size:1.15em; } 26 | h4 { font-size:1.06em; } 27 | 28 | a:link { font-weight:bold; color: #004080; text-decoration: none; } 29 | a:visited { font-weight:bold; color: #006699; text-decoration: none; } 30 | a:link:hover { text-decoration:underline; } 31 | hr { color:#cccccc } 32 | img { border-width: 0px; } 33 | 34 | 35 | h3 { padding-top: 1em; } 36 | 37 | p { margin-left: 1em; } 38 | 39 | p.name { 40 | font-family: "Andale Mono", monospace; 41 | padding-top: 1em; 42 | margin-left: 0em; 43 | } 44 | 45 | blockquote { margin-left: 3em; } 46 | 47 | .example { 48 | background-color: rgb(245, 245, 245); 49 | border-top-width: 1px; 50 | border-right-width: 1px; 51 | border-bottom-width: 1px; 52 | border-left-width: 1px; 53 | border-top-style: solid; 54 | border-right-style: solid; 55 | border-bottom-style: solid; 56 | border-left-style: solid; 57 | border-top-color: silver; 58 | border-right-color: silver; 59 | border-bottom-color: silver; 60 | border-left-color: silver; 61 | padding: 1em; 62 | margin-left: 1em; 63 | margin-right: 1em; 64 | font-family: "Andale Mono", monospace; 65 | font-size: smaller; 66 | } 67 | 68 | 69 | hr { 70 | margin-left: 0em; 71 | background: #00007f; 72 | border: 0px; 73 | height: 1px; 74 | } 75 | 76 | ul { list-style-type: disc; } 77 | 78 | table.index { border: 1px #00007f; } 79 | table.index td { text-align: left; vertical-align: top; } 80 | table.index ul { padding-top: 0em; margin-top: 0em; } 81 | 82 | table { 83 | border: 1px solid black; 84 | border-collapse: collapse; 85 | margin-left: auto; 86 | margin-right: auto; 87 | } 88 | th { 89 | border: 1px solid black; 90 | padding: 0.5em; 91 | } 92 | td { 93 | border: 1px solid black; 94 | padding: 0.5em; 95 | } 96 | div.header, div.footer { margin-left: 0em; } 97 | 98 | #container 99 | { 100 | margin-left: 1em; 101 | margin-right: 1em; 102 | background-color: #f0f0f0; 103 | } 104 | 105 | #product 106 | { 107 | text-align: center; 108 | border-bottom: 1px solid #cccccc; 109 | background-color: #ffffff; 110 | } 111 | 112 | #product big { 113 | font-size: 2em; 114 | } 115 | 116 | #product_logo 117 | { 118 | } 119 | 120 | #product_name 121 | { 122 | } 123 | 124 | #product_description 125 | { 126 | } 127 | 128 | #main 129 | { 130 | background-color: #f0f0f0; 131 | border-left: 2px solid #cccccc; 132 | } 133 | 134 | #navigation 135 | { 136 | float: left; 137 | width: 12em; 138 | margin: 0; 139 | vertical-align: top; 140 | background-color: #f0f0f0; 141 | overflow:visible; 142 | } 143 | 144 | #navigation h1 { 145 | background-color:#e7e7e7; 146 | font-size:1.1em; 147 | color:#000000; 148 | text-align:left; 149 | margin:0px; 150 | padding:0.2em; 151 | border-top:1px solid #dddddd; 152 | border-bottom:1px solid #dddddd; 153 | } 154 | 155 | #navigation ul 156 | { 157 | font-size:1em; 158 | list-style-type: none; 159 | padding: 0; 160 | margin: 1px; 161 | } 162 | 163 | #navigation li 164 | { 165 | text-indent: -1em; 166 | margin: 0em 0em 0em 0.5em; 167 | display: block; 168 | padding: 3px 0px 0px 12px; 169 | } 170 | 171 | #navigation li li a 172 | { 173 | padding: 0px 3px 0px -1em; 174 | } 175 | 176 | #content 177 | { 178 | margin-left: 12em; 179 | padding: 1em; 180 | border-left: 2px solid #cccccc; 181 | border-right: 2px solid #cccccc; 182 | background-color: #ffffff; 183 | } 184 | 185 | #about 186 | { 187 | clear: both; 188 | margin: 0; 189 | padding: 5px; 190 | border-top: 2px solid #cccccc; 191 | background-color: #ffffff; 192 | } 193 | 194 | @media print { 195 | body { 196 | font: 10pt "Times New Roman", "TimeNR", Times, serif; 197 | } 198 | a { font-weight:bold; color: #004080; text-decoration: underline; } 199 | 200 | #main { background-color: #ffffff; border-left: 0px; } 201 | #container { margin-left: 2%; margin-right: 2%; background-color: #ffffff; } 202 | 203 | #content { margin-left: 0px; padding: 1em; border-left: 0px; border-right: 0px; background-color: #ffffff; } 204 | 205 | #navigation { display: none; 206 | } 207 | 208 | #product_logo 209 | { 210 | display: none; 211 | } 212 | 213 | #about img 214 | { 215 | display: none; 216 | } 217 | 218 | .example { 219 | font-family: "Andale Mono", monospace; 220 | font-size: 8pt; 221 | page-break-inside: avoid; 222 | } 223 | } 224 | -------------------------------------------------------------------------------- /doc/us/index.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | xlsx 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 |
16 | 17 |
xlsx
18 |
Read/write xlsx files
19 |
20 | 21 |
22 | 23 | 41 | 42 |
43 | 44 | 45 |

Introduction

46 | 47 |

The module xlsx allows read and write access to .xlsx files.

48 | 49 | 50 | 51 |

Example

52 | 53 | 54 |
 55 |     require 'xlsx'
 56 | 
 57 |     local workbook = xlsx.Workbook(filename)
 58 | 
59 | 60 | 61 | 62 | 63 |
64 |

xlsx Reference

65 | 66 |

workbook = xlsx.Workbook([filename])

67 | 68 |

Creates or opens a Workbook object. Returns a Workbook object.

69 | 70 |
    71 |
  • filename is the name of the .xlsx file to open.
  • 72 |
73 | 74 | 75 | 76 | 77 |
78 |

Workbook Reference

79 | 80 |

totalWorksheets = #workbook

81 | 82 |

Returns the total number of Excel worksheets in the current Excel workbook.

83 | 84 | 85 | 86 |

worksheet = workbook[sheetIndex | name]

87 | 88 |

Returns an object representing the Excel worksheet specified by sheetIndex or name. If no worksheet is found with the given sheetIndex or name, then nil is returned.

89 | 90 |
    91 |
  • sheetIndex is an index in the range of 1 <= sheetIndex <= GetTotalWorksheets representing the sheet to retrieve.
  • 92 |
  • name is a string representing the sheet name to retrieve.
  • 93 |
94 | 95 | 96 | 97 |

workbook:Close()

98 | 99 |

Closes the current Excel workbook.

100 | 101 | 102 | 103 | 104 |

totalWorksheets = workbook:GetTotalWorksheets()

105 | 106 |

Returns the total number of Excel worksheets in the current Excel workbook.

107 | 108 | 109 | 110 | 111 |

worksheet = workbook:GetWorksheet(sheetIndex | name)

112 | 113 |

Returns an object representing the Excel worksheet specified by sheetIndex or name. If no worksheet is found with the given sheetIndex or name, then GetWorksheet returns nil.

114 | 115 |
    116 |
  • sheetIndex is an index in the range of 0 <= sheetIndex < GetTotalWorksheets() representing the sheet to retrieve.
  • 117 |
  • name is either a string or an xls.wchar representing the sheet name to retrieve.
  • 118 |
119 | 120 | 121 | 122 | 123 |

sheetName = workbook:GetSheetName(sheetIndex)

124 | 125 |

Returns either the name of the sheet.

126 | 127 |
    128 |
  • sheetIndex is an index in the range of 1 <= sheetIndex <= GetTotalWorksheets() representing the worksheet to delete.
  • 129 |
130 | 131 | 132 | 133 | 134 |
135 |

Worksheet Reference

136 | 137 |

sheetName = worksheet:GetSheetName()

138 | 139 |

Returns the name of the sheet.

140 | 141 | 142 | 143 | 144 |

totalRows = worksheet:GetTotalRows()

145 | 146 |

Returns the total number of rows in the Excel worksheet.

147 | 148 | 149 | 150 | 151 |

totalColumns = worksheet:GetTotalCols()

152 | 153 |

Returns the total number of columns in the Excel worksheet.

154 | 155 | 156 | 157 |

cell = worksheet:Cell(row, col)

158 | 159 |

Retrieves the contents of a cell from the Excel worksheet.

160 | 161 |

Returns the cell if the operation succeeded, nil if either the row or column are not in range.

162 | 163 |
    164 |
  • row is a value from 0 to 65535, representing the row in the Excel worksheet to retrieve.
  • 165 |
  • col is a value from 0 to 255, representing the column in the Excel worksheet to retrieve.
  • 166 |
167 | 168 | 169 | 170 | 171 |

cell = worksheet.COLROW

172 | 173 |

Retrieves the contents of a cell from the Excel worksheet.

174 | 175 |

Returns the cell if the operation succeeded, nil if either the row or column are not in range.

176 | 177 |
    178 |
  • COLROW is a column and row in Excel format, such as A4 or BD12.
  • 179 |
180 | 181 | 182 | 183 | 184 | 185 |
186 |

Cell Reference

187 | 188 |

cellType = cell:Type()

189 | 190 |

Returns one of the following as the type of this Excel cell.

191 | 192 |
    193 |
  • cell.UNDEFINED
  • 194 |
  • cell.BOOLEAN
  • 195 |
  • cell.INT
  • 196 |
  • cell.DOUBLE
  • 197 |
  • cell.STRING
  • 198 |
  • cell.WSTRING
  • 199 |
  • cell.FORMULA
  • 200 |
201 | 202 | 203 | 204 |

cellContent = cell.value

205 | 206 |

Returns the raw value of the cell.

207 | 208 | 209 | 210 | 211 |

cellContent = cell:Get()

212 | 213 |

If the type of the cell is cell.BOOLEAN, the boolean content of the cell is returned as a Lua boolean. If the type of the cell is cell.INT or cell.DOUBLE, the integer or double content of the cell is returned as a Lua number. If the type of the cell is cell.STRING, the ANSI string content of the cell is returned as a Lua string. If the type of the cell is cell.WSTRING, the Unicode string content of the cell are returned as an xls.wchar. Otherwise, nil is returned.

214 | 215 | 216 | 217 |

cellContent = cell:GetBoolean()

218 | 219 |

If the type of the cell is cell.BOOLEAN, the boolean content of the cell is returned as a Lua boolean. Otherwise, nil is returned.

220 | 221 | 222 | 223 |

cellContent = cell:GetInteger()

224 | 225 |

If the type of the cell is cell.INT, the integer content of the cell is returned as a Lua number. Otherwise, nil is returned.

226 | 227 | 228 | 229 |

cellContent = cell:GetDouble()

230 | 231 |

If the type of the cell is cell.DOUBLE, the double content of the cell is returned as a Lua number. Otherwise, nil is returned.

232 | 233 | 234 | 235 |

cellContent = cell:GetString()

236 | 237 |

If the type of the cell is cell.STRING, the ANSI string content of the cell is returned as a Lua string. Otherwise, nil is returned.

238 | 239 | 240 | 241 | 242 |
243 | 244 |
245 | 246 |
247 |

Valid XHTML 1.0!

248 |
249 | 250 |
251 | 252 | 253 | 254 | 255 | -------------------------------------------------------------------------------- /doc/us/index.md: -------------------------------------------------------------------------------- 1 | ## Introduction 2 | 3 | The module *xlsx* allows read and write access to .xlsx files. 4 | 5 | 6 | 7 | ## Example 8 | 9 |
 10 |     require 'xlsx'
 11 | 
 12 |     local workbook = xlsx.Workbook(filename)
 13 | 
14 | 15 | 16 | 17 | ------------------------- 18 | ## xlsx Reference 19 | 20 | ### workbook = xlsx.Workbook([filename]) 21 | 22 | Creates or opens a Workbook object. Returns a Workbook object. 23 | 24 | * `filename` is the name of the .xlsx file to open. 25 | 26 | 27 | 28 | 29 | ------------------------- 30 | ## Workbook Reference 31 | 32 | ### totalWorksheets = #workbook 33 | 34 | Returns the total number of Excel worksheets in the current Excel workbook. 35 | 36 | 37 | 38 | ### worksheet = workbook[sheetIndex | name] 39 | 40 | Returns an object representing the Excel worksheet specified by `sheetIndex` or `name`. If no worksheet is found with the given `sheetIndex` or `name`, then `nil` is returned. 41 | 42 | * `sheetIndex` is an index in the range of 1 <= `sheetIndex` <= `GetTotalWorksheets` representing the sheet to retrieve. 43 | * `name` is a string representing the sheet name to retrieve. 44 | 45 | 46 | 47 | ### workbook:Close() 48 | 49 | Closes the current Excel workbook. 50 | 51 | 52 | 53 | 54 | ### totalWorksheets = workbook:GetTotalWorksheets() 55 | 56 | Returns the total number of Excel worksheets in the current Excel workbook. 57 | 58 | 59 | 60 | 61 | ### worksheet = workbook:GetWorksheet(sheetIndex | name) 62 | 63 | Returns an object representing the Excel worksheet specified by `sheetIndex` or `name`. If no worksheet is found with the given `sheetIndex` or `name`, then `GetWorksheet` returns `nil`. 64 | 65 | * `sheetIndex` is an index in the range of 0 <= `sheetIndex` < `GetTotalWorksheets()` representing the sheet to retrieve. 66 | * `name` is either a string or an xls.wchar representing the sheet name to retrieve. 67 | 68 | 69 | 70 | 71 | ### sheetName = workbook:GetSheetName(sheetIndex) 72 | 73 | Returns either the name of the sheet. 74 | 75 | * `sheetIndex` is an index in the range of 1 <= `sheetIndex` <= `GetTotalWorksheets()` representing the worksheet to delete. 76 | 77 | 78 | 79 | 80 | ------------------------- 81 | ## Worksheet Reference 82 | 83 | ### sheetName = worksheet:GetSheetName() 84 | 85 | Returns the name of the sheet. 86 | 87 | 88 | 89 | 90 | ### totalRows = worksheet:GetTotalRows() 91 | 92 | Returns the total number of rows in the Excel worksheet. 93 | 94 | 95 | 96 | 97 | ### totalColumns = worksheet:GetTotalCols() 98 | 99 | Returns the total number of columns in the Excel worksheet. 100 | 101 | 102 | 103 | ### cell = worksheet:Cell(row, col) 104 | 105 | Retrieves the contents of a cell from the Excel worksheet. 106 | 107 | Returns the cell if the operation succeeded, `nil` if either the row or column are not in range. 108 | 109 | * `row` is a value from 0 to 65535, representing the row in the Excel worksheet to retrieve. 110 | * `col` is a value from 0 to 255, representing the column in the Excel worksheet to retrieve. 111 | 112 | 113 | 114 | 115 | ### cell = worksheet.COLROW 116 | 117 | Retrieves the contents of a cell from the Excel worksheet. 118 | 119 | Returns the cell if the operation succeeded, `nil` if either the row or column are not in range. 120 | 121 | * `COLROW` is a column and row in Excel format, such as A4 or BD12. 122 | 123 | 124 | 125 | 126 | 127 | ------------------------- 128 | ## Cell Reference 129 | 130 | ### cellType = cell:Type() 131 | 132 | Returns one of the following as the type of this Excel cell. 133 | 134 | * `cell.UNDEFINED` 135 | * `cell.BOOLEAN` 136 | * `cell.INT` 137 | * `cell.DOUBLE` 138 | * `cell.STRING` 139 | * `cell.WSTRING` 140 | * `cell.FORMULA` 141 | 142 | 143 | 144 | ### cellContent = cell.value 145 | 146 | Returns the raw value of the cell. 147 | 148 | 149 | 150 | 151 | ### cellContent = cell:Get() 152 | 153 | If the type of the cell is `cell.BOOLEAN`, the boolean content of the cell is returned as a Lua boolean. If the type of the cell is `cell.INT` or `cell.DOUBLE`, the integer or double content of the cell is returned as a Lua number. If the type of the cell is `cell.STRING`, the ANSI string content of the cell is returned as a Lua string. If the type of the cell is `cell.WSTRING`, the Unicode string content of the cell are returned as an `xls.wchar`. Otherwise, `nil` is returned. 154 | 155 | 156 | 157 | ### cellContent = cell:GetBoolean() 158 | 159 | If the type of the cell is `cell.BOOLEAN`, the boolean content of the cell is returned as a Lua boolean. Otherwise, `nil` is returned. 160 | 161 | 162 | 163 | ### cellContent = cell:GetInteger() 164 | 165 | If the type of the cell is `cell.INT`, the integer content of the cell is returned as a Lua number. Otherwise, `nil` is returned. 166 | 167 | 168 | 169 | ### cellContent = cell:GetDouble() 170 | 171 | If the type of the cell is `cell.DOUBLE`, the double content of the cell is returned as a Lua number. Otherwise, `nil` is returned. 172 | 173 | 174 | 175 | ### cellContent = cell:GetString() 176 | 177 | If the type of the cell is `cell.STRING`, the ANSI string content of the cell is returned as a Lua string. Otherwise, `nil` is returned. 178 | 179 | -------------------------------------------------------------------------------- /doc/us/license.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | xlsx 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 |
16 | 17 |
xlsx
18 |
Read/write xlsx files
19 |
20 | 21 |
22 | 23 | 41 | 42 |
43 | 44 | 45 |

License

46 | 47 | 48 | 49 | 50 |
51 | 52 |
53 | 54 |
55 |

Valid XHTML 1.0!

56 |
57 | 58 |
59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /doc/us/license.md: -------------------------------------------------------------------------------- 1 | ## License 2 | 3 | -------------------------------------------------------------------------------- /doc/us/makedoc.lua: -------------------------------------------------------------------------------- 1 | local cosmo = require "cosmo" 2 | require "markdown" 3 | 4 | local pages = { 5 | { name = "Home", file = "index", sections = {} }, 6 | { name = "License", file = "license", sections = {} } 7 | } 8 | 9 | local project = { 10 | name = "xlsx", 11 | blurb = "Read/write xlsx files", 12 | -- logo = "orbit.png", 13 | } 14 | 15 | local template = [==[ 16 | 17 | 19 | 20 | 21 | $name 22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 |
31 | 32 |
$name
33 |
$blurb
34 |
35 | 36 |
37 | 38 | 50 | 51 |
52 | 53 | $content 54 | 55 |
56 | 57 |
58 | 59 |
60 |

Valid XHTML 1.0!

61 |
62 | 63 |
64 | 65 | 66 | 67 | 68 | ]==] 69 | 70 | local function readfile(filename) 71 | local file = io.open(filename) 72 | local contents = file:read("*a") 73 | file:close() 74 | return contents 75 | end 76 | 77 | local function writefile(filename, contents) 78 | local file = io.open(filename, "w+") 79 | file:write(contents) 80 | file:close() 81 | end 82 | 83 | local function gen_page(project, pages, p) 84 | project.pages = function () 85 | for _, page in ipairs(pages) do 86 | local namelink 87 | if page.file == p.file then 88 | namelink = cosmo.fill([[$name]], { name = page.name}) 89 | else 90 | namelink = cosmo.fill([[$name]], { name = page.name, file = page.file}) 91 | end 92 | cosmo.yield{ namelink = namelink, sections = function () 93 | for _, s in ipairs(page.sections) do 94 | cosmo.yield{ name = s.name, anchor = 95 | page.file .. ".html#" .. s.anchor } 96 | end 97 | end } 98 | end 99 | end 100 | return (cosmo.fill(template, project)) 101 | end 102 | 103 | for _, p in ipairs(pages) do 104 | project.content = markdown(readfile(p.file .. ".md")) 105 | writefile(p.file .. ".html", gen_page(project, pages, p)) 106 | end 107 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | ## Introduction 2 | 3 | The module *xlsx* allows read access to .xlsx files. 4 | 5 | 6 | 7 | ## Example 8 | 9 |
10 |     require 'xlsx'
11 | 
12 |     local workbook = xlsx.Workbook(filename)
13 | 
14 | 15 | -------------------------------------------------------------------------------- /tests/printxlsx.lua: -------------------------------------------------------------------------------- 1 | local xlsx = require 'xlsx' 2 | 3 | -- Load a workbook with one sheet, display its contents and 4 | -- save into another file. 5 | --if Tilde then Tilde('disable') end 6 | local workbook = xlsx.Workbook(arg[1]) 7 | if Tilde then Tilde('enable') end 8 | for sheetIndex = 1, workbook:GetTotalWorksheets() do 9 | local sheet1 = workbook[sheetIndex] 10 | local maxRows = sheet1:GetTotalRows() 11 | local maxCols = sheet1:GetTotalCols() 12 | print(sheetIndex, "Dimension of " .. sheet1:GetSheetName() .. " (" .. maxRows .. ", " .. maxCols .. ")") 13 | 14 | print() 15 | for c = 0, maxCols - 1 do io.write(("%10d|"):format(c + 1)) end 16 | io.write('\n') 17 | 18 | for r = 0, maxRows - 1 do 19 | io.write(("%10d|"):format(r + 1)) 20 | for c = 0, maxCols - 1 do 21 | local cell = sheet1:Cell(r, c) 22 | if cell then 23 | local cellType = cell:Type() 24 | if cellType == cell.UNDEFINED then 25 | io.write(' |') 26 | 27 | elseif cellType == cell.BOOLEAN then 28 | io.write(("%10s|"):format(cell:GetBoolean() and 'TRUE' or 'FALSE')) 29 | 30 | elseif cellType == cell.INT then 31 | io.write(("%10d|"):format(cell:GetInteger())) 32 | 33 | elseif cellType == cell.DOUBLE then 34 | io.write(("%10.6g|"):format(cell:GetDouble())) 35 | 36 | elseif cellType == cell.STRING then 37 | io.write(("%10s|"):format(cell:GetString())) 38 | 39 | elseif cellType == cell.WSTRING then 40 | io.write(("%10s|"):format(tostring(cell:GetWString()))) 41 | end 42 | else 43 | io.write(' |') 44 | end 45 | end 46 | io.write('\n') 47 | end 48 | io.write('\n') 49 | end 50 | 51 | -------------------------------------------------------------------------------- /xlsx.jam: -------------------------------------------------------------------------------- 1 | { 2 | 3 | local SRCS = 4 | xlsx.lua 5 | ; 6 | 7 | Lua.Module xlsx : : $(SRCS) ; 8 | 9 | CopyFiles xlsx : $(LUA_LDIR) : $(SUBDIR)/$(SRCS) ; 10 | 11 | } 12 | -------------------------------------------------------------------------------- /xlsx.lua: -------------------------------------------------------------------------------- 1 | local ziparchive = require 'ziparchive' 2 | local xmlize = require 'xmlize' 3 | 4 | local M = {} 5 | 6 | local colRowPattern = "([a-zA-Z]*)(%d*)" 7 | 8 | local function _xlsx_readdocument(xlsx, documentName) 9 | local file = xlsx.archive:fileopen(documentName) 10 | if not file then return end 11 | local buffer = xlsx.archive:fileread(file) 12 | xlsx.archive:fileclose(file) 13 | return xmlize.luaize(buffer) 14 | end 15 | 16 | local __cellMetatable = { 17 | UNDEFINED = 0, 18 | INT = 1, 19 | DOUBLE = 2, 20 | STRING = 3, 21 | WSTRING = 4, 22 | FORMULA = 5, 23 | BOOLEAN = 6, 24 | 25 | Get = function(self) 26 | return self.value 27 | end, 28 | 29 | GetBoolean = function(self) 30 | return self.value 31 | end, 32 | 33 | GetInteger = function(self) 34 | return self.value 35 | end, 36 | 37 | GetDouble = function(self) 38 | return self.value 39 | end, 40 | 41 | GetString = function(self) 42 | return self.value 43 | end, 44 | } 45 | 46 | __cellMetatable.__index = __cellMetatable 47 | 48 | function __cellMetatable:Type() 49 | return self.type 50 | end 51 | 52 | local function Cell(rowNum, colNum, value, type, formula) 53 | return setmetatable({ 54 | row = tonumber(rowNum), 55 | column = colNum, 56 | value = value, 57 | type = type or __cellMetatable.UNDEFINED, 58 | formula = formula, 59 | }, __cellMetatable) 60 | end 61 | 62 | local __colTypeTranslator = { 63 | b = __cellMetatable.BOOLEAN, 64 | s = __cellMetatable.STRING, 65 | } 66 | 67 | local __sheetMetatable = { 68 | __load = function(self) 69 | local sheetDoc = _xlsx_readdocument(self.workbook, ("xl/worksheets/sheet%d.xml"):format(self.id)) 70 | local sheetData = sheetDoc.worksheet[1]['#'].sheetData 71 | local rows = {} 72 | local columns = {} 73 | if sheetData[1]['#'].row then 74 | for _, rowNode in ipairs(sheetData[1]['#'].row) do 75 | local rowNum = tonumber(rowNode['@'].r) 76 | if not rows[rowNum] then 77 | rows[rowNum] = {} 78 | end 79 | if rowNode['#'].c then 80 | for _, columnNode in ipairs(rowNode['#'].c) do 81 | -- Generate the proper column index. 82 | local cellId = columnNode['@'].r 83 | local colLetters = cellId:match(colRowPattern) 84 | local colNum = 0 85 | if colLetters then 86 | local index = 1 87 | repeat 88 | colNum = colNum * 26 89 | colNum = colNum + colLetters:byte(index) - ('A'):byte(1) + 1 90 | index = index + 1 91 | until index > #colLetters 92 | end 93 | 94 | local colType = columnNode['@'].t 95 | 96 | local data 97 | if columnNode['#'].v then 98 | data = columnNode['#'].v[1]['#'] 99 | if colType == 's' then 100 | colType = __cellMetatable.STRING 101 | data = self.workbook.sharedStrings[tonumber(data) + 1] 102 | elseif colType == 'str' then 103 | colType = __cellMetatable.STRING 104 | elseif colType == 'b' then 105 | colType = __cellMetatable.BOOLEAN 106 | data = data == '1' 107 | else 108 | local cellS = tonumber(columnNode['@'].s) 109 | local numberStyle = self.workbook.styles.cellXfs[cellS - 1].numFmtId 110 | if not numberStyle then 111 | numberStyle = 0 112 | end 113 | if numberStyle == 0 or numberStyle == 1 then 114 | colType = __cellMetatable.INT 115 | else 116 | colType = __cellMetatable.DOUBLE 117 | end 118 | data = tonumber(data) 119 | end 120 | 121 | --local formula 122 | --if columnNode['#'].f then 123 | --assert() 124 | --end 125 | 126 | else 127 | colType = __colTypeTranslator[colType] 128 | end 129 | 130 | if not columns[colNum] then 131 | columns[colNum] = {} 132 | end 133 | local cell = Cell(rowNum, colNum, data, colType, formula) 134 | table.insert(rows[rowNum], cell) 135 | table.insert(columns[colNum], cell) 136 | self.__cells[cellId] = cell 137 | end 138 | end 139 | end 140 | end 141 | self.__rows = rows 142 | self.__cols = columns 143 | self.loaded = true 144 | end, 145 | 146 | rows = function(self) 147 | if not self.loaded then 148 | self.__load() 149 | end 150 | return self.__rows 151 | end, 152 | 153 | cols = function(self) 154 | if not self.loaded then 155 | self.__load() 156 | end 157 | return self.__cols 158 | end, 159 | 160 | GetAnsiSheetName = function(self) 161 | return self.name 162 | end, 163 | 164 | GetUnicodeSheetName = function(self) 165 | return self.name 166 | end, 167 | 168 | GetSheetName = function(self) 169 | return self.name 170 | end, 171 | 172 | GetTotalRows = function(self) 173 | return #self.__rows 174 | end, 175 | 176 | GetTotalCols = function(self) 177 | return #self.__cols 178 | end, 179 | 180 | Cell = function(self, row, col) 181 | local key = '' 182 | local extraColIndex = math.floor(col / 26) 183 | if extraColIndex > 0 then 184 | key = string.char(string.byte('A') + (extraColIndex - 1)) 185 | end 186 | key = key .. string.char(string.byte('A') + (col % 26)) 187 | key = key .. (row + 1) 188 | return self.__cells[key] 189 | end, 190 | 191 | __tostring = function(self) 192 | return "xlsx.Sheet " .. self.name 193 | end 194 | } 195 | 196 | __sheetMetatable.__index = function(self, key) 197 | local value = __sheetMetatable[key] 198 | if value then return value end 199 | return self.__cells[key] 200 | end 201 | 202 | 203 | function Sheet(workbook, id, name) 204 | local self = {} 205 | self.workbook = workbook 206 | self.id = id 207 | self.name = name 208 | self.loaded = false 209 | self.__cells = {} 210 | self.__cols = {} 211 | self.__rows = {} 212 | setmetatable(self, __sheetMetatable) 213 | return self 214 | end 215 | 216 | 217 | local __workbookMetatableMembers = { 218 | GetTotalWorksheets = function(self) 219 | return #self.__sheets 220 | end, 221 | 222 | GetWorksheet = function(self, key) 223 | return self.__sheets[key] 224 | end, 225 | 226 | GetAnsiSheetName = function(self, key) 227 | return self:GetWorksheet(key).name 228 | end, 229 | 230 | GetUnicodeSheetName = function(self, key) 231 | return self:GetWorksheet(key).name 232 | end, 233 | 234 | GetSheetName = function(self, key) 235 | return self:GetWorksheet(key).name 236 | end, 237 | 238 | Sheets = function(self) 239 | local i = 0 240 | return function() 241 | i = i + 1 242 | return self.__sheets[i] 243 | end 244 | end 245 | } 246 | 247 | 248 | local __workbookMetatable = { 249 | __len = function(self) 250 | return #self.__sheets 251 | end, 252 | 253 | __index = function(self, key) 254 | local value = __workbookMetatableMembers[key] 255 | if value then return value end 256 | return self.__sheets[key] 257 | end 258 | } 259 | 260 | 261 | function M.Workbook(filename) 262 | local self = {} 263 | 264 | self.archive = ziparchive.open(filename) 265 | 266 | local sharedStringsXml = _xlsx_readdocument(self, 'xl/sharedstrings.xml') 267 | self.sharedStrings = {} 268 | if sharedStringsXml then 269 | for _, str in ipairs(sharedStringsXml.sst[1]['#'].si) do 270 | if str['#'].r then 271 | local concatenatedString = {} 272 | for _, rstr in ipairs(str['#'].r) do 273 | local t = rstr['#'].t[1]['#'] 274 | if type(t) == 'string' then 275 | concatenatedString[#concatenatedString + 1] = rstr['#'].t[1]['#'] 276 | end 277 | end 278 | concatenatedString = table.concat(concatenatedString) 279 | self.sharedStrings[#self.sharedStrings + 1] = concatenatedString 280 | else 281 | self.sharedStrings[#self.sharedStrings + 1] = str['#'].t[1]['#'] 282 | end 283 | end 284 | end 285 | 286 | local stylesXml = _xlsx_readdocument(self, 'xl/styles.xml') 287 | self.styles = {} 288 | local cellXfs = {} 289 | self.styles.cellXfs = cellXfs 290 | if stylesXml then 291 | for _, xfXml in ipairs(stylesXml.styleSheet[1]['#'].cellXfs[1]['#'].xf) do 292 | local xf = {} 293 | local numFmtId = xfXml['@'].numFmtId 294 | if numFmtId then 295 | xf.numFmtId = tonumber(numFmtId) 296 | end 297 | cellXfs[#cellXfs + 1] = xf 298 | end 299 | end 300 | 301 | self.workbookDoc = _xlsx_readdocument(self, 'xl/workbook.xml') 302 | local sheets = self.workbookDoc.workbook[1]['#'].sheets 303 | self.__sheets = {} 304 | local id = 1 305 | for _, sheetNode in ipairs(sheets[1]['#'].sheet) do 306 | local name = sheetNode['@'].name 307 | local sheet = Sheet(self, id, name) 308 | sheet:__load() 309 | self.__sheets[id] = sheet 310 | self.__sheets[name] = sheet 311 | id = id + 1 312 | end 313 | setmetatable(self, __workbookMetatable) 314 | return self 315 | end 316 | 317 | return M 318 | 319 | --[[ 320 | xlsx = M 321 | 322 | local workbook = xlsx.Workbook('Book1.xlsx') 323 | print("Book") 324 | local sheet = workbook[1] 325 | print(sheet:Cell(0, 52)) 326 | cell = sheet:Cell(0, 52) 327 | print(cell.value) 328 | print(cell:Get()) 329 | print(workbook:GetTotalWorksheets()) 330 | print(sheet:rows()) 331 | print(sheet:cols()) 332 | print(sheet.B1) 333 | 334 | 335 | --]] 336 | 337 | -- vim: set tabstop=4 expandtab: 338 | --------------------------------------------------------------------------------