*! _tabtools_xlsx_deferred_styles Version 1.0.0 2026/10/20
*! Apply queued cell style rules directly to a closed xlsx workbook's XML
*! Author: Timothy P Copeland, Karolinska Institutet
*! Program class: nclass
* Stata's xl() never reuses a style record: every ranged set_font(),
* set_font_bold(), set_fill_pattern(), set_*_border() or alignment call appends
* records for each cell it touches. Styling a large table therefore costs time
* in proportion to the number of cells and, past a few thousand rows, overflows
* the workbook's style pools, which xl() reports as a misleading
* "invalid alignment format" / "invalid font name" error.
*
* _tabtools_xlsx_apply_styles, defer queues the cell-level rules here instead.
* After close_book(), _tabtools_xlsx_compact_styles unpacks the workbook and
* calls _tt_xlsx_pend_apply(): each cell's final style is composed from the
* queued rules in order (later rules win per attribute, as successive xl()
* calls do), each DISTINCT style is written to styles.xml once, and each styled
* cell gets its s= index. Row heights, column widths and merges never enter
* the queue; they are still applied through xl() at rule time.
*
*
* Mata compiled while an ado-file autoloads is private to that file, so every
* other file reaches this code through the subcommands below:
* count using file, local(name) queued rule count for file -> c_local
* apply using file, root(dir) write the queue into the unzipped workbook
* legacy using file apply the queue through xl() (fallback)
* clear drop the queue
program define _tabtools_xlsx_deferred_styles, nclass
version 17.0
local _orig_varabbrev = c(varabbrev)
set varabbrev off
capture noisily {
gettoken sub 0 : 0, parse(" ,")
if "`sub'" == "count" {
syntax using/ , Local(name)
mata: st_local("_n", strofreal(_tt_xlsx_pend_count(`"`using'"')))
c_local `local' `_n'
}
else if "`sub'" == "apply" {
syntax using/ , ROOT(string)
mata: _tt_xlsx_pend_apply(`"`root'"', `"`using'"')
}
else if "`sub'" == "legacy" {
syntax using/
mata: st_local("_n", strofreal(_tt_xlsx_pend_count(`"`using'"')))
if `_n' > 0 {
tempname bk mrules
mata: _tt_xlsx_pend_groups(`"`using'"', "`mrules'")
mata: `bk' = xl()
capture noisily {
mata: `bk'.load_book(`"`using'"')
mata: `bk'.set_mode("open")
forvalues g = 1/`_ngroups' {
mata: `bk'.set_sheet(`"`_gsheet`g''"')
_tabtools_xlsx_apply_styles, book(`bk') sheet(`"`_gsheet`g''"') ///
rules(`mrules'`g') font(`"`_gfont`g''"') altfont(`"`_galt`g''"') ///
color1(`"`_gc1`g''"') color2(`"`_gc2`g''"') ///
color3(`"`_gc3`g''"') color4(`"`_gc4`g''"')
}
}
local rc = _rc
* stata-dev-ignore: capture-rc — best-effort cleanup after local rc = _rc saved the apply-styles status; a failed close must not replace it, and the next line exits with rc
capture mata: `bk'.close_book()
capture mata: mata drop `bk'
forvalues g = 1/`_ngroups' {
capture matrix drop `mrules'`g'
}
if `rc' exit `rc'
}
}
else if "`sub'" == "clear" {
mata: (void) rmexternal("_tt_xp_rules")
mata: (void) rmexternal("_tt_xp_meta")
}
else {
display as error "_tabtools_xlsx_deferred_styles: unknown subcommand `sub'"
exit 198
}
}
local rc = _rc
set varabbrev `_orig_varabbrev'
if `rc' exit `rc'
end
version 17.0
capture mata: mata drop _tt_xlsx_pend_count()
capture mata: mata drop _tt_xlsx_pend_groups()
capture mata: mata drop _tt_xlsx_pend_apply()
capture mata: mata drop _tt_xlsx_pend_sheet()
capture mata: mata drop _tt_xlsx_pend_part()
capture mata: mata drop _tt_xlsx_pend_attr()
capture mata: mata drop _tt_xlsx_pend_setattr()
capture mata: mata drop _tt_xlsx_pend_colnum()
capture mata: mata drop _tt_xlsx_pend_colname()
capture mata: mata drop _tt_xlsx_pend_hex()
capture mata: mata drop _tt_xlsx_pend_named()
capture mata: mata drop _tt_xlsx_pend_esc()
capture mata: mata drop _tt_xlsx_pend_unesc()
capture mata: mata drop _tt_xlsx_pend_id()
capture mata: mata drop _tt_xlsx_pend_pool()
capture mata: mata drop _tt_xlsx_pend_setpool()
capture mata: mata drop _tt_xlsx_pend_read()
capture mata: mata drop _tt_xlsx_pend_write()
local _tt_ms0 = c(matastrict)
mata:
mata set matastrict on
real scalar _tt_xlsx_pend_count(string scalar file)
{
pointer(string matrix) scalar mp
if ((mp = findexternal("_tt_xp_meta")) == NULL) return(0)
if (rows(*mp) == 0) return(0)
return(sum((*mp)[., 1] :== file))
}
// Group the queue for `file' into runs that share sheet, font and colors, and
// hand each run to Stata as a rules matrix for the xl() fallback.
void _tt_xlsx_pend_groups(string scalar file, string scalar mprefix)
{
real matrix R
string matrix M
real scalar i, g, start
string scalar gs
R = *findexternal("_tt_xp_rules")
M = *findexternal("_tt_xp_meta")
R = select(R, M[., 1] :== file)
M = select(M, M[., 1] :== file)
g = 0
start = 1
for (i = 1; i <= rows(R); i++) {
if (i < rows(R)) {
if (M[i + 1, (2..8)] == M[i, (2..8)]) continue
}
g = g + 1
gs = strofreal(g)
st_matrix(mprefix + gs, R[(start::i), .])
st_local("_gsheet" + gs, M[i, 2])
st_local("_gfont" + gs, M[i, 3])
st_local("_galt" + gs, M[i, 4])
st_local("_gc1" + gs, M[i, 5])
st_local("_gc2" + gs, M[i, 6])
st_local("_gc3" + gs, M[i, 7])
st_local("_gc4" + gs, M[i, 8])
start = i + 1
}
st_local("_ngroups", strofreal(g))
}
// ---- direct XML application ---------------------------------------------
// Apply every queued rule for the file to the workbook unpacked under root.
void _tt_xlsx_pend_apply(string scalar root, string scalar file)
{
real matrix R
string matrix M
string colvector sheets
string scalar styles
real scalar i
if (_tt_xlsx_pend_count(file) == 0) return
// QA hook: lets the suite prove the xl() fallback keeps the formatting.
if (st_global("TABTOOLS_QA_FORCE_FASTSTYLE_FAIL") == "1") {
errprintf("deferred styles: forced failure (QA)\n")
_error(498)
}
R = *findexternal("_tt_xp_rules")
M = *findexternal("_tt_xp_meta")
R = select(R, M[., 1] :== file)
M = select(M, M[., 1] :== file)
styles =_tt_xlsx_pend_read(root + "/xl/styles.xml")
sheets = uniqrows(M[., 2])
for (i = 1; i <= rows(sheets); i++) {
styles = _tt_xlsx_pend_sheet(root, sheets[i],
select(R, M[., 2] :== sheets[i]),
select(M, M[., 2] :== sheets[i]), styles)
}
_tt_xlsx_pend_write(root + "/xl/styles.xml", styles)
}
// Compose, register and assign the styles of one sheet; returns styles.xml.
string scalar _tt_xlsx_pend_sheet(string scalar root, string scalar sheet,
real matrix R, string matrix M, string scalar styles)
{
transmorphic scalar fontids, colorids
string colvector fontnames, colorhex
real matrix A, K, U
real colvector idx, touched, T, o, gid, style_of, rnum, keep
real rowvector rv, cv
real scalar nR, nC, i, j, g, G, op, k, nfont, nfill, nborder, nxf, v
real scalar fid, flid, bid, p, q, r, c, cnum, rowtouched, maxr, maxc, nrow
string scalar path, sh, head, tail, body, fonts, fills, borders, xfs, x
string scalar ha, va, cellxml, opentag, content, newrow, ref, dim
string rowvector rparts, cparts
string colvector rxml, cells
real colvector ccol
real rowvector span
path = _tt_xlsx_pend_part(root, sheet)
if (path == "") {
errprintf("deferred styles: sheet %s not found in workbook\n", sheet)
_error(198)
}
// --- 1. overlay the rules on an attribute grid ---
// columns: 1 font name id, 2 size, 3 bold, 4 italic, 5 wrap, 6 halign,
// 7 valign, 8 fill color id, 9 top, 10 bottom, 11 left, 12 right border,
// 13 font color id. Missing = never set by any rule.
nR = max(R[., 3])
nC = max(R[., 5])
A = J(nR * nC, 13, .)
fontids = asarray_create("string", 1)
colorids = asarray_create("string", 1)
fontnames = J(0, 1, "")
colorhex = J(0, 1, "")
for (i = 1; i <= rows(R); i++) {
op = R[i, 1]
rv = (R[i, 2]::R[i, 3])'
cv = (R[i, 4]..R[i, 5])
// cell index (r - 1) * nC + c over the rectangle, as an outer sum
idx = vec(((rv' :- 1) * nC) * J(1, cols(cv), 1) + J(cols(rv), 1, 1) * cv)
if (op == 1 | op == 15) {
x = M[i, 9]
A[idx, 1] = J(rows(idx), 1, _tt_xlsx_pend_id(fontids, fontnames, x))
A[idx, 2] = J(rows(idx), 1, R[i, 6])
if (op == 15) {
x = _tt_xlsx_pend_hex(M[i, 10])
A[idx, 13] = J(rows(idx), 1, _tt_xlsx_pend_id(colorids, colorhex, x))
}
}
else if (op >= 2 & op <= 6) {
A[idx, op + 1] = J(rows(idx), 1, R[i, 7])
}
else if (op == 7) {
x = _tt_xlsx_pend_hex(M[i, 10])
A[idx, 8] = J(rows(idx), 1, _tt_xlsx_pend_id(colorids, colorhex, x))
}
else if (op >= 8 & op <= 11) {
A[idx, op + 1] = J(rows(idx), 1, R[i, 7])
}
}
touched = rowmissing(A) :< 13
T = selectindex(touched)
if (rows(T) == 0) return(styles)
// --- 2. distinct styles ---
K = editmissing(A[T, .], -1)
o = order(K, (1..13))
gid = J(rows(T), 1, .)
G = 0
for (i = 1; i <= rows(o); i++) {
if (i == 1) G = 1
else if (K[o[i], .] != K[o[i - 1], .]) G = G + 1
gid[o[i]] = G
}
U = J(G, 13, .)
for (i = 1; i <= rows(T); i++) U[gid[i], .] = K[i, .]
// --- 3. register one font/fill/border/xf per distinct style ---
nfont = _tt_xlsx_pend_pool(styles, "fonts")
nfill = _tt_xlsx_pend_pool(styles, "fills")
nborder = _tt_xlsx_pend_pool(styles, "borders")
nxf = _tt_xlsx_pend_pool(styles, "cellXfs")
if (nfont < 1 | nfill < 1 | nborder < 1 | nxf < 1) {
errprintf("deferred styles: styles.xml has no base style pools\n")
_error(198)
}
fonts = fills = borders = xfs = ""
flid = nfill
for (g = 1; g <= G; g++) {
// font: schema order b, i, sz, color, name
x = ""
if (U[g, 3] == 1) x = x + ""
if (U[g, 4] == 1) x = x + ""
x = x + `""'
if (U[g, 13] >= 1) x = x + `""'
x = x + `""'
fonts = fonts + x
fid = nfont + g - 1
if (U[g, 8] >= 1) {
fills = fills + `""'
v = flid
flid = flid + 1
}
else v = 0
x = ""
for (k = 1; k <= 4; k++) {
// xml order left, right, top, bottom; grid columns 11, 12, 9, 10
j = (11, 12, 9, 10)[k]
ref = ("left", "right", "top", "bottom")[k]
if (U[g, j] >= 1 & U[g, j] <= 3) {
x = x + "<" + ref + `" style=""' + ("thin", "medium", "thick")[U[g, j]] + `""/>"'
}
else x = x + "<" + ref + "/>"
}
borders = borders + x + ""
bid = nborder + g - 1
ha = U[g, 6] >= 1 ? ("left", "center", "right")[U[g, 6]] : "general"
va = U[g, 7] >= 1 ? ("bottom", "center", "top")[U[g, 7]] : "bottom"
xfs = xfs + `""'
}
styles = _tt_xlsx_pend_setpool(styles, "fonts", nfont + G, fonts)
if (flid > nfill) styles = _tt_xlsx_pend_setpool(styles, "fills", flid, fills)
styles = _tt_xlsx_pend_setpool(styles, "borders", nborder + G, borders)
styles = _tt_xlsx_pend_setpool(styles, "cellXfs", nxf + G, xfs)
// style index for every grid cell (missing = untouched)
style_of = J(nR * nC, 1, .)
style_of[T] = nxf :+ gid :- 1
// --- 4. rewrite sheetData ---
sh = _tt_xlsx_pend_read(path)
p = strpos(sh, "")
if (p == 0) {
p = strpos(sh, "")
if (p == 0) {
errprintf("deferred styles: sheet %s has no sheetData\n", sheet)
_error(198)
}
head = substr(sh, 1, p - 1)
tail = substr(sh, p + strlen(""), .)
body = ""
}
else {
head = substr(sh, 1, p - 1)
q = strpos(sh, "")
body = substr(sh, p + strlen(""), q - p - strlen(""))
tail = substr(sh, q + strlen(""), .)
}
rparts = ustrsplit(body, "= 1 & r <= nR)
if (rowtouched) rowtouched = any(style_of[((r - 1) * nC + 1)::(r * nC)] :< .)
if (!rowtouched) {
nrow = nrow + 1
rnum[nrow] = r
rxml[nrow] = x
continue
}
keep[r] = 1
q = strpos(x, ">")
if (substr(x, q - 1, 1) == "/") {
opentag = substr(x, 1, q - 2) + ">"
content = ""
}
else {
opentag = substr(x, 1, q)
content = substr(x, q + 1, strlen(x) - q - strlen("
"))
}
cparts = ustrsplit(content, "= 1 & cnum <= nC) {
v = style_of[(r - 1) * nC + cnum]
if (v < .) cellxml = _tt_xlsx_pend_setattr(cellxml, "s", strofreal(v))
}
cells = cells \ cellxml
ccol = ccol \ cnum
}
// styled cells that xl() never wrote get an empty styled cell
for (c = 1; c <= nC; c++) {
v = style_of[(r - 1) * nC + c]
if (v >= .) continue
if (any(ccol :== c)) continue
cells = cells \ (`""')
ccol = ccol \ c
}
o = order(ccol, 1)
newrow = opentag + invtokens(cells[o]', "") + ""
nrow = nrow + 1
rnum[nrow] = r
rxml[nrow] = newrow
}
// styled rows that xl() never wrote
for (r = 1; r <= nR; r++) {
if (keep[r]) continue
idx = style_of[((r - 1) * nC + 1)::(r * nC)]
if (!any(idx :< .)) continue
newrow = `""'
for (c = 1; c <= nC; c++) {
if (idx[c] >= .) continue
newrow = newrow + `""'
}
nrow = nrow + 1
rnum[nrow] = r
rxml[nrow] = newrow + "
"
}
if (nrow > 0) {
rnum = rnum[(1::nrow)]
rxml = rxml[(1::nrow)]
o = order(rnum, 1)
body = invtokens(rxml[o]', "")
}
else body = ""
// keep covering every styled cell
maxr = nrow > 0 ? max(rnum) : 1
maxc = nC
span = (strpos(head, " 0) {
x = substr(dim, p + 1, .)
c = _tt_xlsx_pend_colnum(x)
r = strtoreal(substr(x, strlen(_tt_xlsx_pend_colname(c)) + 1, .))
if (c > maxc) maxc = c
if (r > maxr) maxr = r
head = subinstr(head, `"" + body + "" + tail)
return(styles)
}
// worksheet part path for a sheet name, via workbook.xml and its rels
string scalar _tt_xlsx_pend_part(string scalar root, string scalar sheet)
{
string scalar wb, rels, rid, target
string rowvector parts
real scalar i
wb = _tt_xlsx_pend_read(root + "/xl/workbook.xml")
rels = _tt_xlsx_pend_read(root + "/xl/_rels/workbook.xml.rels")
rid = ""
parts = ustrsplit(wb, "")
if (e == 0) e = strlen(x)
key = " " + a + "=" + char(34)
p = strpos(substr(x, 1, e), key)
if (p == 0) return("")
p = p + strlen(key)
q = strpos(substr(x, p, .), char(34))
if (q == 0) return("")
return(substr(x, p, q - 1))
}
// set (or add) attribute a on a cell's opening tag
string scalar _tt_xlsx_pend_setattr(string scalar x, string scalar a, string scalar v)
{
real scalar e, p, q
string scalar key, tagpart
e = strpos(x, ">")
tagpart = substr(x, 1, e)
key = " " + a + "=" + char(34)
p = strpos(tagpart, key)
if (p > 0) {
q = strpos(substr(x, p + strlen(key), .), char(34))
return(substr(x, 1, p - 1) + key + v + char(34) + substr(x, p + strlen(key) + q, .))
}
// insert right after r="..."
p = strpos(tagpart, " r=" + char(34))
if (p == 0) return(substr(x, 1, 2) + key + v + char(34) + substr(x, 3, .))
q = strpos(substr(x, p + 4, .), char(34))
p = p + 4 + q
return(substr(x, 1, p - 1) + key + v + char(34) + substr(x, p, .))
}
real scalar _tt_xlsx_pend_colnum(string scalar ref)
{
real scalar i, n, ch
n = 0
for (i = 1; i <= strlen(ref); i++) {
ch = ascii(substr(ref, i, 1))
if (ch < 65 | ch > 90) break
n = n * 26 + (ch - 64)
}
return(n)
}
string scalar _tt_xlsx_pend_colname(real scalar cin)
{
real scalar c
string scalar s
c = cin
s = ""
while (c > 0) {
s = char(65 + mod(c - 1, 26)) + s
c = floor((c - 1) / 26)
}
return(s)
}
// "R G B" or a Stata color name -> "RRGGBB"
string scalar _tt_xlsx_pend_hex(string scalar color)
{
string rowvector t
real scalar i, v
string scalar out, h
t = tokens(color)
if (cols(t) == 1) t = tokens(_tt_xlsx_pend_named(t[1]))
// stata-dev-ignore: shape-dispatch — t holds the whitespace tokens of one color string; three tokens (R G B) is the validity test of that string, not a matrix layout dispatch
if (cols(t) != 3) {
errprintf("deferred styles: cannot resolve color %s\n", color)
_error(198)
}
out = ""
for (i = 1; i <= 3; i++) {
v = strtoreal(t[i])
if (v >= . | v < 0 | v > 255 | v != floor(v)) {
errprintf("deferred styles: invalid color %s\n", color)
_error(198)
}
h = inbase(16, v)
if (strlen(h) == 1) h = "0" + h
out = out + h
}
return(strupper(out))
}
// Color name -> "R G B". xl() understands the CSS/HTML color names, so the
// names it accepts map to exactly the RGB xl() writes (probed 2026-09-29).
// The remaining Stata color names that tabtools' validator accepts are
// rejected by xl() with r(16136); they resolve through Stata's own
// color-.style definitions instead of failing the export.
string scalar _tt_xlsx_pend_named(string scalar name)
{
string matrix css
string scalar fn, line, n
real scalar fh, p, i
css = J(23, 2, "")
css[1, .] = ("black", "0 0 0")
css[2, .] = ("blue", "0 0 255")
css[3, .] = ("brown", "165 42 42")
css[4, .] = ("cyan", "0 255 255")
css[5, .] = ("dimgray", "105 105 105")
css[6, .] = ("gold", "255 215 0")
css[7, .] = ("gray", "128 128 128")
css[8, .] = ("green", "0 128 0")
css[9, .] = ("khaki", "240 230 140")
css[10, .] = ("lavender", "230 230 250")
css[11, .] = ("lime", "0 255 0")
css[12, .] = ("magenta", "255 0 255")
css[13, .] = ("maroon", "128 0 0")
css[14, .] = ("navy", "0 0 128")
css[15, .] = ("olive", "128 128 0")
css[16, .] = ("orange", "255 165 0")
css[17, .] = ("pink", "255 192 203")
css[18, .] = ("purple", "128 0 128")
css[19, .] = ("red", "255 0 0")
css[20, .] = ("sienna", "160 82 45")
css[21, .] = ("teal", "0 128 128")
css[22, .] = ("white", "255 255 255")
css[23, .] = ("yellow", "255 255 0")
n = strlower(name)
for (i = 1; i <= rows(css); i++) {
if (css[i, 1] == n) return(css[i, 2])
}
fn = findfile("color-" + n + ".style")
if (fn == "") return("")
fh = fopen(fn, "r")
while ((line = fget(fh)) != J(0, 0, "")) {
line = strtrim(line)
if (substr(line, 1, 7) == "set rgb") {
fclose(fh)
p = strpos(line, char(34))
line = substr(line, p + 1, .)
return(substr(line, 1, strpos(line, char(34)) - 1))
}
}
fclose(fh)
return("")
}
string scalar _tt_xlsx_pend_esc(string scalar s)
{
string scalar out
out = subinstr(s, "&", "&", .)
out = subinstr(out, "<", "<", .)
out = subinstr(out, ">", ">", .)
out = subinstr(out, char(34), """, .)
return(out)
}
string scalar _tt_xlsx_pend_unesc(string scalar s)
{
string scalar out
out = subinstr(s, """, char(34), .)
out = subinstr(out, "'", "'", .)
out = subinstr(out, "<", "<", .)
out = subinstr(out, ">", ">", .)
out = subinstr(out, "&", "&", .)
return(out)
}
real scalar _tt_xlsx_pend_id(transmorphic scalar ids, string colvector names,
string scalar key)
{
if (asarray_contains(ids, key)) return(asarray(ids, key))
names = names \ key
asarray(ids, key, rows(names))
return(rows(names))
}
// count= of a style pool (0 when absent)
real scalar _tt_xlsx_pend_pool(string scalar styles, string scalar tag)
{
real scalar p
string scalar x
p = strpos(styles, "<" + tag + " ")
if (p == 0) return(0)
x = substr(styles, p, strpos(substr(styles, p, .), ">"))
return(strtoreal(_tt_xlsx_pend_attr(x, "count")))
}
string scalar _tt_xlsx_pend_setpool(string scalar styles, string scalar tag,
real scalar n, string scalar add)
{
real scalar p, e, q
string scalar open, close
p = strpos(styles, "<" + tag + " ")
e = p + strpos(substr(styles, p, .), ">") - 1
open = substr(styles, p, e - p + 1)
close = "" + tag + ">"
q = strpos(styles, close)
if (p == 0 | q == 0 | substr(open, strlen(open) - 1, 1) == "/") {
errprintf("deferred styles: cannot extend %s pool\n", tag)
_error(198)
}
open = subinstr(open, `" count=""' + _tt_xlsx_pend_attr(open, "count") + `"""',
`" count=""' + ustrregexra(strofreal(n, "%18.0f"), "-(?=[0.,]+\z)", "") + `"""', 1)
return(substr(styles, 1, p - 1) + open + substr(styles, e + 1, q - e - 1) +
add + substr(styles, q, .))
}
string scalar _tt_xlsx_pend_read(string scalar path)
{
real scalar fh, n
string scalar s
fh = fopen(path, "r")
fseek(fh, 0, 1)
n = ftell(fh)
fseek(fh, 0, -1)
s = n > 0 ? fread(fh, n) : ""
fclose(fh)
return(s)
}
void _tt_xlsx_pend_write(string scalar path, string scalar s)
{
real scalar fh
unlink(path)
fh = fopen(path, "w")
fwrite(fh, s)
fclose(fh)
}
end
mata: mata set matastrict `_tt_ms0'