기술 문서

Delphi에서 ISO 29500 Strict XLSX 파일 작성하기

HotXLS는 저장하기 전에 StrictOOXML 속성 하나만 설정하면 Delphi와 C++Builder에서 ISO/IEC 29500 Strict Open XML 워크북을 작성합니다. xl/workbook.xml부터 관계 파일과 콘텐츠 타입에 이르기까지 패키지 안의 모든 파트가 전이형 schemas.openxmlformats.org 대신 strict용 purl.oclc.org 어휘로 작성되며, Strict가 허용하지 않는 기능은 그냥 내보내지는 대신 명시적인 예외로 거부됩니다

대부분의 개발자는 이 요구 사항을 조달 문서를 통해 마주칩니다. 여러 관할권의 공공 부문 입찰은 Office가 기본으로 작성하는 전이형이 아니라 ISO 표준화된 형태의 Open XML을 요구하며, ISO 29500 Strict를 강제하는 아카이브는 Excel에서 완벽하게 열리는 일반 .xlsx조차 거부합니다. 전이형 네임스페이스는 레거시 바이너리 동작을 수용하기 위해 존재하며, strict 네임스페이스가 진짜 표준입니다

Strict와 Transitional은 실제로 무엇이 다를까?

눈에 보이는 차이는 어휘입니다. strict 워크북 파트는 루트 네임스페이스로 http://purl.oclc.org/ooxml/spreadsheetml/main을, 관계 참조로 http://purl.oclc.org/ooxml/officeDocument/relationships를 선언하며, 전이형 네임스페이스는 패키지 어디에도 남아 있어서는 안 됩니다. 관계 타입도 함께 바뀌어서, 루트 관계 파트는 익숙한 openxmlformats 형태 대신 .../ooxml/officeDocument/relationships/officeDocument라는 이름을 가지며, 확장 속성 타입은 카멜케이스로 extendedProperties가 됩니다

눈에 보이지 않는 차이는 범위입니다. Strict는 레거시 바이너리 파일을 왕복하기 위해서만 존재했던 전이형 스키마의 일부와 그 이후 Office가 추가한 벤더 확장을 의도적으로 생략합니다. 그래서 이 변환은 문자열을 검색해 치환하는 작업이 아닙니다. 어떤 기능은 애초에 strict용 표현이 없어서 아예 쓰면 안 되기 때문입니다

기능 켜기

일반적인 작성 코드는 바뀌지 않습니다. 늘 하던 대로 워크북을 만들고, 플래그를 설정한 다음 저장하면 됩니다:

var
  Wb: TXLSXWorkbook;
  Sh: TXLSXWorksheet;
begin
  Wb := TXLSXWorkbook.Create;
  try
    Sh := Wb.Sheets.Add('Data');
    Sh.Cells[1, 1].Value := 'Product';
    Sh.Cells[1, 2].Value := 'Amount';
    Sh.Cells[2, 1].Value := 'Widget';
    Sh.Cells[2, 2].Value := 17;
    Sh.Cells[3, 2].Formula := '=SUM(B2:B2)';

    Wb.StrictOOXML := True;          // ISO/IEC 29500 Strict output
    if Wb.SaveAs('archive-copy.xlsx') <> 1 then
      raise Exception.Create('strict save failed');
  finally
    Wb.Free;
  end;
end;

이 플래그는 저장 작업이 시작될 때마다 초기화되고 워크북 속성으로부터 다시 대입되므로, 한 번의 저장 중 예외가 발생해도 strict 모드가 다음 저장으로 새어 들어갈 수 없습니다. 이 세부 사항은 하나의 워크북 객체가 여러 내보내기 요청을 처리하는 서버 프로세스에서 중요합니다

strict 저장은 왜 실행을 거부할 수 있을까?

네 가지 기능군은 ISO 29500 Strict에 대응하는 것이 없는 마이크로소프트 확장이며, HotXLS는 strict 규격 준수를 주장하면서 실제로는 그렇지 않은 패키지를 내보내는 대신 저장 시점에 예외를 던집니다:

// Strict output cannot embed a VBA project
//   -> save macro-enabled workbooks as transitional .xlsm
// Strict output cannot carry form controls
//   -> buttons, checkboxes, combo boxes and their ctrlProps
// Strict output cannot carry threaded comments
//   -> the modern persons/threads model, not classic notes
// Strict output cannot carry dynamic array metadata
//   -> spill ranges recorded through the metadata part

여기서는 요란하게 실패하는 편이 옳은 선택입니다. VBA 프로젝트가 조용히 빠지면 잘 동작하던 워크북이 여전히 열리기는 하지만 고장난 워크북이 되어 버리고, 그 실패는 몇 주 뒤 사용자로부터 보고됩니다. 예외는 호출 코드가 여전히 무엇을 내보내려 했는지 알고 있는 그 순간에 기능과 바꿔야 할 속성을 정확히 알려줍니다. 전이형 경로에서의 매크로와 외부 링크 보존은 VBA 프로젝트와 외부 링크 보존에서 다룹니다

