HotXLS는 차트를 전용 차트 시트에 두는 대신 셀 범위에 앵커해 워크시트에 직접 놓을 수 있습니다. BIFF8 용어로는 타입 5 OBJ 레코드를 단 드로잉 셰이프를 쓰고 차트 서브스트림을 시트 레코드 스트림 끝에 세워 두는 것인데, 이것이 정확히 Excel이 만들어 내는 레이아웃이고 정확히 리더가 찾을 것으로 기대하는 자리입니다
이 구분은 운영 리포트를 만드는 사람에게 중요합니다. 차트 시트는 헤드라인용 시각화 하나의 좋은 보금자리입니다. 월간 지역 분석은 요약하는 숫자 옆에, 같은 시트 위에, 자기가 속한 셀 블록 크기에 맞춰 차트가 있어야 독자가 탭을 갈아타고 맥락을 잃는 대신 한 번 스크롤합니다
읽기는 이미 있었고, 쓰기는 없었다
이 비대칭은 작업의 모양을 결정하므로 이름을 붙일 가치가 있습니다. HotXLS는 이미 임베디드 차트를 읽을 수 있었습니다. 워크시트 레코드 스트림에 차트 서브스트림으로 표시된 BOF가 있으면 파서가 컨텍스트를 전환하고 차트 레코드들을 수집하며, 닫는 EOF에서 OBJ 레코드가 소개한 드로잉 셰이프에게 돌려줍니다. 이 경로는 이 라이브러리가 열어 본 모든 Excel 작성 워크북이 거쳐 갔습니다
없던 것은 저작 쪽이었고, 쓸모 있는 귀결은 새 라이터가 정확한 명세를 맞추면 된다는 것이었습니다. 기존 리더가 이미 다시 붙이는 바이트 레이아웃을 만들어 내라. 바이너리 포맷 기능에 이보다 좋은 인수 조건은 없습니다. 여러분이 고칠 수 없는, 독립적으로 작성된 리더니까요
임베디드 차트의 구성
세 조각이 합의해야 합니다. 드로잉 계층은 호스트 컨트롤 셰이프를 내놓고, 오브젝트 계층은 common object data가 오브젝트 타입 5를 선언하는 OBJ 레코드를 내놓으며, 레코드 스트림은 차트 서브스트림 자체를 내놓습니다. OBJ 레코드의 옵션 플래그는 Excel이 차트 프레임에 쓰는 것들입니다. positioned, locked, automatic line, automatic fill. 사용자가 클릭할 때 임베디드 차트가 네이티브처럼 동작하게 만드는 것이 바로 이것입니다
앵커는 off-by-one 버그의 흔한 원천이므로 한마디 할 가치가 있습니다. HotXLS API는 라이브러리의 나머지와 맞춰 1 기반 행·열 번호를 받고, 파일에 쓰이는 client anchor는 0 기반입니다. 변환은 AddChartObject 안에서 일어나므로 호출자는 어디서나 쓰는 좌표계에 머물지만, 헥스 덤프를 자기 호출과 대조하는 사람은 그 경계의 어느 쪽을 읽고 있는지 기억해야 합니다
var
Book: TXLSWorkbook;
Sheet: TXLSWorksheet;
Series: array[0..1] of TXLSChartSeriesInfo;
begin
Book := TXLSWorkbook.Create(nil);
try
Book.LoadFromFile('regional-sales.xls');
Sheet := Book.Sheets[0];
FillChar(Series, SizeOf(Series), 0);
Series[0].Name := 'Actual';
Series[0].Categories := 'Data!$A$2:$A$13';
Series[0].Values := 'Data!$B$2:$B$13';
Series[0].DataLabels.ShowValue := True;
Series[0].HasDataLabels := True;
Series[1].Name := 'Target';
Series[1].Categories := 'Data!$A$2:$A$13';
Series[1].Values := 'Data!$C$2:$C$13';
Series[1].SecondaryAxis := True;
// 이 시트의 E2:M20에 앵커, 1 기반
Sheet.AddChartObject(xlsChartTypeColumn, 'Regional sales',
'Month', 'Amount', Series, 2, 5, 20, 13);
Book.SaveToFile('regional-sales-charted.xls');
finally
Book.Free;
end;
end;
시리즈 배열의 FillChar는 장식이 아닙니다. TXLSChartSeriesInfo는 데이터 레이블, 계열별 스타일, 추세선, 오차 막대 같은 여러 선택 서브 레코드를 실으며 각각이 불리언으로 게이트됩니다. 부분 초기화된 레코드가 스택에 있으면 이미터는 아무도 설정하지 않은 플래그를 건네받습니다. 배열을 0으로 만들고, 의도한 필드만 설정하세요
임베디드 경로가 받아들이는 계열 참조는?
같은 워크북 안의 평범한 A1 스타일 범위이며, 이 제약은 실수가 아니라 의도입니다. 모든 참조는 워크북 시트 목록에 대해 해석되고 차트 레코드가 필요로 하는 외부 참조 인덱스로 바뀝니다. 명명 범위나 외부 워크북 참조는 길이 0의 파싱된 식을 갖는 플레이스홀더로 폴백되므로, 차트는 깨끗하게 쓰이지만 그 계열은 범위를 가리킬 때까지 데이터 원본이 없습니다
이유는 소박한 엔지니어링 트레이드입니다. 전체 참조 컴파일 경로는 워크시트 컬렉션 계층 안에 감싸인 채 차트 시트 경로에 존재하고, 그것을 깨끗하게 들어내는 것은 실무에서 흔치 않은 사례를 위해 해석 로직 백 줄을 복제하는 일이 됩니다. 임베디드 차트는 거의 항상 자기 시트나 형제 데이터 시트의 셀을 그립니다. 명명 참조와 외부 참조는 AddChartSheet를 통한 차트 시트 경로에서 커버되므로, 불가능한 것은 아무것도 없고 진입점이 다를 뿐입니다
계열 모델의 나머지 전부는 두 경로에서 똑같이 동작합니다. 보조 축 바인딩, 계열별 선·채움·마커 스타일, 추세선, 오차 막대, 데이터 레이블은 모두 TXLSChartSeriesInfo의 일부이고 모두 같은 방식으로 emit되므로, 차트 정의는 호출만 바꿔서 임베디드 오브젝트와 차트 시트 사이를 이동할 수 있습니다. 보조 축 플래그 뒤의 축 그룹 역학은 BIFF 쓰기에서의 보조 축 그룹에서 다룹니다
차트 제목이 두 글자로 읽힌 이유는?
바이트 수가 기대되는 자리에 문자 수가 전달됐기 때문이고, BIFF Unicode 문자열은 그 실수를 쓰기 쉽고 보기 어렵게 만듭니다. 짧은 BIFF Unicode 문자열은 문자 수와 플래그 바이트로 시작하고, 플래그 바이트는 페이로드가 문자당 1바이트인지 2바이트인지를 말하는 하이바이트 비트를 실고 있습니다. 문자 수를 바이트 길이인 양 16비트 페이로드를 읽으면 정확히 절반의 문자열이 나옵니다. Sales라는 이름의 계열은 Sa로 돌아오고, 차트 제목도 같은 식으로 잘립니다. 제목과 계열 레이블이 같은 디코딩 경로를 공유하기 때문입니다
이 결함이 주목할 만한 이유는 같은 레코드 패밀리에서 세 번 재발했다는 것입니다. 추세선 이름에서 한 번, 피벗 차트 이름에서 한 번, 차트 제목에서 한 번. 각 사건은 새 기능의 새로운 버그처럼 보였습니다. 셋 모두 같은 빠진 곱셈이었습니다. 마침내 문을 닫은 규칙은 기계적이고 판단 없이 적용되어야 합니다. 이 문자열을 읽을 때면 하이바이트 플래그를 먼저 보고, 버퍼를 만지기 전에 문자 수에 페이로드 폭을 곱하라. 레코드 수준 디테일은 XLUnicodeString 문자 수와 하이바이트 플래그 디코딩에 있습니다
// 임베디드 차트는 이미지와 셰이프와 드로잉 계층을 공유하므로 시트의
// 기존 드로잉은 보존됩니다. AddChartObject는 생성된 오브젝트의
// 인덱스를 돌려줍니다
var
ObjIndex: Integer;
begin
ObjIndex := Sheet.AddChartObject(xlsChartTypeLine, 'Trend',
'Week', 'Units', Series, 2, 8, 18, 16);
if ObjIndex < 0 then
raise Exception.Create('chart object was not created');
end;
대안들과 비교한 임베디드 차트의 자리
경로는 셋이고 서로 다른 질문에 답합니다. 임베디드 차트 오브젝트는 워크시트에서 자기 데이터 곁에 속하며 대부분의 리포트가 원하는 것입니다. 차트 시트는 프레젠테이션용 시각화 하나에 어울리고 전체 참조 컴파일 경로를 줍니다. 로드한 파일에서 기존 차트를 손대지 않고 보존하는 것은 워크북이 누구도 라이브러리에게 재해석시키고 싶지 않은 서식을 갖춘 채 Excel에서 왔을 때 옳은 답입니다. 그 패스스루 동작은 보존된 ChartML과 콤비네이션 차트에서 기술됩니다
임베디드 차트는 드로잉 계층을 타고 가므로 같은 시트의 이미지와 셰이프를 대체하는 게 아니라 공존하며, 그 계층의 일반 모델은 HotXLS의 차트, 이미지, 드로잉에서 다룹니다. 세 경로 모두 HotXLS Delphi 스프레드시트 컴포넌트에 실려 있으므로, 선택은 라이브러리가 무엇을 표현할 수 있는지가 아니라 리포트가 어떻게 보여야 하는지에 관한 것입니다
방법론적 포인트가 챙겨갈 가치가 있는 것입니다. 바이너리 포맷 기능에 기존 리더가 있다면, 스펙에 대한 여러분의 해석이 아니라 리더를 향해 라이터를 만드세요. 리더는 실제 애플리케이션이 실제로 만들어 낸 파일들과 수년간 접촉한 것이 몸에 배어 있고, 스펙이 느슨하게 말하는 부분들을 포함합니다. 그것을 만족시키는 라이터는 Excel도 만족시킬 가능성이 훨씬 큽니다