PHP Classes

File: extension/compat/reverse.go

Recommend this page to a friend!
  Packages of Roni   Easy-Excel   extension/compat/reverse.go   Download  
File: extension/compat/reverse.go
Role: Auxiliary data
Content type: text/plain
Description: Auxiliary data
Class: Easy-Excel
Export and read XLSX files with a PHP Go extension
Author: By
Last change:
Date: 7 days ago
Size: 7,099 bytes
 

Contents

Class file image Download
package compat import ( "github.com/xuri/excelize/v2" ) // Reverse translation (wave 4.2): excelize styles back into the // PhpSpreadsheet spec shape, so getStyle() can reflect loaded files and // duplicateStyle() can copy them. Only what TranslateStyle can produce is // reversed; exotic stylesheet content comes back best-effort. var borderStyleNames = invertIntMap(borderStyles) var fillPatternNames = invertIntMap(fillPatterns) func invertIntMap(m map[string]int) map[int]string { out := make(map[int]string, len(m)) for k, v := range m { out[v] = k } return out } // builtinNumFmts maps the OOXML built-in numFmt ids PhpSpreadsheet users // meet in practice (ECMA-376 §18.8.30 subset). var builtinNumFmts = map[int]string{ 0: "General", 1: "0", 2: "0.00", 3: "#,##0", 4: "#,##0.00", 9: "0%", 10: "0.00%", 11: "0.00E+00", 12: "# ?/?", 13: "# ??/??", 14: "m/d/yy", 15: "d-mmm-yy", 16: "d-mmm", 17: "mmm-yy", 18: "h:mm AM/PM", 19: "h:mm:ss AM/PM", 20: "h:mm", 21: "h:mm:ss", 22: "m/d/yy h:mm", 37: "#,##0 ;(#,##0)", 38: "#,##0 ;[Red](#,##0)", 39: "#,##0.00;(#,##0.00)", 40: "#,##0.00;[Red](#,##0.00)", 45: "mm:ss", 46: "[h]:mm:ss", 47: "mmss.0", 48: "##0.0E+0", 49: "@", } // ReverseStyle converts an excelize style into the applyFromArray spec shape. func ReverseStyle(s *excelize.Style) StyleSpec { spec := StyleSpec{} if s == nil { return spec } if f := s.Font; f != nil { font := map[string]any{} if f.Bold { font["bold"] = true } if f.Italic { font["italic"] = true } if f.Strike { font["strikethrough"] = true } if f.Underline != "" { font["underline"] = f.Underline } if f.Family != "" { font["name"] = f.Family } if f.Size > 0 { font["size"] = f.Size } if f.Color != "" { font["color"] = map[string]any{"rgb": stripAlpha(f.Color)} } if len(font) > 0 { spec["font"] = font } } if s.Fill.Type == "gradient" && len(s.Fill.Color) > 0 { fill := map[string]any{ "fillType": "linear", "rotation": shadingRotation(s.Fill.Shading), "startColor": map[string]any{"rgb": stripAlpha(s.Fill.Color[0])}, } if s.Fill.Shading == 16 { fill["fillType"] = "path" } if len(s.Fill.Color) > 1 { fill["endColor"] = map[string]any{"rgb": stripAlpha(s.Fill.Color[1])} } spec["fill"] = fill } else if s.Fill.Pattern > 0 { fill := map[string]any{"fillType": fillPatternNames[s.Fill.Pattern]} if len(s.Fill.Color) > 0 { fill["startColor"] = map[string]any{"rgb": stripAlpha(s.Fill.Color[0])} } spec["fill"] = fill } if len(s.Border) > 0 { borders := map[string]any{} diagonal := 0 for _, b := range s.Border { side := map[string]any{"borderStyle": borderStyleNames[b.Style]} if b.Color != "" { side["color"] = map[string]any{"rgb": stripAlpha(b.Color)} } switch b.Type { case "diagonalUp": diagonal |= 1 borders["diagonal"] = side case "diagonalDown": diagonal |= 2 borders["diagonal"] = side default: borders[b.Type] = side } } if diagonal != 0 { borders["diagonalDirection"] = float64(diagonal) } if len(borders) > 0 { spec["borders"] = borders } } if a := s.Alignment; a != nil { align := map[string]any{} if a.Horizontal != "" { align["horizontal"] = a.Horizontal } if a.Vertical != "" { align["vertical"] = a.Vertical } if a.WrapText { align["wrapText"] = true } if a.ShrinkToFit { align["shrinkToFit"] = true } if a.TextRotation != 0 { align["textRotation"] = float64(a.TextRotation) } if a.Indent != 0 { align["indent"] = float64(a.Indent) } if len(align) > 0 { spec["alignment"] = align } } if p := s.Protection; p != nil { prot := map[string]any{} if !p.Locked { prot["locked"] = "unprotected" } if p.Hidden { prot["hidden"] = true } if len(prot) > 0 { spec["protection"] = prot } } switch { case s.CustomNumFmt != nil && *s.CustomNumFmt != "": spec["numberFormat"] = map[string]any{"formatCode": *s.CustomNumFmt} case s.NumFmt != 0: if code, ok := builtinNumFmts[s.NumFmt]; ok { spec["numberFormat"] = map[string]any{"formatCode": code} } } return spec } // shadingRotation is the inverse of gradientShading's bucket centers. func shadingRotation(shading int) float64 { switch { case shading >= 3 && shading <= 5: return 90 case shading >= 6 && shading <= 8: return 135 case shading >= 9 && shading <= 11: return 45 default: return 0 } } // ReverseValidation converts an excelize data validation into the shim's // spec shape plus its range. func ReverseValidation(dv *excelize.DataValidation) (ref string, spec map[string]any) { spec = map[string]any{ "type": dv.Type, "operator": dv.Operator, "formula1": dv.Formula1, "formula2": dv.Formula2, "allowBlank": dv.AllowBlank, "showDropDown": dv.ShowDropDown, "showInputMessage": dv.ShowInputMessage, "showErrorMessage": dv.ShowErrorMessage, } deref := func(p *string) string { if p == nil { return "" } return *p } spec["errorStyle"] = deref(dv.ErrorStyle) spec["errorTitle"] = deref(dv.ErrorTitle) spec["error"] = deref(dv.Error) spec["promptTitle"] = deref(dv.PromptTitle) spec["prompt"] = deref(dv.Prompt) return dv.Sqref, spec } var conditionalOperators = invertStringMap(conditionalCriteria) func invertStringMap(m map[string]string) map[string]string { out := make(map[string]string, len(m)) for k, v := range m { out[v] = k } // excelize reads criteria back in long form, not the symbols it accepts for long, op := range map[string]string{ "equal to": "equal", "not equal to": "notEqual", "greater than": "greaterThan", "greater than or equal to": "greaterThanOrEqual", "less than": "lessThan", "less than or equal to": "lessThanOrEqual", } { out[long] = op } return out } // ReverseConditional converts excelize conditional-format options back into // the shim's rule shape (style is returned as a style spec when resolvable). func ReverseConditional(opt excelize.ConditionalFormatOptions, style *excelize.Style) map[string]any { rule := map[string]any{"stopIfTrue": opt.StopIfTrue} switch opt.Type { case "cell", "text": rule["type"] = "cellIs" if opt.Type == "text" { rule["type"] = "containsText" } if op, ok := conditionalOperators[opt.Criteria]; ok { rule["operator"] = op } if opt.Value != "" { rule["conditions"] = []any{opt.Value} } case "formula": rule["type"] = "expression" rule["conditions"] = []any{opt.Criteria} case "2_color_scale", "3_color_scale": rule["type"] = "colorScale" scale := map[string]any{"minColor": opt.MinColor, "maxColor": opt.MaxColor} if opt.MidColor != "" { scale["midColor"] = opt.MidColor } rule["colorScale"] = scale case "data_bar": rule["type"] = "dataBar" rule["dataBar"] = map[string]any{"color": opt.BarColor} default: rule["type"] = opt.Type } if style != nil { if spec := ReverseStyle(style); len(spec) > 0 { rule["style"] = map[string]any(spec) } } return rule }