Tempo Di Valse

[Windows] PDFium 와 boost::gil 을 함께 사용하기 본문

개발/ETC

[Windows] PDFium 와 boost::gil 을 함께 사용하기

TempoDiValse 2022. 2. 4. 18:41
 

[Windows] PDFium 를 이용한 PDF to PNG

우선, PNG 파일을 만들기 전에 윈도우에 깔려 있어야 하는 라이브러리들이 있다. - libpng - zlib 그지같은 설명서에서는 PDF 파일 여는 것만 있지 이걸 어떻게 사용하라는 안내도 없고, 헤더파일 열자

tempodivalse.tistory.com

에서는 PDFium 에서 비트맵 렌더한 데이터를 libpng 를 사용하여 PNG 파일로 추출해보았는데, 생각해보니 다른 이미지 파일들까지 함께 처리하느라 C++ 에서 대표적으로 사용되어진다는 boost 라이브러리의 gil 기능을 사용하고 있어서, 'gil 로도 PNG 파일을 만들 수 있는데 libpng 로 이걸 쓸 필요 있을까?' 했다.

 

그래서 먼저, 구글링으로 찾아본 게 PDFium 과 boost::gil 을 함께 사용한 경우가 있는 지 확인해 보았다. 그러나.. 그런건 1도 없었다는거 ㅠㅠ. 같은 boost::gil 에도 libpng 를 사용하는데 왜 이런 경우를 다룬 StackOverflow 는 없을까 하며 소스를 일일히 뒤지며 공통점을 찾아보았는다.

 

겨우 찾아낸 것은 다행히도 이미지 버퍼 형식은 uint8_t(=unsigned char) 형식이라는 것이었다. 그렇다고하면 gil 의 이미지 구조에 잘 삽입하면 될 것 같다고 생각했다.

우선 PNG 이기 때문에 gil 에서는 알파값도 다루는 "rgba8_image_t" 이미지 구조를 사용하고 있었고, 여기에 대입해보자 해서 raw buffer 를 넣어서 초기화 할 수 있는 곳이 어디 있나 찾아보았다. 그러나 일단 C++ 초짜라 typename 으로 도배된 이것들을 해석할 능력이 되지 못했다. 어딘가에 있겠지만 못찾는게 확실 할 것이다.

 

대신에 구글링중에 unsigned char 형을 버퍼로 받는 interleaved_view 라는 것을 발견하게 되었다. raw data 를 써있는다고 하고 width/height 와 rowBytes 를 설정하는 곳이 있어서 이 메소드를 이용하면 복사 가능하겠다고 생각을 해서 도전을 해보았다.

 

How to use Boost.GIL views attached to bytes array with interleaved_ptr, versus view constructed with cast to pixel type

How to use Boost.GIL views attached to bytes array with interleaved_ptr, versus view constructed with cast to pixel type - boost_gil_issue_with_interleaved_ptr.cpp

gist.github.com

 

그래서, 저번 포스팅에서 작성한 소스 중에..

// Include 했다...

#include <boost/gil.hpp>
#include <boost/gil/image.hpp>
#include <boost/gil/extension/io/png.hpp>

int main(){
    const char *path = PDF_FILE_PATH;
    int indexToLoad = 0; // 열고 싶은 페이지 인덱스 (0 번부터 1페이지이다)

    // PDFium 을 초기화한다. 이걸 해야 사용할 수 있음
    FPDF_InitLibrary();

    // PDF 파일을 연다
    FPDF_DOCUMENT doc = FPDF_LoadDocument(path, NULL);

    /////////////////////////////////////
    // .... 페이지를 열어 렌더했다 .... 
    /////////////////////////////////////

    int width = FPDFBitmap_GetWidth(panel.get());
    int height = FPDFBitmap_GetHeight(panel.get());
    int rowBytes = FPDFBitmap_GetStride(panel.get());

    uint8_t *bitmap = static_Cast<uint8_t*>(FPDFBitmap_GetBuffer(panel.get());

    FPDF_CloseDocument(doc);
    FPDF_DestroyLibrary();

    // --------- 여기까지 완료 PDFium 으로 불러왔다
    
    auto view = interleaved_view(width, 
        height, 
        (boost::gil::rgba_pixel_t const *) &bitmap[0],
        rowBytes); // 이 아이는 boost::gil::rgba8c_view_t 형이다

    // PNG 파일로 작성한다.
    boost::gil::write_view("result.png", view, boost::gil::png_tag{});

    // 압축옵션을 주고싶다면,
    /*
    boost::gil::write_view("result.png", 
        view, 
        boost::gil::image_write_info<boost::gil::png_tag>(
            PNG_COMPRESSION_TYPE_DEFAULT, // 컴프레션 타입인데, 딱히 프리셋은 없는 듯하다.
            3 // 압축률을 설정하는데 0-9 까지 있고 보통 3-6 을 쓰나보다. boost 기본값은 '3'
            , ... // 기타 옵션은 image_write_info 참조하면 나와있다.
        ));
    */
}
 

다행히 uint8_t 버퍼가 rgba_pixel_t 로 캐스팅이 가능해서 image 구조객체는 아니지만 view 객체로 받아서 PNG 파일로 간단하게 만들 수 있었다.

 

boost 에서 알아서 PNG 를 처리해주니 무지 간편했다 (찾아서 쓰기도 어려운데 많은 일을 덜어내주었다).

 

물론 다른 방법도 있고 할 테지만 정말 C++ 은 토나와서 도저히 무슨 말인지 모르겠어서 일단 되는 대로 개발해보고 좀 더 C++ 에 대한 해독력이 늘어간다면 리팩하는걸로 스킵해본다.

 

반응형
Comments