Skip to content

Commit 5c72f48

Browse files
committed
Task #195: OOXML 차트 콤보/이중축 지원 + HWPX 차트 파서 추가
## 변경 사항 ### ooxml_chart (콤보 차트 + 이중 Y축) - OoxmlSeries에 series_type/axis_group/axis_ids/format_code 필드 추가 - 파서: barChart/lineChart 공존 인식, valAx axPos로 primary/secondary 축 매핑 - 시리즈 색상 추출: spPr의 srgbClr (직접 RGB) + schemeClr (accent1~6 매핑) - 숫자 포맷: c:formatCode 파싱, 천 단위 콤마 지원 - 렌더러: 콤보 렌더, 이중 Y축, 라인 데이터 포인트 마커, nice-number 눈금 ### HWPX 차트 파싱 (신규) - parse_hwpx: Chart/chartN.xml을 BinDataContent에 주입 (id=60000+N, extension=ooxml_chart) - section.rs: hp:switch/hp:case/hp:chart 엘리먼트 핸들러 추가 - hp:switch 내부 OOXML 차트 우선, 없으면 hp:ole fallback - 공통 shape 속성(sz, pos, outMargin) 파싱 헬퍼 추가 ### 렌더러 연결 - find_bin_data: 1-indexed 순번 실패 시 id 필드로 직접 검색 fallback - shape_layout Ole arm: extension=ooxml_chart면 CFB 파싱 건너뛰고 직접 렌더
1 parent 92ad57a commit 5c72f48

7 files changed

Lines changed: 937 additions & 174 deletions

File tree

src/ooxml_chart/mod.rs

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,36 @@
1-
//! OOXML 차트 (DrawingML) 파싱 및 SVG 렌더링 (Task #195 단계 8)
1+
//! OOXML 차트 (DrawingML) 파싱 및 SVG 렌더링
22
//!
3-
//! HWP 파일 내 OLE 개체의 `OOXMLChartContents` 스트림은 Microsoft OOXML DrawingML
4-
//! 차트 XML로 저장된다. 이 모듈은 해당 XML을 파싱하여 데이터 모델로 변환한 뒤,
5-
//! 네이티브 SVG 차트로 렌더링한다.
3+
//! HWP 파일 내 OLE 개체의 `OOXMLChartContents` 스트림 또는 HWPX `Chart/chartN.xml`은
4+
//! Microsoft OOXML DrawingML 차트 XML로 저장된다. 이 모듈은 해당 XML을 파싱하여
5+
//! 데이터 모델로 변환한 뒤, 네이티브 SVG 차트로 렌더링한다.
66
//!
7-
//! ## 지원 범위 (1차)
7+
//! ## 지원 범위
88
//! - `c:barChart` (세로/가로 막대)
99
//! - `c:lineChart` (꺾은선)
1010
//! - `c:pieChart` (원형)
11+
//! - **콤보 차트** (barChart + lineChart 혼합) — 시리즈별 타입 보존
12+
//! - **이중 Y축** (primary + secondary) — 시리즈별 축 그룹 매핑
1113
//!
1214
//! ## 범위 외
13-
//! - 3D 차트, 영역/산점도, 복합 차트, 보조축, 추세선, 애니메이션, 세밀 스타일
15+
//! - 3D 차트, 영역/산점도, 추세선, 애니메이션, 세밀 스타일
1416
1517
pub mod parser;
1618
pub mod renderer;
1719

1820
/// OOXML 차트 데이터 모델
1921
#[derive(Debug, Clone, Default)]
2022
pub struct OoxmlChart {
23+
/// 주 차트 타입 (콤보인 경우 첫 번째 plotType이 들어감; 렌더러는 시리즈별 타입 우선)
2124
pub chart_type: OoxmlChartType,
2225
pub title: Option<String>,
2326
pub series: Vec<OoxmlSeries>,
2427
pub categories: Vec<String>,
28+
/// 시리즈 중 하나라도 보조축을 쓰면 true
29+
pub has_secondary_axis: bool,
2530
}
2631

2732
/// 차트 종류
28-
#[derive(Debug, Clone, Copy, PartialEq, Default)]
33+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
2934
pub enum OoxmlChartType {
3035
/// 세로 막대 (barDir=col)
3136
Column,
@@ -58,6 +63,14 @@ pub struct OoxmlSeries {
5863
pub values: Vec<f64>,
5964
/// RGB 색상 (`0xRRGGBB`), 파서가 확정 못하면 None (렌더러가 기본 팔레트 적용)
6065
pub color: Option<u32>,
66+
/// 시리즈 본인의 차트 타입 (콤보 차트에서 바/라인 구분용)
67+
pub series_type: OoxmlChartType,
68+
/// 이 시리즈가 속한 플롯의 c:axId 값 목록 (parser 내부에서 axis 분류에 사용)
69+
pub axis_ids: Vec<String>,
70+
/// 0 = 기본축(왼쪽/아래), 1 = 보조축(오른쪽/위)
71+
pub axis_group: u8,
72+
/// 숫자 포맷 코드 (예: "#,##0")
73+
pub format_code: Option<String>,
6174
}
6275

6376
impl OoxmlChart {
@@ -71,4 +84,15 @@ impl OoxmlChart {
7184
pub fn render_svg(&self, x: f64, y: f64, w: f64, h: f64) -> String {
7285
renderer::render_chart_svg(self, x, y, w, h)
7386
}
87+
88+
/// 시리즈가 여러 타입을 섞어 쓰는지 (콤보 차트) 여부
89+
pub fn is_combo(&self) -> bool {
90+
let mut types: std::collections::HashSet<OoxmlChartType> = std::collections::HashSet::new();
91+
for s in &self.series {
92+
if s.series_type != OoxmlChartType::Unknown {
93+
types.insert(s.series_type);
94+
}
95+
}
96+
types.len() > 1
97+
}
7498
}

0 commit comments

Comments
 (0)