확장 기능군 중 두 가지는 다르게 처리되며, 그 이유를 알아둘 만합니다. 데이터 막대, 스파크라인 같은 기능은 x14xm 어휘에 있고, 그림의 SVG 변형은 c15에 있습니다. 이들은 네임스페이스 자체로 자신을 설명하는 확장 목록 콘텐츠이며, 일반적인 스프레드시트 파서는 이를 허용하고, 이를 옮겨 담을 ISO 대응 표현도 없습니다. HotXLS는 사용자 콘텐츠를 바닥에 떨어뜨리는 대신 이를 그대로 유지합니다. 파이프라인의 검증기가 네임스페이스뿐 아니라 확장에 대해서도 엄격하다면, 내보내기 전에 원본 워크북에서 이 기능들을 제거하십시오

번역은 아무도 평소에 다시 쓰지 않는 파트까지 닿아야 한다

strict 출력에서 흥미로운 엔지니어링 문제는 워크시트 XML이 아닙니다. 빠른 작성기라면 차라리 그대로 복사하고 싶어할 파트들입니다. HotXLS는 테마, 연결, 외부 링크, 차트, 피벗 블롭을 원본의 압축된 바이트를 그대로 복사해 보존하는데, 이는 충실도를 위해서는 정확히 옳은 방법이지만 strict 출력에는 정확히 잘못된 방법입니다. 복사된 바이트는 전이형 네임스페이스를 그대로 담고 있기 때문입니다

StrictOOXML 아래에서는 이 다섯 가지 보존 경로가 바이트 복사 빠른 경로를 건너뛰고 재구축이나 번역하며 다시 쓰는 방식으로 전환됩니다. 모든 XML은 하나의 번역 루틴을 거치는데, 이 루틴은 큰따옴표로 감싼 속성 값에만 작동을 고정해 셀 안의 URI처럼 보이는 문자열이 실수로 다시 쓰이는 일이 없도록 합니다. 같은 URI가 셀 텍스트 안에 있으면 XML에서 엔터티로 이스케이프되므로, 고정된 치환 로직은 그 텍스트를 아예 볼 수 없습니다. 스트리밍 작성기는 먼저 자신의 스켈레톤을 번역한 다음 sheetData에서 분리하는데, 행 블록에는 어휘 URI가 전혀 없기 때문입니다. 이 보존 경로에 관련된 메커니즘은 테마, 확장 목록, calcChain의 무손실 왕복에서 다룹니다

Excel이 strict로 저장한 파일 읽기

출력만이 전부는 아닙니다. Excel은 "Strict Open XML 스프레드시트"를 저장 옵션으로 제공하며, 그렇게 만들어진 파일은 올바르게 열려야 합니다. HotXLS는 패키지 안의 모든 관계 파싱 지점, 즉 루트, 외부 링크, 워크시트, 도면, 피벗 테이블에서 관계 타입을 정규화하므로, strict 관계 타입이 그에 대응하는 전이형 상수와 똑같이 취급됩니다

읽는 쪽에서의 대응 조치는 네임스페이스 접두사 정규화로, 임의의 접두사와 두 어휘 모두가 하나의 정규 이름 테이블로 귀결되도록 허용합니다. 이 작업은 strict 파일뿐 아니라 일반 파일에도 도움이 되는데, 서드파티 생성기가 접두사를 자유롭게 바인딩하기 때문이며, XLSX 패키지의 OPC 관계 해석에서 설명하는 것과 같은 메커니즘입니다

strict 출력을 내보내기 전 짧은 점검 목록

Excel이 아니라 패키지로 검증하십시오. Excel은 두 형태 모두 문제없이 열기 때문에, 여는 데 성공했다는 사실만으로는 규격 준수에 대해 아무것도 증명하지 못합니다. 결과물을 압축 해제해 xl/workbook.xml이 purl 네임스페이스를 선언하는지, 어떤 파트도 schemas.openxmlformats.org/spreadsheetml을 담고 있지 않은지, _rels/.relsxl/_rels/workbook.xml.rels의 관계 타입이 strict 형태를 쓰는지 확인하십시오

그런 다음 HotXLS로 파일을 다시 열어 값, 수식, 서식, 하이퍼링크를 원본과 비교하십시오. 다시 읽기 테스트는 번역이 콘텐츠를 손상시키지 않았음을 증명하는 유일하게 저렴한 방법이며, 동시에 읽기 쪽 정규화도 검증해 줍니다. 워크북에 차트가 있다면 그것도 확인하십시오. 차트 파트는 strict 모드에서 재구축 경로로 전환되는 보존 파트 중 하나입니다

strict 출력, 관대한 읽기, 무손실 보존은 모두 Delphi와 C++Builder용 같은 OOXML 엔진의 일부입니다. 전체 기능 목록은 HotXLS Delphi 스프레드시트 컴포넌트 페이지에 있습니다