Linux

PDFium 라이브러리 삽질기 - 3

respiro 2019. 12. 11. 17:15

PDFium 라이브러리 삽질기 - 3

RPM 패키징 및 gdal 함께 사용하기 (1)

작성일자: 2019년 12월 11일

최종수정: 2019년 12월 13일

작성자: N3


1. gdal 과 함께 빌드하기

gdal 에서 사용하기 위해 빌드 테스트를 해 본다.

사용한 버전은 gdal 2.3.2 버전이다.


$ cd ~/pdfium

$ mkdir include lib

$ cd include

$ ln -sf ~/pdfium/public pdfium

$ cd ../lib

$ ln -sf ../out/shared/*.so .


$ cd ~/gdal-2.3.2

$ ./configure \

..

--with-pdfium=~/pdfium/ \ 

..


checking for pdfium... no

configure: error: pdfium requested but not found


gdal 의 configure 에서 with-pdfium 설정시 pdfium  의 유무를 판단하는 코드를 살펴 본다.

echo '#include <fpdfview.h>' > testpdfium.cpp

echo '#include <core/include/fpdfapi/fpdf_page.h>' >> testpdfium.cpp

echo 'int main(int argc, char** argv) { FPDF_InitLibrary(); FPDF_DestroyLibrary(); return 0; } ' >> testpdfium.cpp


 TEST_CXX_FLAGS="-std=c++0x"
 ${CXX} ${CXXFLAGS} ${CPPFLAGS} $TEST_CXX_FLAGS testpdfium.cpp -o testpdfium ${TEST_PDFIUM_INC} ${PDFIUM_LIB}

해당 코드를 앞에서 테스트했던 코드로 변경해서 테스트 해 보면....

testpdfium.cpp:2:10: fatal error: core/include/fpdfapi/fpdf_page.h: 그런 파일이나 디렉터리가 없습니다
 #include <core/include/fpdfapi/fpdf_page.h>


위 헤더 include 를 삭제한다. 그리고 테스트를 위한 링크 라이브러리 옵션을 추가한다.


--with-pdfium-extra-lib-for-test="-lpthread -lm -lc -lstdc++ -lz -ljpeg -lopenjp2 -llcms2 -lpng"



정상적으로 빌드 테스트되는 것을 확인할 수 있다. 이제 빌드해 본다.


$ make

In file included from gdal_pdf.h:53,

                 from pdfdataset.cpp:36:

pdfsdk_headers.h:119:10: fatal error: core/include/fpdfapi/fpdf_page.h: 그런 파일이나 디렉터리가 없습니다

 #include <core/include/fpdfapi/fpdf_page.h>



gdal 에서 사용하고 있는 pdfium 의 헤더 경로가 최신 pdfium 과 다르다.

앞의 테스트코드 처럼 해당 코드에서 위 헤더추가를 모두 제거해준다.

iff -urN gdal-2.3.2-fedora/configure gdal-2.3.2-pdfium/configure

--- gdal-2.3.2-fedora/configure 2019-12-10 13:45:09.745538100 +0900

+++ gdal-2.3.2-pdfium/configure 2019-12-10 13:54:50.597897767 +0900

@@ -34698,7 +34698,6 @@

     if test "x$with_pdfium_lib" = "x" ; then

         rm -f testpdfium.*

         echo '#include <fpdfview.h>' > testpdfium.cpp

-        echo '#include <core/include/fpdfapi/fpdf_page.h>' >> testpdfium.cpp

         echo 'int main(int argc, char** argv) { FPDF_InitLibrary(); FPDF_DestroyLibrary(); return 0; } ' >> testpdfium.cpp

         TEST_CXX_FLAGS="-std=c++0x"

         if test ! -z "`uname | grep Darwin`" ; then

diff -urN gdal-2.3.2-fedora/frmts/pdf/pdfsdk_headers.h gdal-2.3.2-pdfium/frmts/pdf/pdfsdk_headers.h

--- gdal-2.3.2-fedora/frmts/pdf/pdfsdk_headers.h        2018-09-21 18:04:33.000000000 +0900

+++ gdal-2.3.2-pdfium/frmts/pdf/pdfsdk_headers.h        2019-12-10 13:53:44.849271839 +0900

@@ -116,9 +116,9 @@

 #include <cstring>

 //#include <fpdfsdk/include/fsdk_define.h>

 #include <fpdfview.h>

-#include <core/include/fpdfapi/fpdf_page.h>

-#include <core/include/fpdfapi/fpdf_objects.h>

-#include "fpdfsdk/include/fsdk_rendercontext.h"

+//#include <core/include/fpdfapi/fpdf_page.h>

+//#include <core/include/fpdfapi/fpdf_objects.h>

+//#include "fpdfsdk/include/fsdk_rendercontext.h"

 #endif // HAVE_PDFIUM


 #endif


수정 후, 다시 빌드 해 본다.

..

pdfobject.h:313:9: error: ‘CPDF_Object’ does not name a type; did you mean ‘PSObject’?

         CPDF_Object* m_po;

         ^~~~~~~~~~~

..


삽질과 서핑 도중 아래의 선배 삽질러를 찾았다.


gdal 3.1(최신 소스코드) 에 pdfium 을 링크하기 위해  pdfium 을 패치한 내용들이다. 아래 링크는 clang 을 사용하며, static 라이브러리를 생성한다.


몇 개의 패치코드는 살펴볼 필요가 있다.



 위 링크의 내용들을 참조하고 gdal 3.1 의 pdf 포맷 지원 코드를 현재 2.3.2 코드로 백포팅하기로 한다. 

위 코드로 업그레이드하면, 관련되어서 업그레이드할 것들이 많다.


gdal 의 pdfium 최신 패치 확인하기



일단 URL 및 소스와 패치만 확인하고 패치코드 일부를 적용한 pdfium rpm 패키지를 만들어 본다.


gdal 과 함께  컴파일하는 것은 다음 삽질과정으로 남겨두고, gdal 테스트를 위해 먼저 pdfium 을 패키징해서 시스템에 설치해둔다.


2.  CentOS 7 용 PDFium RPM 패키지 만들기


이제 삽질한 파일들과 내용들을 참조하여 CentOS 7 용 RPM 패키지로 만들어 본다. 

패키지 이름은 대충 libpdfium 로 한다.

라이브러리 버전을 확인할 수 없어 일단은 chromium/3989 를 사용했다. 


정적 라이브러리를 위한 옵션

# Build arguments go here.

# See "gn args <out_dir> --list" for available build arguments.

is_official_build       = true

pdf_is_standalone   = true                 # Set for a non-embedded build.

pdf_is_complete_lib = true                 # Static Library - libpdfium.a

pdf_enable_xfa      = false                # XFA support enabled.

pdf_enable_v8       = false                # Javascript support enabled.

pdf_use_skia        = false                # Avoid skia backend experiment.

pdf_use_skia_paths  = false                # Avoid other skia backend experiment.


pdf_bundle_freetype = true

use_system_freetype = false


clang_use_chrome_plugins = false


use_goma            = false

use_custom_libcxx   = false

use_sysroot         = false

use_rtti            = true


is_component_build  = false                # Dynamic Library - libpdfium.so

is_debug            = false                # Enable debugging features.

is_clang            = false                # Avoid dependency hell.


use_system_zlib     = true

use_system_libpng   = true

use_system_lcms2    = true

use_system_libjpeg  = true

use_libjpeg_turbo   = true

use_system_libopenjpeg2 = true



동적 라이브러리를 위한 빌드

# Build arguments go here.

# See "gn args <out_dir> --list" for available build arguments.

is_official_build       = true

pdf_is_standalone   = true                  # Set for a non-embedded build.

pdf_is_complete_lib = false                 # Static Library

is_component_build  = true                  # Dynamic Library


pdf_enable_xfa      = false                 # XFA support enabled.

pdf_enable_v8       = false                 # Javascript support enabled.

pdf_use_skia        = false                 # Avoid skia backend experiment.

pdf_use_skia_paths  = false                 # Avoid other skia backend experiment.


pdf_bundle_freetype = true

use_system_freetype = false


clang_use_chrome_plugins = false


use_goma            = false

use_custom_libcxx   = false

use_sysroot         = false

use_rtti            = true


is_debug            = false                 # Enable debugging features.

is_clang            = false                 # Avoid dependency hell.


use_system_zlib     = true

use_system_libpng   = true

use_system_lcms2    = true

use_system_libjpeg  = true

use_libjpeg_turbo   = true

use_system_libopenjpeg2 = true



libpdfium.spec

%global commit 26cd122a8d10d33091bac50b025df919405a0950

%global shortcommit %(c=%{commit}; echo ${c:0:7})

%global chromium_rev   "chromium/3989"


Name:           libpdfium

Version:        0.0.3989

Release:        1%{?dist}

Summary:        PDF generation and rendering library


Group:          Development/Libraries

License:        BSD 3-clause

URL:            https://pdfium.googlesource.com/pdfium/

Source0:        .

# Static Build Argument

Source1:        pdfium-static-args.gn

Source2:        pdfium-shared-args.gn


BuildRoot:      %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX)


BuildRequires:  zlib-devel

BuildRequires:  libpng-devel

BuildRequires:  libjpeg-turbo-devel

BuildRequires:  openjpeg2-devel > 2.3.0

BuildRequires:  lcms2-devel

BuildRequires:  devtoolset-8-gcc-c++

BuildRequires:  git


Requires:       zlib

Requires:       libpng

Requires:       libjpeg-turbo

Requires:       openjpeg2

Requires:       lcms2


%description

Used in Chrome for displaying PDFs and print preview. Used in Android for PDF rendering


%package devel

Summary: Libraries and headers for pdfium

Group: Development/Libraries

Requires: %{name}%{?_isa} = %{version}-%{release}


%description devel

You should install the libpdfium-devel package if you would like to

compile applications based on pdfium.


%prep


%build


## Change GCC

##scl enable devtoolset-8 bash


export DEPOT_TOOLS_URL="https://chromium.googlesource.com/chromium/tools/depot_tools.git"

export PDFIUM_URL="https://pdfium.googlesource.com/pdfium.git"


mkdir -p %{name}

cd %{name}

export CURDIR=`pwd`

export DEPOT_TOOLS_DIR=$CURDIR/depot_tools

if [ ! -d "$DEPOT_TOOLS_DIR" ]; then
   git clone "$DEPOT_TOOLS_URL"  depot_tools
else
   pushd $DEPOT_TOOLS_DIR
   git checkout master
   git pull
   popd
fi

export PATH="$DEPOT_TOOLS_DIR:$PATH"

# Checkout sources
# From https://pdfium.googlesource.com/pdfium/
gclient config --unmanaged "$PDFIUM_URL"

# Tested Revision
gclient sync --revision="$chromium_rev"

# Current Revision
##gclient sync

## Static Build
pushd pdfium
mkdir -p out/static
cp %{SOURCE1} out/static/args.gn
gn gen out/static

# Test Build Error
##ninja -C out/static pdfium_all
ninja -C out/static pdfium

## Shared Build
mkdir -p out/shared
cp %{SOURCE2} out/shared/args.gn
gn gen out/shared
##ninja -C out/shared pdfium_all
ninja -C out/shared pdfium
popd

%install
mkdir -p %{buildroot}/%{_includedir}/pdfium
mkdir -p %{buildroot}/%{_libdir}

cd %{name}/pdfium

# Header Install
HEADER_SUBDIRS="build fpdfsdk core/fxge core/fxge/agg core/fxge/dib core/fpdfdoc core/fpdfapi/parser core/fpdfapi/page core/fpdfapi/render core/fxcrt third_party/agg23 third_party/base third_party/base/allocator/partition_allocator third_party/base/numerics"

cp -r public %{buildroot}/%{_includedir}/pdfium
for subdir in $HEADER_SUBDIRS; do
    mkdir -p "%{buildroot}/%{_includedir}/pdfium/$subdir"
    cp "$subdir"/*.h  "%{buildroot}/%{_includedir}/pdfium/$subdir"
done
# Static Library Install
install -m 0644 out/static/obj/libpdfium.a %{buildroot}%{_libdir}/libpdfium.a

# Shared Library Install
install -m 0755 out/shared/*.so %{buildroot}%{_libdir}/

%clean
rm -rf %{buildroot}

%pre

%postun

%files
%defattr(-,root,root,-)
%doc
%{_libdir}/*.so

%files devel
%defattr(-,root,root,-)
%{_includedir}/pdfium
%{_libdir}/libpdfium.a

%changelog
* Wed Dec 11 2019 N3 <n3syskr@gmail.com> - 0.0.0-1
- initial build


헤더파일과 정적 라이브러리는 devel 패키지로 따로 분리하였다.


gdal 과 빌드하기 위해 위에서 참조한 URL 로부터 pdfium 패치를 가져와 다시 빌드할 것이다.


pdfium 패치

diff -urN pdfium/core/fpdfapi/page/cpdf_occontext.cpp pdfium-a/core/fpdfapi/page/cpdf_occontext.cpp

--- pdfium/core/fpdfapi/page/cpdf_occontext.cpp 2019-12-11 15:41:16.595645562 +0900

+++ pdfium-a/core/fpdfapi/page/cpdf_occontext.cpp       2019-12-11 15:41:52.535538284 +0900

@@ -181,7 +181,7 @@

   return bState;

 }


-bool CPDF_OCContext::CheckObjectVisible(const CPDF_PageObject* pObj) const {

+bool CPDF_OCContextInterface::CheckObjectVisible(const CPDF_PageObject* pObj) const {

   for (size_t i = 0; i < pObj->m_ContentMarks.CountItems(); ++i) {

     const CPDF_ContentMarkItem* item = pObj->m_ContentMarks.GetItem(i);

     if (item->GetName() == "OC" &&

diff -urN pdfium/core/fpdfapi/page/cpdf_occontext.h pdfium-a/core/fpdfapi/page/cpdf_occontext.h

--- pdfium/core/fpdfapi/page/cpdf_occontext.h   2019-12-11 15:41:16.596645587 +0900

+++ pdfium-a/core/fpdfapi/page/cpdf_occontext.h 2019-12-11 15:42:16.940144477 +0900

@@ -17,15 +17,21 @@

 class CPDF_Document;

 class CPDF_PageObject;


-class CPDF_OCContext final : public Retainable {

+class CPDF_OCContextInterface : public Retainable {

+    public:

+        virtual ~CPDF_OCContextInterface() = default;

+        virtual bool CheckOCGVisible(const CPDF_Dictionary* pOCGDict) const = 0;

+        virtual bool CheckObjectVisible(const CPDF_PageObject* pObj) const;

+};

+

+class CPDF_OCContext : public CPDF_OCContextInterface {

  public:

   enum UsageType { View = 0, Design, Print, Export };


   template <typename T, typename... Args>

   friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);


-  bool CheckOCGVisible(const CPDF_Dictionary* pOCGDict) const;

-  bool CheckObjectVisible(const CPDF_PageObject* pObj) const;

+  bool CheckOCGVisible(const CPDF_Dictionary* pOCGDict) const override;


  private:

   CPDF_OCContext(CPDF_Document* pDoc, UsageType eUsageType);

diff -urN pdfium/core/fpdfapi/render/cpdf_renderoptions.h pdfium-a/core/fpdfapi/render/cpdf_renderoptions.h

--- pdfium/core/fpdfapi/render/cpdf_renderoptions.h     2019-12-11 15:41:16.596645587 +0900

+++ pdfium-a/core/fpdfapi/render/cpdf_renderoptions.h   2019-12-11 15:41:52.535538284 +0900

@@ -56,16 +56,16 @@

   void SetDrawAnnots(bool draw) { m_bDrawAnnots = draw; }

   bool GetDrawAnnots() const { return m_bDrawAnnots; }


-  void SetOCContext(RetainPtr<CPDF_OCContext> context) {

+  void SetOCContext(RetainPtr<CPDF_OCContextInterface> context) {

     m_pOCContext = context;

   }

-  const CPDF_OCContext* GetOCContext() const { return m_pOCContext.Get(); }

+  const CPDF_OCContextInterface* GetOCContext() const { return m_pOCContext.Get(); }


  private:

   Type m_ColorMode = kNormal;

   bool m_bDrawAnnots = false;

   Options m_Options;

-  RetainPtr<CPDF_OCContext> m_pOCContext;

+  RetainPtr<CPDF_OCContextInterface> m_pOCContext;

 };


 #endif  // CORE_FPDFAPI_RENDER_CPDF_RENDEROPTIONS_H_
diff -urN pdfium/core/fpdfdoc/cpdf_annotlist.cpp pdfium-a/core/fpdfdoc/cpdf_annotlist.cpp
--- pdfium/core/fpdfdoc/cpdf_annotlist.cpp      2019-12-11 15:41:16.596645587 +0900
+++ pdfium-a/core/fpdfdoc/cpdf_annotlist.cpp    2019-12-11 15:41:52.536538309 +0900
@@ -246,7 +246,7 @@

     if (pOptions) {
       const CPDF_Dictionary* pAnnotDict = pAnnot->GetAnnotDict();
-      const CPDF_OCContext* pOCContext = pOptions->GetOCContext();
+      const auto pOCContext = pOptions->GetOCContext();
       if (pAnnotDict && pOCContext &&
           !pOCContext->CheckOCGVisible(
               pAnnotDict->GetDictFor(pdfium::annotation::kOC))) {


앞의 삽질에서 동적 라이브러리로 빌드하는 경우, icu libicuuc.so 파일이 생긴다.

이 라이브러리 이름이 시스템의 icu 라이브러리 이름과 충돌하며 API 도 완전히 호환되지 않는다.

그래서, 생성되는 라이브러리의 이름을 변경하기로 삽질해보자.


먼저, third_party/icu 폴더로 이동한다.


config.gni 파일을 열고 다음 변수를 추가한다.

declare_args() {

  # Tells icu to load an external data file rather than rely on the icudata

  # being linked directly into the binary.

  icu_use_data_file = true

  icu_rename = false

}


그리고, BUILD.gn 파일에 다음을 추가한다.

is_fuchsia 는 운영체제가 구글의 자체 운영체제인지 묻는 옵션이다. 이 옵션을 건들지 말고, 새로운 옵션을 추가해 이름만 수정한다.

..

  if (is_fuchsia || icu_rename) {

    # Fuchsia puts its own libicui18n.so in /system/lib where we need to put our

    # .so when doing component builds, so we need to give this a different name.

    output_name = "icui18n_cr"

  }

..

  if (is_fuchsia || icu_rename) {

    # Fuchsia puts its own libicuuc.so in /system/lib where we need to put our

    # .so when doing component builds, so we need to give this a different name.

    output_name = "icuuc_cr"

  }



전체 icu 라이브러리 이름 변경 패치

diff -urN pdfium/third_party/icu/BUILD.gn pdfium-icu/third_party/icu/BUILD.gn

--- pdfium/third_party/icu/BUILD.gn     2019-12-11 15:31:28.283025785 +0900

+++ pdfium-icu/third_party/icu/BUILD.gn 2019-12-11 16:36:25.528721775 +0900

@@ -630,7 +630,7 @@

   configs += [ ":icu_code" ]

   public_configs = [ ":icu_config" ]


-  if (is_fuchsia) {

+  if (is_fuchsia || icu_rename) {

     # Fuchsia puts its own libicui18n.so in /system/lib where we need to put our

     # .so when doing component builds, so we need to give this a different name.

     output_name = "icui18n_cr"

@@ -1080,7 +1080,7 @@


   defines += [ "U_ICUDATAENTRY_IN_COMMON" ]


-  if (is_fuchsia) {

+  if (is_fuchsia || icu_rename) {

     # Fuchsia puts its own libicuuc.so in /system/lib where we need to put our

     # .so when doing component builds, so we need to give this a different name.

     output_name = "icuuc_cr"

diff -urN pdfium/third_party/icu/config.gni pdfium-icu/third_party/icu/config.gni

--- pdfium/third_party/icu/config.gni   2019-12-11 15:31:28.645034782 +0900

+++ pdfium-icu/third_party/icu/config.gni       2019-12-11 16:35:57.330023671 +0900

@@ -6,4 +6,5 @@

   # Tells icu to load an external data file rather than rely on the icudata

   # being linked directly into the binary.

   icu_use_data_file = true

+  icu_rename = false

 }



동적 라이브러리를 빌드할 때,

icu_rename = true

옵션을 추가해서 빌드하면, 

libicuuc.so 파일의 이름이 libicuuc_cr.so  이름으로 생성되어 기존 icu 라이브러리와 충돌하지 않게 할 수 있다.



RPM 패키지을 만들고 로컬 인스톨해둔다.

Dependencies Resolved


=====================================================================================

 Package                     Arch               Version                       Repository         Size

=====================================================================================

Installing:

 libpdfium-devel             x86_64             0.0.3989-1.el7                n3sys             3.4 M

Installing for dependencies:

 libpdfium                   x86_64             0.0.3989-1.el7                n3sys             1.9 M


Transaction Summary

=====================================================================================

Install  1 Package (+1 Dependent package)


Total download size: 5.3 M

Installed size: 19 M


RedHat 계열에서 아래 소스 rpm 을 사용하면 된다.


테스트 시작 시점에 리비전 번호는 3989 였다.  현재는 3999다.


최신 버전으로 테스트하려면, libpdfium.spec 파일에서 해당 리비전 번호를 수정해서 테스트하면 된다.

%global chromium_rev   "chromium/3989"

...

gclient sync --revision="$chromium_rev"



$ rpmbuild --rebuild libpdfium-0.0.3989-1.el7.src.rpm


Checking for unpackaged file(s): /usr/lib/rpm/check-files /home/respiro/rpmbuild/BUILDROOT/libpdfium-0.0.3989-1.el7.x86_64

Wrote: /home/respiro/rpmbuild/SRPMS/libpdfium-0.0.3989-1.el7.src.rpm

Wrote: /home/respiro/rpmbuild/RPMS/x86_64/libpdfium-0.0.3989-1.el7.x86_64.rpm

Wrote: /home/respiro/rpmbuild/RPMS/x86_64/libpdfium-devel-0.0.3989-1.el7.x86_64.rpm


설치 후 , 앞에서 만든 테스트 프로그램을 빌드하고 실행해 본다.

PDFIUM_REPO= /usr/include/pdfium/

INC_DIR= -I ${PDFIUM_REPO}/public

LIB_DIR= -L /lib64


실행이 정상 동작하고 있다. 


이제 gdal 과 링크하기 위한 삽질을 시작하기 위한 준비를 마쳤다.