11import { isColorValid , toHex } from "../../helpers/color" ;
22import {
3+ AxesDesign ,
34 ExcelChartDataset ,
45 ExcelChartDefinition ,
56 ExcelChartTrendConfiguration ,
67 ExcelTrendlineType ,
8+ TitleDesign ,
79} from "../../types/chart" ;
810import { XLSX_CHART_TYPES , XLSXChartType } from "../../types/xlsx" ;
911import { CHART_TYPE_CONVERSION_MAP , DRAWING_LEGEND_POSITION_CONVERSION_MAP } from "../conversion" ;
@@ -30,6 +32,10 @@ export class XlsxChartExtractor extends XlsxBaseExtractor {
3032 return textElement . textContent || "" ;
3133 }
3234 ) . join ( "" ) ;
35+ const chartTitleStyle = this . extractDefRPrStyle (
36+ rootChartElement ,
37+ "c:chart > c:title a:p a:pPr a:defRPr"
38+ ) ;
3339 const barChartGrouping = this . extractChildAttr ( rootChartElement , "c:grouping" , "val" , {
3440 default : "clustered" ,
3541 } ) . asString ( ) ;
@@ -45,8 +51,9 @@ export class XlsxChartExtractor extends XlsxBaseExtractor {
4551 ( el ) => el . attributes . getNamedItem ( "val" ) ?. value === "1"
4652 ) ;
4753 return {
48- title : { text : chartTitle } ,
54+ title : { text : chartTitle , ... chartTitleStyle } ,
4955 type : CHART_TYPE_CONVERSION_MAP [ chartType ] ! ,
56+ axesDesign : this . extractAxesDesign ( rootChartElement ) ,
5057 dataSets : this . extractChartDatasets (
5158 this . querySelectorAll ( rootChartElement , `c:${ chartType } ` ) ! ,
5259 chartType
@@ -95,6 +102,10 @@ export class XlsxChartExtractor extends XlsxBaseExtractor {
95102 return textElement . textContent || "" ;
96103 }
97104 ) . join ( "" ) ;
105+ const chartTitleStyle = this . extractDefRPrStyle (
106+ chartElement ,
107+ "c:chart > c:title a:p a:pPr a:defRPr"
108+ ) ;
98109 const barChartGrouping = this . extractChildAttr ( chartElement , "c:grouping" , "val" , {
99110 default : "clustered" ,
100111 } ) . asString ( ) ;
@@ -103,8 +114,9 @@ export class XlsxChartExtractor extends XlsxBaseExtractor {
103114 ( el ) => el . attributes . getNamedItem ( "val" ) ?. value === "1"
104115 ) ;
105116 return {
106- title : { text : chartTitle } ,
117+ title : { text : chartTitle , ... chartTitleStyle } ,
107118 type : "combo" ,
119+ axesDesign : this . extractAxesDesign ( chartElement ) ,
108120 dataSets : [
109121 ...this . extractChartDatasets (
110122 this . querySelectorAll ( chartElement , `c:barChart` ) ,
@@ -136,6 +148,105 @@ export class XlsxChartExtractor extends XlsxBaseExtractor {
136148 } ;
137149 }
138150
151+ private extractDefRPrStyle (
152+ element : Element ,
153+ defRPrQuery : string
154+ ) : Pick < TitleDesign , "bold" | "italic" | "fontSize" | "color" > {
155+ const defRPr = this . querySelector ( element , defRPrQuery ) ;
156+ if ( ! defRPr ) {
157+ return { } ;
158+ }
159+ const bAttr = defRPr . getAttribute ( "b" ) ;
160+ const bold = bAttr === "1" || bAttr === "true" ? true : undefined ;
161+ const iAttr = defRPr . getAttribute ( "i" ) ;
162+ const italic = iAttr === "1" || iAttr === "true" ? true : undefined ;
163+ const szAttr = defRPr . getAttribute ( "sz" ) ;
164+ const fontSize = szAttr ? Math . round ( parseInt ( szAttr ) / 100 ) : undefined ;
165+ const color = this . extractDrawingFillColor ( defRPr ) ;
166+ return { bold, italic, fontSize, color } ;
167+ }
168+
169+ private extractDrawingFillColor ( element : Element ) : string | undefined {
170+ const srgbClr = this . querySelector ( element , "a:solidFill a:srgbClr" ) ;
171+ if ( srgbClr ) {
172+ const val = srgbClr . getAttribute ( "val" ) ;
173+ return val && isColorValid ( val ) ? toHex ( val ) : undefined ;
174+ }
175+ const schemeClr = this . querySelector ( element , "a:solidFill a:schemeClr" ) ;
176+ if ( schemeClr ) {
177+ const schemeName = schemeClr . getAttribute ( "val" ) ;
178+ if ( schemeName ) {
179+ return this . resolveSchemeColor ( schemeName ) ;
180+ }
181+ }
182+ return undefined ;
183+ }
184+
185+ /**
186+ * Resolve a DrawingML scheme color name (e.g. "accent1", "dk1") to its hex
187+ * RGB value by looking it up in the theme's `a:clrScheme` element.
188+ * Returns `undefined` if the theme is unavailable or the color cannot be found.
189+ */
190+ private resolveSchemeColor ( schemeName : string ) : string | undefined {
191+ const themeFile = this . xlsxFileStructure . theme ;
192+ if ( ! themeFile ) {
193+ return undefined ;
194+ }
195+ const schemeEl = this . querySelector ( themeFile . file . xml , `a:clrScheme a:${ schemeName } ` ) ;
196+ if ( ! schemeEl ) {
197+ return undefined ;
198+ }
199+ const srgbClr = this . querySelector ( schemeEl , "a:srgbClr" ) ;
200+ if ( srgbClr ) {
201+ const val = srgbClr . getAttribute ( "val" ) ;
202+ return val && isColorValid ( val ) ? toHex ( val ) : undefined ;
203+ }
204+
205+ const sysClr = this . querySelector ( schemeEl , "a:sysClr" ) ;
206+ if ( sysClr ) {
207+ const lastClr = sysClr . getAttribute ( "lastClr" ) ;
208+ return lastClr && isColorValid ( lastClr ) ? toHex ( lastClr ) : undefined ;
209+ }
210+ return undefined ;
211+ }
212+
213+ private extractAxisTitleDesign ( axElement : Element | null ) : TitleDesign | undefined {
214+ if ( axElement === null ) {
215+ return undefined ;
216+ }
217+ const titleText = this . mapOnElements (
218+ { parent : axElement , query : "c:title a:t" } ,
219+ ( el ) => el . textContent || ""
220+ ) . join ( "" ) ;
221+ if ( ! titleText ) {
222+ return undefined ;
223+ }
224+ const style = this . extractDefRPrStyle ( axElement , "c:title a:p a:pPr a:defRPr" ) ;
225+ return { text : titleText , ...style } ;
226+ }
227+
228+ private extractAxesDesign ( chartElement : Element ) : AxesDesign | undefined {
229+ const catAx = this . querySelector ( chartElement , "c:catAx" ) ;
230+ const valAx = this . querySelector ( chartElement , "c:valAx" ) ;
231+ const axPos = catAx ? this . extractChildAttr ( catAx , "c:axPos" , "val" ) ?. asString ( ) : undefined ;
232+ const isHorizontalChart = axPos === "l" || axPos === "r" ;
233+ const xAx = isHorizontalChart ? valAx : catAx ;
234+ const yAx = isHorizontalChart ? catAx : valAx ;
235+ const xTitle = this . extractAxisTitleDesign ( xAx ) ;
236+ const yTitle = this . extractAxisTitleDesign ( yAx ) ;
237+ if ( ! xTitle && ! yTitle ) {
238+ return undefined ;
239+ }
240+ const axesDesign : AxesDesign = { } ;
241+ if ( xTitle ) {
242+ axesDesign . x = { title : xTitle } ;
243+ }
244+ if ( yTitle ) {
245+ axesDesign . y = { title : yTitle } ;
246+ }
247+ return axesDesign ;
248+ }
249+
139250 private extractChartDatasets (
140251 chartElements : NodeListOf < Element > ,
141252 chartType : XLSXChartType
0 commit comments