cat / 012
clone

a spreadsheet for the terminal: lotus 1-2-3 looks, google sheets keys and formulas, one go binary

main
012 / internal / fileio / xlsxlines.go
xlsxlines.go 4.2K
1package fileio23import (4 "bufio"5 "fmt"6 "maps"7 "slices"89 "src.rest/cat/012/internal/sheet"10)1112// Column and row styles: Excel keeps a style for whole columns (in13// <cols>) and rows (a <row>'s s, with customFormat), as 012 keeps line14// formats; both travel both ways. Excel has no style for the whole15// sheet: 012's goes out on every column, and a style on both A and XFD16// comes back as the sheet's.1718// LineFormat is the format and style of a whole column or row.19type LineFormat struct {20 Format sheet.Format21 Style sheet.Style22}2324// snapLines copies a sheet's column (row false) or row formats, the25// sheet's own format on every column.26func snapLines(s *sheet.Sheet, row bool) map[int]LineFormat {27 fs, ss := s.ColFormats(), s.ColStyles()28 if row {29 fs, ss = s.RowFormats(), s.RowStyles()30 }31 out := map[int]LineFormat{}32 if f, st := s.SheetFormat(); !row && (!f.IsZero() || !st.IsZero()) {33 for c := range sheet.MaxCols {34 out[c] = LineFormat{f, st}35 }36 }37 for n, f := range fs {38 l := out[n]39 l.Format = f40 out[n] = l41 }42 for n, st := range ss {43 l := out[n]44 l.Style = st45 out[n] = l46 }47 return out48}4950// loadColStyles gives the builder's sheet the styles of its columns, as51// styleOf (by column from 0) reports them: the sheet's when A and XFD52// have the same one, and those of the first width columns (at least53// 256) that differ from it.54func loadColStyles(b *builder, width int, styleOf func(col int) (xlsxStyle, bool)) {55 var whole xlsxStyle56 if first, ok := styleOf(0); ok {57 if last, _ := styleOf(sheet.MaxCols - 1); last == first {58 whole = first59 b.s.LoadLineFormat(false, -1, first.format, first.style)60 }61 }62 for c := range min(max(width, 256), sheet.MaxCols) {63 if st, ok := styleOf(c); ok && st != whole {64 b.s.LoadLineFormat(false, c, st.format, st.style)65 }66 }67}6869// loadRowStyle gives row (from 0) of the builder's sheet its style.70func loadRowStyle(b *builder, row int, st xlsxStyle) {71 if st != (xlsxStyle{}) && row >= 0 && row < sheet.MaxRows {72 b.s.LoadLineFormat(true, row, st.format, st.style)73 }74}7576// colSpec is what <cols> says of a column: its width (0 for the77// default) and style.78type colSpec struct {79 width int80 style int81}8283// writeCols writes <cols>: the columns with a width or a format, runs84// of columns alike as one <col>.85func (w *xlsxWriter) writeCols(bw *bufio.Writer, snap *Snapshot) {86 specs := map[int]colSpec{}87 for c, width := range snap.Widths {88 specs[c] = colSpec{width: max(width-excelPadding, 1)}89 }90 for c, l := range snap.ColFormats {91 sp := specs[c]92 sp.style = w.styles.id(l.Format, l.Style)93 specs[c] = sp94 }95 if len(specs) == 0 {96 return97 }98 bw.WriteString(`<cols>`)99 cs := slices.Sorted(maps.Keys(specs))100 for i := 0; i < len(cs); {101 j := i + 1102 for j < len(cs) && cs[j] == cs[j-1]+1 && specs[cs[j]] == specs[cs[i]] {103 j++104 }105 sp := specs[cs[i]]106 fmt.Fprintf(bw, `<col min="%d" max="%d"`, cs[i]+1, cs[j-1]+1)107 if sp.width > 0 {108 fmt.Fprintf(bw, ` width="%d" customWidth="1"`, sp.width)109 }110 if sp.style > 0 {111 fmt.Fprintf(bw, ` style="%d"`, sp.style)112 }113 bw.WriteString(`/>`)114 i = j115 }116 bw.WriteString(`</cols>`)117}118119// lineRows are the rows with a style or a height of their own, in120// order.121func lineRows(snap *Snapshot) []int {122 rows := slices.Collect(maps.Keys(snap.RowFormats))123 for r := range snap.Heights {124 if _, styled := snap.RowFormats[r]; !styled {125 rows = append(rows, r)126 }127 }128 slices.Sort(rows)129 return rows130}131132// rowStart writes a <row> start tag, with the row's style and height if133// it has them.134func (w *xlsxWriter) rowStart(bw *bufio.Writer, snap *Snapshot, row int) {135 fmt.Fprintf(bw, `<row r="%d"`, row+1)136 if l, ok := snap.RowFormats[row]; ok {137 fmt.Fprintf(bw, ` s="%d" customFormat="1"`, w.styles.id(l.Format, l.Style))138 }139 if h, ok := snap.Heights[row]; ok {140 fmt.Fprintf(bw, ` ht="%d" customHeight="1"`, h*pointsPerLine)141 }142 if snap.HiddenRows[row] {143 bw.WriteString(` hidden="1"`)144 }145 bw.WriteString(`>`)146}147148// styledRows writes, as empty rows, the rows of lineRows (ascending)149// before row to, returning those left.150func (w *xlsxWriter) styledRows(bw *bufio.Writer, snap *Snapshot, rows []int, to int) []int {151 for len(rows) > 0 && rows[0] < to {152 w.rowStart(bw, snap, rows[0])153 bw.WriteString(`</row>`)154 rows = rows[1:]155 }156 return rows157}