HotXLS는 AddCamera를 통해 Delphi에서 XLSX 워크시트에 Excel 카메라 객체를 생성합니다. 카메라 객체는 셀 범위에 영구적으로 링크된 그림입니다: Excel은 워크북이 열릴 때마다, 또는 원본이 바뀔 때마다 원본 범위로부터 이를 다시 렌더링하므로, 대시보드는 다른 시트에 있는 표를 어떤 크기로든, 원한다면 회전까지 해서 실시간으로 보여 줄 수 있습니다
이 기능은 Excel에 카메라 도구로 존재하는데, 기본적으로 리본에 없기 때문에 대부분의 사용자가 본 적 없는 버튼입니다. 이는 다른 대안보다 실제 대시보드 문제를 더 잘 해결합니다: 복사된 범위는 낡아 버리고, 차트는 임의의 셀 내용을 표시할 수 없으며, 수동으로 붙여넣은 링크된 그림은 코드로 생성할 수 없습니다. 카메라 객체는 실시간성과 임의성을 동시에 갖춘 유일한 구성체입니다
이것은 어디에 문서화되어 있으며, 왜 중요한가?
메인 스프레드시트 명세는 카메라 객체를 설명하지 않습니다. 이는 구현 세부사항으로 취급됩니다. 권위 있는 설명은 드로잉 마크업 문서의 카메라 도구 항목 아래에 있으며, 이를 알아두면 엉뚱한 문서를 뒤지는 데 오후 시간을 낭비하지 않아도 됩니다
구조적으로 카메라 객체는 확장 목록을 담고 있는 비시각적 그림 속성을 가진 평범한 그림 요소입니다. 이 확장은 고정된 GUID로 식별되며, 그 안에는 2010년 드로잉 네임스페이스에서 온 요소가 두 가지를 기록합니다: A1 스타일의 절대 참조로 표현되며 선택적으로 시트가 지정된 원본 범위, 그리고 도형 식별자입니다. 그림에 관한 나머지는 모두 평범합니다
카메라 객체 만들기
범위 이름을 지정하는 두 가지 방식이 편리하기 때문에 두 가지 오버로드가 존재합니다. 텍스트 형태는 참조를 저장될 그대로 정확히 받으며, 이는 시트 간 원본에서 원하는 방식입니다. 좌표 형태는 같은 시트 위의 셀 좌표 네 개를 받아 절대 참조를 대신 만들어 줍니다:
uses
lxHandleX;
var
Book: TXLSXWorkbook;
Dashboard: TXLSXWorksheet;
Cam: TXLSXImage;
begin
Book := TXLSXWorkbook.Create;
try
if Book.Open('reporting.xlsx') <> 1 then
Exit;
Dashboard := Book.Sheets[0];
// Data!$B$2:$D$4의 실시간 뷰를 대시보드의 B10:F20 위에 배치
Cam := Dashboard.AddCamera('Data!$B$2:$D$4', 10, 2, 20, 6);
// 배치 상자는 기본적으로 Excel 고유의 64px 열과
// 20px 행을 따르며, 필요하면 이후에 EMU 단위로 그려지는 크기를 조정함
Cam.WidthEMU := Round(12.5 * 914400 / 2.54); // 12.5cm
Cam.HeightEMU := Round(6.0 * 914400 / 2.54);
Book.SaveAs('reporting-dashboard.xlsx');
finally
Book.Free;
end;
end;
반환된 객체는 CameraRange라는 속성 하나가 추가된 평범한 그림 객체이며, 이 속성은 그림이 카메라일 때만 비어 있지 않습니다. 자신이 만들지 않은 워크북에서 카메라를 찾아내는 방법도 마찬가지입니다: 그림들을 열거하고 이 속성을 확인하면 됩니다
왜 자리표시자 이미지가 실물일 필요가 없는가?
XLSX 패키지의 모든 그림은 이미지 파트가 필요하며, 카메라 객체도 예외가 아닙니다. 하지만 Excel은 그 이미지를 무시합니다: 로드 시 링크된 범위를 다시 렌더링하고 그 결과를 그립니다. 임베드된 바이트는 카메라 동작을 구현하지 않는 도구를 위한 표시용 캐시로서만 존재합니다
이 사실 덕분에 구현 전체의 한 서브시스템이 통째로 사라집니다. 원본 범위를 래스터화할 필요도, 스냅샷을 만드는 렌더링 엔진도 필요 없고, 캐시된 이미지가 Excel 안의 실시간 이미지와 어긋날 위험도 없습니다. HotXLS는 최소한의 자리표시자 메타파일을 기록하는데 — 헤더 레코드와 파일 끝 레코드로 총 108바이트입니다 — 이는 패키지를 구조적으로 완전하게 유지하면서도 비용이 전혀 들지 않습니다
대비해 둘 결과가 하나 있습니다: 카메라 객체를 구현하지 않은 채 XLSX를 렌더링하는 뷰어는 자리표시자를 표시하는데, 이는 비어 있습니다. 워크북을 그런 도구가 소비한다면, 카메라 객체는 잘못된 선택이며 범위를 렌더링한 이미지가 올바른 선택입니다
틀리기 쉬운 단위 변환
Open XML의 드로잉 지오메트리는 English Metric Unit(EMU)으로 측정되며, 1인치는 914,400 EMU, 1센티미터는 360,000 EMU입니다. 하지만 메타파일 헤더는 프레임을 0.01밀리미터 단위로 기록합니다. 둘 사이의 변환은 360으로 나누는 것입니다
이를 명시적으로 짚어 둘 가치가 있는데, 그 실수가 조용하기 때문입니다. 대신 96 DPI 픽셀을 거쳐 변환하면 — 2540을 곱하고 9525로 나누면 — 96배나 지나치게 큰 값이 나오는데, 아무것도 이를 거부하지 않습니다: 패키지는 유효하고, 그림은 앵커에 의해 올바르게 배치되며, 오직 메타파일에 선언된 프레임만 말이 안 되게 됩니다. EMU 모델과 그 반올림 동작은 이미지 지오메트리, EMU 단위와 스케일링에서 다룹니다
이미 카메라가 있는 워크북 왕복 처리하기
워크북을 열고 저장해도 확장 요소와 그 범위 참조를 포함해 카메라 객체는 보존됩니다. 이는 카메라를 새로 만드는 것보다 더 중요한데, 카메라 객체가 있는 대부분의 워크북은 분석가가 Excel에서 만든 것이며, 저장 시 이를 조용히 평범한 그림으로 바꿔 버리는 라이브러리는 그 분석가가 의존했던 실시간 동작을 망가뜨린 것이기 때문입니다
// 어떤 그림이 카메라이고 어디를 가리키는지 감사함
for I := 0 to Sheet.Images.Count - 1 do
if Sheet.Images[I].CameraRange <> '' then
Writeln(Format('camera %d -> %s',
[I, Sheet.Images[I].CameraRange]));
대시보드를 프로그래밍 방식으로 생성할 때는 이 감사가 회귀 테스트도 겸합니다: 저장 후 다시 여는 주기를 거친 뒤에도 카메라의 개수와 가리키는 범위가 동일해야 합니다. 위치를 결정하는 앵커링 모델을 포함한 일반적인 그림 및 드로잉 처리는 차트, 이미지, 드로잉에서 다룹니다
카메라가 대안보다 나은 경우
같은 실시간 표가 여러 시트에 서로 다른 크기로 나타나야 하거나, 인쇄 레이아웃이 다른 시트의 영역들과 나란히 구성된 한 시트의 영역을 필요로 하거나, 요약 블록이 모든 셀을 개별적으로 잇는 수식 없이도 다른 곳에서 이루어진 편집을 따라가야 할 때 카메라 객체를 사용하세요
대상이 몇 개의 셀뿐이라면 평범한 수식을 선호하세요. 시트 간 수식이 더 단순하고 모든 도구가 이를 이해하기 때문입니다. 데이터가 서식이 지정된 블록이 아니라 진짜 계열이라면 차트를 선호하세요. 그리고 워크북을 Excel이 아닌 다른 도구가 소비하거나, 전달 후 스냅샷이 바뀌어서는 안 될 때는 렌더링된 이미지를 선호하세요 — 아카이브된 보고서는 실시간이어서는 안 됩니다
실무에서 나온 레이아웃 관련 참고 사항 하나: 카메라는 병합된 셀, 조건부 서식, 열 너비를 포함해 원본 범위를 서식이 지정된 그대로 정확히 보여 줍니다. 그러므로 깔끔해 보이는 대시보드 블록을 만드는 것은 원본 범위를 마치 완성된 출력물인 것처럼 서식을 지정하는 데서 시작하며, 이는 병합된 셀과 보고서 템플릿 레이아웃에서 설명하는 것과 같은 원칙입니다
카메라 객체, 드로잉, 그리고 그 뒤에 있는 XLSX 패키지 작성기는 Delphi와 C++Builder를 위한 하나의 라이브러리로 제공됩니다. 전체 기능 목록은 HotXLS Delphi 스프레드시트 컴포넌트 페이지에서 확인할 수 있습니다