Stats-LikeR
view release on metacpan or search on metacpan
/* Append an unsigned integer's decimal text to an SV. */
static void xlsx_cat_uint(pTHX_ SV *restrict b, unsigned long v) {
char tmp[24];
int n = snprintf(tmp, sizeof(tmp), "%lu", v);
if (n > 0) sv_catpvn(b, tmp, (STRLEN)n);
}
/* Append s (UTF-8 bytes) to out, escaping XML metacharacters and dropping the
* control characters XML 1.0 forbids (all but tab / newline / carriage-return). */
static void xlsx_xml_cat(pTHX_ SV *restrict out, const char *restrict s, STRLEN len) {
for (STRLEN i = 0; i < len; i++) {
unsigned char c = (unsigned char)s[i];
switch (c) {
case '&': SV_CATLIT(out, "&"); break;
case '<': SV_CATLIT(out, "<"); break;
case '>': SV_CATLIT(out, ">"); break;
case '"': SV_CATLIT(out, """); break;
case '\'': SV_CATLIT(out, "'"); break;
default:
if (c < 0x20 && c != '\t' && c != '\n' && c != '\r') break;
sv_catpvn(out, (const char*)&s[i], 1);
}
}
}
/* 0-based column index -> A1-style letters (A, B, ..., Z, AA, ...) appended. */
static void xlsx_col_letters(pTHX_ SV *restrict b, size_t idx) {
char tmp[16];
int n = 0;
size_t v = idx + 1; /* bijective base-26 */
while (v > 0 && n < (int)sizeof(tmp)) {
v -= 1;
tmp[n++] = (char)('A' + (int)(v % 26));
v /= 26;
}
while (n > 0) { char c = tmp[--n]; sv_catpvn(b, &c, 1); }
}
/* True when a cell should be written as an xlsx number: looks_like_number and
* made only of the characters a plain/scientific decimal uses, so "Inf"/"NaN"
* and space-padded values fall back to text and never produce an invalid <v>. */
static bool xlsx_plain_number(pTHX_ SV *restrict cell) {
if (!cell || !SvOK(cell) || !looks_like_number(cell)) return 0;
STRLEN l; const char *restrict s = SvPV(cell, l);
if (l == 0) return 0;
for (STRLEN i = 0; i < l; i++) {
char c = s[i];
if (!((c >= '0' && c <= '9') || c == '.' || c == 'e' || c == 'E'
|| c == '+' || c == '-')) return 0;
}
return 1;
}
/* Append one STORED (uncompressed) member to the ZIP under construction: 'zip'
* is the growing archive, 'cdir' accumulates its central-directory records and
* '*count' the member count. */
static void xlsx_zip_add(pTHX_ SV *restrict zip, SV *restrict cdir,
unsigned *restrict count, const char *restrict name, SV *restrict content)
{
STRLEN nlen = strlen(name);
STRLEN clen; const char *restrict cdata = SvPV(content, clen);
uint32_t crc = xlsx_crc32((const unsigned char*)cdata, (size_t)clen);
uint32_t off = (uint32_t)SvCUR(zip);
/* local file header */
zip_le32(aTHX_ zip, 0x04034b50);
zip_le16(aTHX_ zip, 20); /* version needed to extract */
zip_le16(aTHX_ zip, 0); /* general-purpose flags */
zip_le16(aTHX_ zip, 0); /* method 0 = stored */
zip_le16(aTHX_ zip, 0); /* mod time */
zip_le16(aTHX_ zip, 0x21); /* mod date = 1980-01-01 */
zip_le32(aTHX_ zip, crc);
zip_le32(aTHX_ zip, (uint32_t)clen); /* compressed size */
zip_le32(aTHX_ zip, (uint32_t)clen); /* uncompressed size */
zip_le16(aTHX_ zip, (unsigned)nlen);
zip_le16(aTHX_ zip, 0); /* extra length */
sv_catpvn(zip, name, nlen);
sv_catpvn(zip, cdata, clen);
/* central-directory header */
zip_le32(aTHX_ cdir, 0x02014b50);
zip_le16(aTHX_ cdir, 20); /* version made by */
zip_le16(aTHX_ cdir, 20); /* version needed */
zip_le16(aTHX_ cdir, 0);
zip_le16(aTHX_ cdir, 0);
zip_le16(aTHX_ cdir, 0);
zip_le16(aTHX_ cdir, 0x21);
zip_le32(aTHX_ cdir, crc);
zip_le32(aTHX_ cdir, (uint32_t)clen);
zip_le32(aTHX_ cdir, (uint32_t)clen);
zip_le16(aTHX_ cdir, (unsigned)nlen);
zip_le16(aTHX_ cdir, 0); /* extra length */
zip_le16(aTHX_ cdir, 0); /* comment length */
zip_le16(aTHX_ cdir, 0); /* disk number start */
zip_le16(aTHX_ cdir, 0); /* internal attributes */
zip_le32(aTHX_ cdir, 0); /* external attributes */
zip_le32(aTHX_ cdir, off); /* local-header offset */
sv_catpvn(cdir, name, nlen);
(*count)++;
}
/* Build a complete .xlsx from the collected rows (element 0 = header record,
* the rest data records, each an AV of SVs -- exactly what print_string_row()
* gathers for the tex path) and write it to 'file'. freeze_rows / freeze_cols
* give the number of leading rows / columns to freeze in place (0 = none). */
static void write_xlsx_workbook(pTHX_ AV *restrict rows, const char *restrict file,
const char *restrict sheet_name, SV *restrict comment,
unsigned freeze_rows, unsigned freeze_cols)
{
/* ---- worksheet ---- */
SV *restrict sheet = sv_2mortal(newSVpvs(
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>"
"<worksheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\">"));
/* Freeze panes: a <sheetViews> block, which the schema requires *before*
* <sheetData>. topLeftCell is the first cell below/right of the frozen
* region -- e.g. freezing 1 row gives "A2"; 1 row + 2 cols gives "C2". */
if (freeze_rows || freeze_cols) {
SV *restrict tl = sv_2mortal(newSVpvs(""));
xlsx_col_letters(aTHX_ tl, (size_t)freeze_cols);
xlsx_cat_uint(aTHX_ tl, (unsigned long)freeze_rows + 1);
STRLEN tll; const char *restrict tls = SvPV(tl, tll);
const char *restrict ap = (freeze_rows && freeze_cols) ? "bottomRight"
: freeze_cols ? "topRight" : "bottomLeft";
SV_CATLIT(sheet, "<sheetViews><sheetView workbookViewId=\"0\"><pane ");
if (freeze_cols) {
SV_CATLIT(sheet, "xSplit=\"");
xlsx_cat_uint(aTHX_ sheet, (unsigned long)freeze_cols);
SV_CATLIT(sheet, "\" ");
}
if (freeze_rows) {
SV_CATLIT(sheet, "ySplit=\"");
xlsx_cat_uint(aTHX_ sheet, (unsigned long)freeze_rows);
SV_CATLIT(sheet, "\" ");
}
SV_CATLIT(sheet, "topLeftCell=\"");
sv_catpvn(sheet, tls, tll);
SV_CATLIT(sheet, "\" activePane=\"");
sv_catpv(sheet, ap);
SV_CATLIT(sheet, "\" state=\"frozen\"/><selection pane=\"");
( run in 2.090 seconds using v1.01-cache-2.11-cpan-6aa56a78535 )