The styles reader checks a list's length once for every list, and the snapshot, the row import and the excelize reference importer hand their heights, merges and kept cells to helpers.
- commit
- 90892b2700b0c9094442fc786ca6c32465b5c2f0
- parent
- e80deb0d8e419ed036d6848cf68fa809369ccdf9
- author
- Dami <dami.etoile@gmail.com>
- kind
- refactor risk score 1.2 of 3, Jev's estimate
| M | internal/ | 35 | +++++++++++++-------- |
| M | internal/ | 51 | +++++++++++++++++++++--------- |
| M | internal/ | 19 | ++++++++---- |
| M | internal/ | 25 | ++++++--------- |
@@ -99,19 +99,7 @@ func Snap(s *sheet.Sheet, r sheet.Rect, name string) *Snapshot {
9999 ColFormats: snapLines(s, false), RowFormats: snapLines(s, true), Filter: s.Filter(),100100 CondFormats: s.CondFormats(), Validations: s.Validations()}101101 snap.FrozenRows, snap.FrozenCols = s.Frozen()102- for row, h := range s.Heights() {103- if row >= r.From.Row && row <= r.To.Row {104- if snap.Heights == nil {105- snap.Heights = map[int]int{}106- }107- snap.Heights[row] = h108- }109- }110- for _, m := range s.Merges() {111- if r.Contains(m.From) && r.Contains(m.To) {112- snap.Merges = append(snap.Merges, m)113- }114- }102+ snap.Heights, snap.Merges = heightsMergesIn(s, r)115103 for _, a := range s.NotesIn(notes) {116104 if snap.Notes == nil {117105 snap.Notes = map[sheet.Addr]string{}@@ -150,6 +138,27 @@ func Snap(s *sheet.Sheet, r sheet.Rect, name string) *Snapshot {
150138 return snap151139 }152140 141+// heightsMergesIn are the heights of the rows of r and the merges inside142+// it.143+func heightsMergesIn(s *sheet.Sheet, r sheet.Rect) (map[int]int, []sheet.Rect) {144+ var heights map[int]int145+ for row, h := range s.Heights() {146+ if row >= r.From.Row && row <= r.To.Row {147+ if heights == nil {148+ heights = map[int]int{}149+ }150+ heights[row] = h151+ }152+ }153+ var merges []sheet.Rect154+ for _, m := range s.Merges() {155+ if r.Contains(m.From) && r.Contains(m.To) {156+ merges = append(merges, m)157+ }158+ }159+ return heights, merges160+}161+153162 // snapLayout adds to a whole sheet's snapshot what its layout needs in154163 // a workbook, growing the range to hold it: rows with heights, merged155164 // cells, and blank cells that draw borders, which count as contents@@ -75,11 +75,7 @@ func importXLSXExcelize(ctx context.Context, name string, opt Options) (*Result,
7575 if err != nil {7676 return nil, err7777 }78- for i, hs := range heights {79- for row, h := range hs {80- book.Sheet(i).LoadRowHeight(row, h)81- }82- }78+ loadHeights(book, heights)8379 notes = append(notes, excelizeNames(x, book)...)8480 notes = append(notes, (&xlsxBook{protected: protected}).protectionNote()...)8581 active := book.Sheet(clamp(x.GetActiveSheetIndex(), 0, book.Len()-1))@@ -95,6 +91,15 @@ func importXLSXExcelize(ctx context.Context, name string, opt Options) (*Result,
9591 return &Result{Sheet: s, Rows: done, Notes: append(notes, applyFilters(book, filters)...)}, nil9692 }9793 94+// loadHeights gives each sheet of book its rows' heights.95+func loadHeights(book *sheet.Workbook, heights []map[int]int) {96+ for i, hs := range heights {97+ for row, h := range hs {98+ book.Sheet(i).LoadRowHeight(row, h)99+ }100+ }101+}102+98103 // excelizeView freezes s as sheet ws is frozen and loads its notes.99104 func excelizeView(x *excelize.File, ws string, s *sheet.Sheet) error {100105 if p, err := x.GetPanes(ws); err == nil && p.Freeze {@@ -265,11 +270,7 @@ func excelizeSheet(ctx context.Context, x *excelize.File, b *builder, ws string,
265270 return 0, err266271 }267272 defer rows.Close()268- dimCols := 0269- if dim, err := x.GetSheetDimension(ws); err == nil {270- _, to, _ := strings.Cut(dim, ":")271- dimCols, _, _ = excelize.CellNameToCoordinates(cmp.Or(to, dim))272- }273+ dimCols := excelizeDimCols(x, ws)273274 row, width := 0, 0274275 for ; rows.Next(); row++ {275276 if row%128 == 0 {@@ -315,14 +316,32 @@ func excelizeSheet(ctx context.Context, x *excelize.File, b *builder, ws string,
315316 }316317 b.s.LoadColWidth(c, max(int(math.Round(w))+excelPadding, 1))317318 }318- if merges, err := x.GetMergeCells(ws, true); err == nil {319- for _, mc := range merges {320- if r, ok := sheet.ParseRange(mc.GetStartAxis() + ":" + mc.GetEndAxis()); ok {321- b.s.LoadMerge(r)322- }319+ excelizeMerges(x, ws, b.s)320+ return row, excelizeView(x, ws, b.s)321+}322+323+// excelizeDimCols is the last column of sheet ws's dimension.324+func excelizeDimCols(x *excelize.File, ws string) int {325+ dim, err := x.GetSheetDimension(ws)326+ if err != nil {327+ return 0328+ }329+ _, to, _ := strings.Cut(dim, ":")330+ cols, _, _ := excelize.CellNameToCoordinates(cmp.Or(to, dim))331+ return cols332+}333+334+// excelizeMerges merges the cells sheet ws merges.335+func excelizeMerges(x *excelize.File, ws string, s *sheet.Sheet) {336+ merges, err := x.GetMergeCells(ws, true)337+ if err != nil {338+ return339+ }340+ for _, mc := range merges {341+ if r, ok := sheet.ParseRange(mc.GetStartAxis() + ":" + mc.GetEndAxis()); ok {342+ s.LoadMerge(r)323343 }324344 }325- return row, excelizeView(x, ws, b.s)326345 }327346 328347 // excelizeNames defines the workbook's named ranges that are a range on@@ -159,12 +159,7 @@ func (bk *xlsxBook) importRow(b *builder, r *xlsxSheetReader) error {
159159 if !slices.IsSortedFunc(cells, func(a, b xlsxCell) int { return a.col - b.col }) {160160 slices.SortStableFunc(cells, func(a, b xlsxCell) int { return a.col - b.col })161161 }162- lastCol := 0163- for i := range cells {164- if cells[i].kept() || !bk.styles.style(cells[i].style).style.Borders.IsZero() {165- lastCol = cells[i].col166- }167- }162+ lastCol := bk.lastKept(cells)168163 if lastCol == 0 || !b.fits(sheet.Addr{Col: lastCol - 1, Row: row.num - 1}) && row.num > sheet.MaxRows {169164 return nil170165 }@@ -194,6 +189,18 @@ func (bk *xlsxBook) importRow(b *builder, r *xlsxSheetReader) error {
194189 return nil195190 }196191 192+// lastKept is the column of a row's last cell that is kept: one with a193+// value or a formula, or drawing borders.194+func (bk *xlsxBook) lastKept(cells []xlsxCell) int {195+ last := 0196+ for i := range cells {197+ if cells[i].kept() || !bk.styles.style(cells[i].style).style.Borders.IsZero() {198+ last = cells[i].col199+ }200+ }201+ return last202+}203+197204 // importCell stores a cell, reporting whether it stored a formula.198205 // cellStyle is the cell format of the cell c (nil when it isn't written)199206 // in column col of the current row: its own, else the row's, else the@@ -126,50 +126,45 @@ func (s *xlsxStyles) read(p *xlsxPackage, part string) error {
126126 }127127 }128128 129+// styleLists name what each list of the styles part holds, by section130+// and element, for the error when one is too long.131+var styleLists = map[[2]string]string{132+ {"dxfs", "dxf"}: "styles", {"numFmts", "numFmt"}: "styles", {"fonts", "font"}: "fonts",133+ {"borders", "border"}: "borders", {"cellXfs", "xf"}: "cell formats",134+}135+129136 // element reads one element inside a section of the styles part.130137 func (s *xlsxStyles) element(x *xmlStream, se xml.StartElement, section string, cur *xlsxXf) (*xlsxXf, error) {131138 full := len(s.numFmts) >= x.g.p.lim.styles || len(s.fonts) >= x.g.p.lim.styles || len(s.xfs) >= x.g.p.lim.styles ||132139 len(s.dxfs) >= x.g.p.lim.styles || len(s.borders) >= x.g.p.lim.styles140+ if what, listed := styleLists[[2]string{section, se.Name.Local}]; listed && full && x.depth == 3 {141+ return nil, fmt.Errorf("the workbook has more than %d %s: %w", x.g.p.lim.styles, what, errXLSXLimit)142+ }133143 switch {134144 case section == "dxfs" && se.Name.Local == "dxf" && x.depth == 3:135- if full {136- return nil, fmt.Errorf("the workbook has more than %d styles: %w", x.g.p.lim.styles, errXLSXLimit)137- }138145 st, err := readDxf(x)139146 if err != nil {140147 return nil, err141148 }142149 s.dxfs = append(s.dxfs, st)143150 case section == "numFmts" && se.Name.Local == "numFmt" && x.depth == 3:144- if full {145- return nil, fmt.Errorf("the workbook has more than %d styles: %w", x.g.p.lim.styles, errXLSXLimit)146- }147151 id, err := strconv.Atoi(attrOr(se, "numFmtId", ""))148152 if err == nil {149153 s.numFmts[id], _ = attr(se, "formatCode")150154 }151155 case section == "fonts" && se.Name.Local == "font" && x.depth == 3:152- if full {153- return nil, fmt.Errorf("the workbook has more than %d fonts: %w", x.g.p.lim.styles, errXLSXLimit)154- }155156 f, err := readFont(x)156157 if err != nil {157158 return nil, err158159 }159160 s.fonts = append(s.fonts, f)160161 case section == "borders" && se.Name.Local == "border" && x.depth == 3:161- if full {162- return nil, fmt.Errorf("the workbook has more than %d borders: %w", x.g.p.lim.styles, errXLSXLimit)163- }164162 b, err := readBorder(x)165163 if err != nil {166164 return nil, err167165 }168166 s.borders = append(s.borders, b)169167 case section == "cellXfs" && se.Name.Local == "xf" && x.depth == 3:170- if full {171- return nil, fmt.Errorf("the workbook has more than %d cell formats: %w", x.g.p.lim.styles, errXLSXLimit)172- }173168 s.xfs = append(s.xfs, xlsxXf{174169 numFmt: intAttr(se, "numFmtId", -1), font: intAttr(se, "fontId", -1), border: intAttr(se, "borderId", -1),175170 applyFont: boolAttr(se, "applyFont", true), applyAlign: boolAttr(se, "applyAlignment", true),