라이브러리

dom-to-image 작동 원리

kicksky 2021. 5. 5. 23:39

갈피 프로젝트에서 캔버스로 만든 엘리먼트를 이미지화하는 데 dom-to-image 패키지를 썼다. 그 동안 어떤 원리로 이미지 화하는건지 알아봐야겠다고 생각만 하다가 드디어 정리해본다. 

 

우선 깃허브 레포에는 다음과 같이 설명되어있다.

  1. 오리지널 DOM 노드를 재귀적으로 클론
  2. 노드와 서브노드의 스타일을 계산해서 상응하는 클론에 카피 + pseudo element 재생성(얘네는 클론되지 않으니까)
  3. 웹 폰트 임베드
    • 모든 @font-face 선언 찾기
    • file URLs 파싱해서 다운로드
    • base64 인코드와 인라인 컨텐츠를 data: 로 가져오기
    • 모든 CSS 규칙을 이어붙여서 하나의 <style> 엘리먼트에 얹기, 클론에 붙이기
  4. 이미지 임베드
    • <img>에 이미지 url 임베드
    • background css 프로퍼티에 쓰인 이미지를 인라인으로 (폰트처럼)
  5. 클론으로 만든 노드를 XML에 직렬화(serialize, 메모리나 저장 공간에 담을 수 있도록 일렬로 만든다)
  6. 위에서 만든 XML을 <foreignObject> 태그 내부로, 그 다음 SVG로 감싼다. 그러고 나서 이것을 data URL로 만든다.
  7. PNG 컨텐츠나 Unit8Array인 raw pixel을 얻으려면, SVG를 소스로 하는 Image 엘리먼트를 생성하고 이를 off-screen 캔버스 위에 렌더링한다. 그 다음 캔버스에서 컨텐츠를 읽는다.
  8. 끝 !

정리하면 가상의 DOM 노드를 클론해서 여기에 추가적인 스타일이나 폰트, 이미지를 덧붙이고,

최종적으로 XML로 만들어 이를 차례대로 <foreignObject> 태그, SVG로 감싸서 data URL로 만드는 과정이라는 것 같다.

 

XML은 데이터를 보여주는 HTML과 다르게 저장 및 전달 역할만 수행하는 마크업 언어이고, 위에서 클론 노드를 XML에 직렬화한다는 말은 결국 이미지에 대한 정보를 담아야하는 SVG가 이 XML에 기반하기 때문인 것 같다. 그래서 개발자들은 최종 결과인 data URL을 얻어서 활용한다.

 

다른 형식으로 데이터를 얻어야 하는 경우에는 OffscreenCanvas로 옮겨서 추가적인 가공을 한다. 

 


몰랐던 것들

  • XML : XML은 HTML처럼 데이터를 보여주는 목적이 아닌, 데이터를 저장하고 전달할 목적으로만 쓰인다. SVG가 XML을 기반으로 한다.
  • <foreignObject> : SVG 엘리먼트이고 서로 다른 XML 네임스페이스를 가진 엘리먼트를 포함한다.
  • XML namespace : XML에서는 사용자가 XML 요소의 이름을 직접 정의하는데, 이 네임스페이스가 XML 요소 간의 이름에 대한 충돌을 방지해 주는 방법을 제공한다. URI로 식별한다.
  • OffscreenCanvas : HTML5. 화면 밖에서 렌더링하는 캔버스라고 한다.

github.com/tsayen/dom-to-image

developer.mozilla.org/ko/docs/Web/API/OffscreenCanvas

iamsjy17.github.io/html5/2018/12/30/offscreencanvas-test.html