-
Notifications
You must be signed in to change notification settings - Fork 3
기술공유 02. GraphicsMagick으로 PDF 파일을 웹에 렌더링 시키기
저희는 프레젠테이션 채널을 생성 할 때, 자체적으로 개발한 UI에 PDF를 어떤식으로 렌더링 해야 할지 고민했습니다.🤔 처음 계획은 우리가 직접 모든 것을 개발해보자!
였습니다. 하지만 현실적으로 프로젝트를 완료하기 위해서 많은 것을 포기 해야 했습니다. 왜냐하면 PDF를 어떤식으로 웹에 한장씩 어떤식으로 뿌려줘야 하는 해결책이 도무지 나오지 않았기 때문입니다.
그 중 발견 한 것이 OS위에서 작동하는 프로그램인 Ghostscript와 ImageMagick입니다.
Ghostscript는 PDF파일을 다룰수 있는 기능을 제공해줬습니다. 덕분에 PDF파일을 이미지로 바꿀 수 있었습니다. 또한 ImageMagick으로 그래픽 작업을 합니다. 그래서 원하는 방식대로 그래픽 작업을 할 수 있었습니다.
하지만 여기서 문제가 있습니다.😥 바로 OS에서 작동한 다는 것이었고, 저희는 node에서 사용할 수 있는 모듈이 필요했습니다.
GraphicsMagick(GM)은 기존의 ImageMagick을 fork 해서 만든 업그레이드 된 프로그램입니다. 성능면에서도 확실히 GM이 앞서는 자료가 있기 때문에, 저희는 GM을 사용하기로 했습니다. GraphicsMagick vs ImageMagick 성능비교
GM은 다행히 node에서 사용할 수 있게 API를 가진 gm 이라는 모듈을 제공해줬고, 이 것을 이용해 이미지를 자유롭게 다룰 수 있는 상황이 되었습니다.
다음은 GM이 해주는 놀라운 기능들 입니다.
- 한 형식에서 다른 형식으로 이미지 변환 (예 : TIFF에서 JPEG로)
- 이미지 크기 조정, 회전, 선명 화, 색상 감소 또는 특수 효과 추가
- 이미지 섬네일의 몽타주 만들기
- 웹에서 사용하기에 적합한 투명한 이미지 작업
- 두 이미지 비교
- 이미지 그룹을 GIF 애니메이션 시퀀스로 전환
- 여러 개의 개별 이미지를 결합하여 합성 이미지 만들기
- 이미지에 도형 또는 텍스트 그리기 기능
- 테두리 또는 프레임으로 이미지 장식
- 파일에 형식과 특성을 확인, 페이지 목록 너비 높이 등 파일의 정보를 파싱 이런 많은 기능이 있기 때문에, 저희처럼 한정된 기능 외에도 많은 기능으로 이미지를 다룰 수 있습니다.
최초 타입스크립트로 구상했던 프로젝트가 구현이 많다는 이유로 자바스크립트로 대체 되었었습니다. 컨버터 모듈은 처음부터 오픈소스화를 염두해 두고 개발하였기 때문에 앞으로의 확장성을 생각하여 타입스크립트를 적용하였습니다. 아무래도 정적타이핑이 대규모 프로젝트, 장기간 유지보수 상황에서 확실히 안정성을 보장하고 생산성을 높일 수 있기 때문입니다. 또 컨버터 모듈을 객체지향적으로 설계하려고 했기 때문에 인터페이스나 클래스에 대한 섬세한 문법을 적용할 수 있는 타입스크립트가 적합하다고 판단하였습니다.
dropy메인 화면에서 채널 만들기를 누른 후에 PDF를 드랍하는 순간!! server-converter로 PDF와 Header-Token이 전송됩니다.
Header-Token은 신원을 파악하기 위한 방식입니다.
이제 본격적으로 저희의 코드가 힘을 낼 때군요! 물론 GraphicsMagick과 Ghostscript의 도움을 받고 있지만요!!
저희는 총 4개의 API를 사용했습니다.
- 첫 번째 identify입니다. identify API는 파일에 형식과 특성을 확인해주는 API 입니다. 저희는 페이지수를 카운팅 하기 위해 이 API를 사용했습니다.
- 두 번째 density입니다. 이 옵션은 래스터 이미지 혹은 벡터 형식 이미지를 인코딩하는 동안 저장할 이미지 해상도 캔버스 해상도를 지정합니다.
- 세 번째 quality입니다. jpeg | miff | png | tiff 압축 레벨을 조정합니다. val범위는 0에서 100까지입니다.
- 마지막으로 compress입니다. None, BZip, Fax, Group4, JPEG, Lossless, LZW, RLE, Zip, or LZMA
// 사용 예시입니다.
gm(inputPath).identify('%p ', (err, data) => {
if (err) reject(err);
else resolve(data.split(' ').length);
});
gm("img.png").density(width, height);
gm("img.png").quality(val);
gm("img.png").compress(type);
그 후에 image를 페이지 수 대로 써내렸습니다.
결국 PDF를 페이지 별로 나누고 저희가 만든 함수들을 거쳐 채널에 렌더링할 이미지로 새롭게 태어납니다. 그리고 PDF원본 파일은 추후에 저장할 수 있도록 저장하고, 각 페이지는 따로 저장을 했습니다.
# Object Storage server에 저장
ChannelId/files/PDF #PDF 원본 파일 저장 경로
ChannelId/images/slide.png # PDF 페이지별 저장 경로
성공적으로 위의 과정이 끝났다면, 채널이 개설 됨 동시에, 첫 번째 페이지가 렌더링 됩니다.
© BoostCamp 김김이조.
Members
'김'도현 (happydhKim) | '김'재원 (load0ne) | '이'미림 (always-awake) | '조'애리 (aereeeee)
-
Plans
-
Rules
-
Style Guides
-
Sprint Meeting Logs