diff --git a/excelize.go b/excelize.go index 0972e96..99243a7 100644 --- a/excelize.go +++ b/excelize.go @@ -21,6 +21,7 @@ type File struct { Sheet map[string]*xlsxWorksheet SheetCount int Styles *xlsxStyleSheet + Theme *xlsxTheme WorkBook *xlsxWorkbook WorkBookRels *xlsxWorkbookRels XLSX map[string][]byte @@ -66,6 +67,7 @@ func OpenReader(r io.Reader) (*File, error) { } f.sheetMap = f.getSheetMap() f.Styles = f.stylesReader() + f.Theme = f.themeReader() return f, nil } diff --git a/excelize_test.go b/excelize_test.go index aca33b4..549190c 100644 --- a/excelize_test.go +++ b/excelize_test.go @@ -2,6 +2,7 @@ package excelize import ( "fmt" + "image/color" _ "image/gif" _ "image/jpeg" _ "image/png" @@ -1145,6 +1146,31 @@ func TestOutlineLevel(t *testing.T) { xlsx.SetColOutlineLevel("Sheet2", "B", 2) } +func TestThemeColor(t *testing.T) { + t.Log(ThemeColor("000000", -0.1)) + t.Log(ThemeColor("000000", 0)) + t.Log(ThemeColor("000000", 1)) +} + +func TestHSL(t *testing.T) { + var hsl HSL + t.Log(hsl.RGBA()) + t.Log(hslModel(hsl)) + t.Log(hslModel(color.Gray16{Y: uint16(1)})) + t.Log(HSLToRGB(0, 1, 0.4)) + t.Log(HSLToRGB(0, 1, 0.6)) + t.Log(hueToRGB(0, 0, -1)) + t.Log(hueToRGB(0, 0, 2)) + t.Log(hueToRGB(0, 0, 1.0/7)) + t.Log(hueToRGB(0, 0, 0.4)) + t.Log(hueToRGB(0, 0, 2.0/4)) + t.Log(RGBToHSL(255, 255, 0)) + t.Log(RGBToHSL(0, 255, 255)) + t.Log(RGBToHSL(250, 100, 50)) + t.Log(RGBToHSL(50, 100, 250)) + t.Log(RGBToHSL(250, 50, 100)) +} + func trimSliceSpace(s []string) []string { for { if len(s) > 0 && s[len(s)-1] == "" { diff --git a/file.go b/file.go index 4f06d1e..2b1f1e0 100644 --- a/file.go +++ b/file.go @@ -36,6 +36,7 @@ func NewFile() *File { f.WorkBookRels = f.workbookRelsReader() f.Sheet["xl/worksheets/sheet1.xml"] = f.workSheetReader("Sheet1") f.sheetMap["Sheet1"] = "xl/worksheets/sheet1.xml" + f.Theme = f.themeReader() return f } diff --git a/hsl.go b/hsl.go new file mode 100644 index 0000000..bd868b1 --- /dev/null +++ b/hsl.go @@ -0,0 +1,141 @@ +/* +Copyright (c) 2012 Rodrigo Moraes. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package excelize + +import ( + "image/color" + "math" +) + +// HSLModel converts any color.Color to a HSL color. +var HSLModel = color.ModelFunc(hslModel) + +// HSL represents a cylindrical coordinate of points in an RGB color model. +// +// Values are in the range 0 to 1. +type HSL struct { + H, S, L float64 +} + +// RGBA returns the alpha-premultiplied red, green, blue and alpha values +// for the HSL. +func (c HSL) RGBA() (uint32, uint32, uint32, uint32) { + r, g, b := HSLToRGB(c.H, c.S, c.L) + return uint32(r) * 0x101, uint32(g) * 0x101, uint32(b) * 0x101, 0xffff +} + +// hslModel converts a color.Color to HSL. +func hslModel(c color.Color) color.Color { + if _, ok := c.(HSL); ok { + return c + } + r, g, b, _ := c.RGBA() + h, s, l := RGBToHSL(uint8(r>>8), uint8(g>>8), uint8(b>>8)) + return HSL{h, s, l} +} + +// RGBToHSL converts an RGB triple to a HSL triple. +func RGBToHSL(r, g, b uint8) (h, s, l float64) { + fR := float64(r) / 255 + fG := float64(g) / 255 + fB := float64(b) / 255 + max := math.Max(math.Max(fR, fG), fB) + min := math.Min(math.Min(fR, fG), fB) + l = (max + min) / 2 + if max == min { + // Achromatic. + h, s = 0, 0 + } else { + // Chromatic. + d := max - min + if l > 0.5 { + s = d / (2.0 - max - min) + } else { + s = d / (max + min) + } + switch max { + case fR: + h = (fG - fB) / d + if fG < fB { + h += 6 + } + case fG: + h = (fB-fR)/d + 2 + case fB: + h = (fR-fG)/d + 4 + } + h /= 6 + } + return +} + +// HSLToRGB converts an HSL triple to a RGB triple. +func HSLToRGB(h, s, l float64) (r, g, b uint8) { + var fR, fG, fB float64 + if s == 0 { + fR, fG, fB = l, l, l + } else { + var q float64 + if l < 0.5 { + q = l * (1 + s) + } else { + q = l + s - s*l + } + p := 2*l - q + fR = hueToRGB(p, q, h+1.0/3) + fG = hueToRGB(p, q, h) + fB = hueToRGB(p, q, h-1.0/3) + } + r = uint8((fR * 255) + 0.5) + g = uint8((fG * 255) + 0.5) + b = uint8((fB * 255) + 0.5) + return +} + +// hueToRGB is a helper function for HSLToRGB. +func hueToRGB(p, q, t float64) float64 { + if t < 0 { + t++ + } + if t > 1 { + t-- + } + if t < 1.0/6 { + return p + (q-p)*6*t + } + if t < 0.5 { + return q + } + if t < 2.0/3 { + return p + (q-p)*(2.0/3-t)*6 + } + return p +} diff --git a/styles.go b/styles.go index 32350ca..e2a1ae6 100644 --- a/styles.go +++ b/styles.go @@ -2723,3 +2723,29 @@ func drawConfFmtExp(p int, ct string, format *formatConditional) *xlsxCfRule { func getPaletteColor(color string) string { return "FF" + strings.Replace(strings.ToUpper(color), "#", "", -1) } + +// themeReader provides function to get the pointer to the xl/theme/theme1.xml +// structure after deserialization. +func (f *File) themeReader() *xlsxTheme { + var theme xlsxTheme + _ = xml.Unmarshal([]byte(f.readXML("xl/theme/theme1.xml")), &theme) + return &theme +} + +// ThemeColor applied the color with tint value. +func ThemeColor(baseColor string, tint float64) string { + if tint == 0 { + return "FF" + baseColor + } + r, _ := strconv.ParseInt(baseColor[0:2], 16, 64) + g, _ := strconv.ParseInt(baseColor[2:4], 16, 64) + b, _ := strconv.ParseInt(baseColor[4:6], 16, 64) + h, s, l := RGBToHSL(uint8(r), uint8(g), uint8(b)) + if tint < 0 { + l *= (1 + tint) + } else { + l = l*(1-tint) + (1 - (1 - tint)) + } + br, bg, bb := HSLToRGB(h, s, l) + return fmt.Sprintf("FF%02X%02X%02X", br, bg, bb) +} diff --git a/xmlTheme.go b/xmlTheme.go new file mode 100644 index 0000000..d2ab343 --- /dev/null +++ b/xmlTheme.go @@ -0,0 +1,140 @@ +package excelize + +import "encoding/xml" + +// xlsxTheme directly maps the theme element in the namespace +// http://schemas.openxmlformats.org/drawingml/2006/main +type xlsxTheme struct { + ThemeElements xlsxThemeElements `xml:"themeElements"` + ObjectDefaults xlsxObjectDefaults `xml:"objectDefaults"` + ExtraClrSchemeLst xlsxExtraClrSchemeLst `xml:"extraClrSchemeLst"` + ExtLst *xlsxExtLst `xml:"extLst"` +} + +// objectDefaults element allows for the definition of default shape, line, +// and textbox formatting properties. An application can use this information +// to format a shape (or text) initially on insertion into a document. +type xlsxObjectDefaults struct { + ObjectDefaults string `xml:",innerxml"` +} + +// xlsxExtraClrSchemeLst element is a container for the list of extra color +// schemes present in a document. +type xlsxExtraClrSchemeLst struct { + ExtraClrSchemeLst string `xml:",innerxml"` +} + +// xlsxThemeElements directly maps the element defines the theme formatting +// options for the theme and is the workhorse of the theme. This is where the +// bulk of the shared theme information is contained and used by a document. +// This element contains the color scheme, font scheme, and format scheme +// elements which define the different formatting aspects of what a theme +// defines. +type xlsxThemeElements struct { + ClrScheme xlsxClrScheme `xml:"clrScheme"` + FontScheme xlsxFontScheme `xml:"fontScheme"` + FmtScheme xlsxFmtScheme `xml:"fmtScheme"` +} + +// xlsxClrScheme element specifies the theme color, stored in the document's +// Theme part to which the value of this theme color shall be mapped. This +// mapping enables multiple theme colors to be chained together. +type xlsxClrScheme struct { + Name string `xml:"name,attr"` + Children []xlsxClrSchemeEl `xml:",any"` +} + +// xlsxFontScheme element defines the font scheme within the theme. The font +// scheme consists of a pair of major and minor fonts for which to use in a +// document. The major font corresponds well with the heading areas of a +// document, and the minor font corresponds well with the normal text or +// paragraph areas. +type xlsxFontScheme struct { + Name string `xml:"name,attr"` + MajorFont xlsxMajorFont `xml:"majorFont"` + MinorFont xlsxMinorFont `xml:"minorFont"` + ExtLst *xlsxExtLst `xml:"extLst"` +} + +// xlsxMajorFont element defines the set of major fonts which are to be used +// under different languages or locals. +type xlsxMajorFont struct { + Children []xlsxFontSchemeEl `xml:",any"` +} + +// xlsxMinorFont element defines the set of minor fonts that are to be used +// under different languages or locals. +type xlsxMinorFont struct { + Children []xlsxFontSchemeEl `xml:",any"` +} + +// xlsxFmtScheme element contains the background fill styles, effect styles, +// fill styles, and line styles which define the style matrix for a theme. The +// style matrix consists of subtle, moderate, and intense fills, lines, and +// effects. The background fills are not generally thought of to directly be +// associated with the matrix, but do play a role in the style of the overall +// document. Usually, a given object chooses a single line style, a single +// fill style, and a single effect style in order to define the overall final +// look of the object. +type xlsxFmtScheme struct { + Name string `xml:"name,attr"` + FillStyleLst xlsxFillStyleLst `xml:"fillStyleLst"` + LnStyleLst xlsxLnStyleLst `xml:"lnStyleLst"` + EffectStyleLst xlsxEffectStyleLst `xml:"effectStyleLst"` + BgFillStyleLst xlsxBgFillStyleLst `xml:"bgFillStyleLst"` +} + +// xlsxFillStyleLst element defines a set of three fill styles that are used +// within a theme. The three fill styles are arranged in order from subtle to +// moderate to intense. +type xlsxFillStyleLst struct { + FillStyleLst string `xml:",innerxml"` +} + +// xlsxLnStyleLst element defines a list of three line styles for use within a +// theme. The three line styles are arranged in order from subtle to moderate +// to intense versions of lines. This list makes up part of the style matrix. +type xlsxLnStyleLst struct { + LnStyleLst string `xml:",innerxml"` +} + +// xlsxEffectStyleLst element defines a set of three effect styles that create +// the effect style list for a theme. The effect styles are arranged in order +// of subtle to moderate to intense. +type xlsxEffectStyleLst struct { + EffectStyleLst string `xml:",innerxml"` +} + +// xlsxBgFillStyleLst element defines a list of background fills that are +// used within a theme. The background fills consist of three fills, arranged +// in order from subtle to moderate to intense. +type xlsxBgFillStyleLst struct { + BgFillStyleLst string `xml:",innerxml"` +} + +// xlsxClrScheme maps to children of the clrScheme element in the namespace +// http://schemas.openxmlformats.org/drawingml/2006/main - currently I have +// not checked it for completeness - it does as much as I need. +type xlsxClrSchemeEl struct { + XMLName xml.Name + SysClr *xlsxSysClr `xml:"sysClr"` + SrgbClr *attrValString `xml:"srgbClr"` +} + +// xlsxFontSchemeEl directly maps the major and minor font of the style's font +// scheme. +type xlsxFontSchemeEl struct { + XMLName xml.Name + Script string `xml:"script,attr,omitempty"` + Typeface string `xml:"typeface,attr"` + Panose string `xml:"panose,attr,omitempty"` + PitchFamily string `xml:"pitchFamily,attr,omitempty"` + Charset string `xml:"charset,attr,omitempty"` +} + +// xlsxSysClr element specifies a color bound to predefined operating system +// elements. +type xlsxSysClr struct { + Val string `xml:"val,attr"` + LastClr string `xml:"lastClr,attr"` +}